1665 lines
51 KiB
C++
1665 lines
51 KiB
C++
//=============================================================================*
|
|
// COPYRIGHT© 2001 Microsoft Corporation and Executive Software International, Inc.
|
|
//=============================================================================*
|
|
// File: Defrag.cpp
|
|
//=============================================================================*
|
|
|
|
#include "stdafx.h"
|
|
|
|
extern "C" {
|
|
#include "SysStruc.h"
|
|
}
|
|
|
|
|
|
#include "AdminPrivs.h"
|
|
#include "DataIo.h"
|
|
#include "DataIoCl.h"
|
|
|
|
#include "DfrgCmn.h"
|
|
#include "DfrgRes.h"
|
|
|
|
#include "ErrMacro.h"
|
|
#include "GetDfrgRes.h"
|
|
#include "TextBlock.h"
|
|
|
|
#include "Defrag.h"
|
|
#include "resource.h"
|
|
|
|
#include "UiCommon.h"
|
|
|
|
#include <stdio.h>
|
|
#include <comdef.h>
|
|
#include <atlconv.h>
|
|
#include <locale.h>
|
|
#include <winnlsp.h> // in public\internal\base\inc
|
|
|
|
#include "secattr.h"
|
|
|
|
|
|
// return code
|
|
static int RetCode = 0;
|
|
|
|
// resource DLL
|
|
static HINSTANCE resdll = NULL;
|
|
|
|
// force defrag flag
|
|
static BOOL ForceDefrag = FALSE;
|
|
|
|
// analyse vs defrag flag
|
|
BOOL AnalyzeOnly = FALSE;
|
|
|
|
// verbose vs concise output flag
|
|
BOOL VerboseOutput = FALSE;
|
|
|
|
// force Boot Optimize flag
|
|
static BOOL BootOptimize = FALSE;
|
|
|
|
// event engine signals to tell us it is finished
|
|
static HANDLE hEngineDoneEvent = NULL;
|
|
|
|
|
|
// semaphore to prevent multiple defraggers (command line or UI)
|
|
static HANDLE hIsOkToRunSemaphore = NULL;
|
|
|
|
// name of program
|
|
static PTCHAR prog;
|
|
|
|
WCHAR g_szTempBuffer[1024];
|
|
|
|
|
|
|
|
// prototypes
|
|
static void DisplayHelp(PTCHAR prog);
|
|
static int Defrag(PTCHAR cDriveGUIDString, PTCHAR fileSystem, HANDLE stopEvent);
|
|
static int GetMISemaphore();
|
|
static void ReleaseMISemaphore();
|
|
|
|
|
|
BOOL
|
|
PrintOnConsole(
|
|
IN LPCWSTR wszStr,
|
|
IN HANDLE hConsoleOutput,
|
|
IN BOOL bIsTrueConsoleOutput
|
|
)
|
|
{
|
|
DWORD dwCharsOutput = 0;
|
|
|
|
if (bIsTrueConsoleOutput) {
|
|
//
|
|
// Output to the console
|
|
//
|
|
if (!WriteConsoleW(hConsoleOutput,
|
|
(PVOID)wszStr,
|
|
(DWORD)wcslen(wszStr),
|
|
&dwCharsOutput,
|
|
NULL)
|
|
) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Output being redirected. WriteConsoleW doesn't work for redirected output. Convert
|
|
// UNICODE to the current output CP multibyte charset.
|
|
//
|
|
LPSTR lpszTmpBuffer;
|
|
DWORD dwByteCount;
|
|
|
|
//
|
|
// Get size of temp buffer needed for the conversion.
|
|
//
|
|
dwByteCount = WideCharToMultiByte(
|
|
GetConsoleOutputCP(),
|
|
0,
|
|
wszStr,
|
|
-1,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (0 == dwByteCount) {
|
|
return FALSE;
|
|
}
|
|
|
|
lpszTmpBuffer = (LPSTR)malloc(dwByteCount);
|
|
if (NULL == lpszTmpBuffer ) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now convert it.
|
|
//
|
|
dwByteCount = WideCharToMultiByte(
|
|
GetConsoleOutputCP(),
|
|
0,
|
|
wszStr,
|
|
-1,
|
|
lpszTmpBuffer,
|
|
dwByteCount,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (0 == dwByteCount) {
|
|
free(lpszTmpBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
// Finally output it.
|
|
if (!WriteFile(
|
|
hConsoleOutput,
|
|
lpszTmpBuffer,
|
|
dwByteCount - 1, // Get rid of the trailing NULL char
|
|
&dwCharsOutput,
|
|
NULL)
|
|
) {
|
|
free(lpszTmpBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
free(lpszTmpBuffer);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PrintOnStdOut(
|
|
IN LPCWSTR wszStr
|
|
)
|
|
{
|
|
static BOOL bIsTrueConsoleOutput = TRUE;
|
|
static HANDLE hConsoleOutput = INVALID_HANDLE_VALUE;
|
|
DWORD fdwMode = 0;
|
|
|
|
if (INVALID_HANDLE_VALUE == hConsoleOutput) {
|
|
|
|
hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
if (INVALID_HANDLE_VALUE == hConsoleOutput) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Stash away the results in static vars. bIsTrueConsoleOutput is TRUE when the
|
|
// standard output handle is pointing to a console character device.
|
|
//
|
|
bIsTrueConsoleOutput = (GetFileType( hConsoleOutput ) & FILE_TYPE_CHAR ) &&
|
|
GetConsoleMode( hConsoleOutput, &fdwMode );
|
|
}
|
|
|
|
if (NULL == wszStr) {
|
|
return FALSE;
|
|
}
|
|
|
|
return PrintOnConsole(wszStr, hConsoleOutput, bIsTrueConsoleOutput);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
PrintOnStdErr(
|
|
IN LPCWSTR wszStr
|
|
)
|
|
{
|
|
static BOOL bIsTrueConsoleOutput = TRUE;
|
|
static HANDLE hConsoleOutput = INVALID_HANDLE_VALUE;
|
|
DWORD fdwMode = 0;
|
|
|
|
|
|
if (INVALID_HANDLE_VALUE == hConsoleOutput) {
|
|
|
|
hConsoleOutput = GetStdHandle(STD_ERROR_HANDLE);
|
|
if (INVALID_HANDLE_VALUE == hConsoleOutput) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Stash away the results in static vars. bIsTrueConsoleOutput is TRUE when the
|
|
// standard output handle is pointing to a console character device.
|
|
//
|
|
bIsTrueConsoleOutput = (GetFileType( hConsoleOutput ) & FILE_TYPE_CHAR ) &&
|
|
GetConsoleMode( hConsoleOutput, &fdwMode );
|
|
}
|
|
|
|
|
|
if (NULL == wszStr) {
|
|
return FALSE;
|
|
}
|
|
|
|
return PrintOnConsole(wszStr, hConsoleOutput, bIsTrueConsoleOutput);
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------*
|
|
// function: main
|
|
//
|
|
// returns: 0 if all is well, otherwise error code
|
|
// note:
|
|
//-------------------------------------------------------------------*
|
|
extern "C" int __cdecl _tmain(int argc, TCHAR* argv[])
|
|
{
|
|
int ret = 0;
|
|
int ii;
|
|
DWORD_PTR dwParams[10];
|
|
DWORD dLastError = 0;
|
|
BOOL bDriveEntered = FALSE;
|
|
TCHAR fileSystem[10];
|
|
WCHAR buf[400];
|
|
UINT lenbuf = sizeof(buf)/sizeof(WCHAR); //135977 pass number of characters not number of bytes
|
|
WCHAR msg[400];
|
|
UINT lenmsg = sizeof(msg)/sizeof(WCHAR); //135977 pass number of characters not number of bytes
|
|
DWORD len;
|
|
HANDLE parentProcessHandle = NULL;
|
|
HANDLE stopEventHandle = NULL;
|
|
HANDLE stopEventSourceHandle;
|
|
DWORD parentProcessId;
|
|
BOOL stopEventSpecified = FALSE;
|
|
BOOL parentProcessSpecified = FALSE;
|
|
BOOL success;
|
|
TCHAR cDriveGUIDString[GUID_LENGTH];
|
|
VString tmpVolumeArg;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// Use the OEM code page ...
|
|
setlocale(LC_ALL, ".OCP");
|
|
|
|
// Use the console UI language
|
|
SetThreadUILanguage( 0 );
|
|
|
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
|
|
/*
|
|
// Initialize COM security
|
|
hr = CoInitializeSecurity(
|
|
NULL,
|
|
-1, // IN LONG cAuthSvc,
|
|
NULL, // IN SOLE_AUTHENTICATION_SERVICE *asAuthSvc,
|
|
NULL, // IN void *pReserved1,
|
|
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // IN DWORD dwAuthnLevel,
|
|
RPC_C_IMP_LEVEL_IDENTIFY, // IN DWORD dwImpLevel,
|
|
NULL, // IN void *pAuthList,
|
|
(EOAC_SECURE_REFS | EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL ),
|
|
NULL // IN void *pReserved3
|
|
);
|
|
|
|
if(FAILED(hr)) {
|
|
return 0;
|
|
}
|
|
*/
|
|
// load resource DLL
|
|
resdll = GetDfrgResHandle();
|
|
if (resdll == NULL) {
|
|
PrintOnStdErr(L"Error: cannot load resource DLL.\r\nContact system administrator.\r\n");
|
|
RetCode = ENGERR_SYSTEM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
// must be an administrator to run this
|
|
if (!CheckForAdminPrivs()) {
|
|
LoadString(resdll, IDS_NEED_ADMIN_PRIVS, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
RetCode = ENGERR_SYSTEM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
|
|
// check for multiple instances
|
|
if (GetMISemaphore() != 0)
|
|
{
|
|
RetCode = 1;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
// strip off path from prog name
|
|
prog = _tcsrchr(argv[0], TEXT('\\'));
|
|
if (prog == NULL)
|
|
{
|
|
prog = argv[0];
|
|
}
|
|
else
|
|
{
|
|
prog++;
|
|
}
|
|
|
|
//
|
|
dwParams[0] = (DWORD_PTR) prog;
|
|
dwParams[1] = NULL;
|
|
|
|
// process command line
|
|
for (ii = 1; ii < argc; ii++)
|
|
{
|
|
// command line switches start with a dash or slash
|
|
if (argv[ii][0] == TEXT('-') || argv[ii][0] == TEXT('/'))
|
|
{
|
|
// process command line switches
|
|
switch (argv[ii][1])
|
|
{
|
|
// Analyse only
|
|
case TEXT('A'):
|
|
case TEXT('a'):
|
|
AnalyzeOnly = TRUE;
|
|
break;
|
|
|
|
// force boot optimize only
|
|
case TEXT('B'):
|
|
case TEXT('b'):
|
|
BootOptimize = TRUE;
|
|
break;
|
|
|
|
// force defragmentation, even if too little free space
|
|
case TEXT('F'):
|
|
case TEXT('f'):
|
|
ForceDefrag = TRUE;
|
|
break;
|
|
|
|
// help request
|
|
case TEXT('H'):
|
|
case TEXT('h'):
|
|
case TEXT('?'):
|
|
DisplayHelp(prog);
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
break;
|
|
|
|
// parent process
|
|
case TEXT('P'):
|
|
case TEXT('p'):
|
|
ii++;
|
|
if (ii < argc) {
|
|
if (0 == _stscanf(argv[ii], TEXT("%x"), &parentProcessId)) {
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
parentProcessSpecified = TRUE;
|
|
}
|
|
break;
|
|
|
|
// stop event
|
|
case TEXT('S'):
|
|
case TEXT('s'):
|
|
ii++;
|
|
if (ii < argc) {
|
|
if (0 == _stscanf(argv[ii], TEXT("%p"), &stopEventSourceHandle)) {
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
stopEventSpecified = TRUE;
|
|
}
|
|
break;
|
|
|
|
// verbose output (full report)
|
|
case TEXT('V'):
|
|
case TEXT('v'):
|
|
VerboseOutput = TRUE;
|
|
break;
|
|
|
|
// unknown option
|
|
default:
|
|
dwParams[0] = (DWORD_PTR) argv[ii];
|
|
dwParams[1] = NULL;
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_ERR_BAD_OPTION, buf, lenbuf);
|
|
assert(wcslen(buf) < lenbuf);
|
|
|
|
len = FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
buf, 0, 0, msg, lenmsg, (va_list*) dwParams);
|
|
assert(wcslen(msg) < lenmsg);
|
|
PrintOnStdErr(msg);
|
|
DisplayHelp(prog);
|
|
}
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
break;
|
|
}
|
|
}
|
|
// otherwise, assume it is a drive letter or mount point
|
|
else
|
|
{
|
|
// check to make sure we don't already have a volume
|
|
if (bDriveEntered) // error
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
// multiple drive error
|
|
LoadString(resdll, IDS_ERR_2_DRIVES, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
DisplayHelp(prog);
|
|
}
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
// get a copy of the parameter
|
|
tmpVolumeArg = argv[ii];
|
|
|
|
// make sure it has a trailing backslash
|
|
len = tmpVolumeArg.GetLength();
|
|
if (tmpVolumeArg.operator [](len - 1) != TEXT('\\'))
|
|
{
|
|
if (!tmpVolumeArg.AddChar(TEXT('\\'))) {
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
}
|
|
|
|
// get GUID from system
|
|
if (!GetVolumeNameForVolumeMountPoint(tmpVolumeArg.GetBuffer(), cDriveGUIDString, GUID_LENGTH))
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
// bad drive error
|
|
LoadString(resdll, IDS_CMDLINE_BAD_VOL, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
DisplayHelp(prog);
|
|
}
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
bDriveEntered = TRUE;
|
|
}
|
|
}
|
|
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_COMMANDLINE_DESCRIPTION, buf, lenbuf);
|
|
assert(wcslen(buf) < lenbuf);
|
|
PrintOnStdOut(buf);
|
|
}
|
|
|
|
// no drive letter = help request
|
|
if (bDriveEntered == FALSE)
|
|
{
|
|
DisplayHelp(prog);
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
// error if not a valid volume
|
|
if (IsValidVolume(cDriveGUIDString, NULL, fileSystem) == FALSE)
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_VOLUME_TYPE_NOT_SUPPORTED, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
// error if non-writeable device
|
|
//sks bug#205674 disk full error
|
|
if (IsVolumeWriteable(cDriveGUIDString, &dLastError) == FALSE)
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
if(dLastError == ERROR_HANDLE_DISK_FULL)
|
|
{
|
|
LoadString(resdll, IDS_DISK_FULL, buf, lenbuf);
|
|
|
|
wsprintf(g_szTempBuffer, L"\r\n%s.\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
|
|
} else
|
|
{
|
|
LoadString(resdll, IDS_READONLY_VOLUME, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s.\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
}
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
// if both a parent process and a stop event has been specified, open
|
|
// the parent process and duplicate the handle to the event that it
|
|
// may signal to stop/cancel us.
|
|
if (parentProcessSpecified && stopEventSpecified)
|
|
{
|
|
|
|
// open the parent process.
|
|
parentProcessHandle = OpenProcess(PROCESS_DUP_HANDLE,
|
|
FALSE,
|
|
parentProcessId);
|
|
|
|
if (!parentProcessHandle)
|
|
{
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
// duplicate the stop event.
|
|
success = DuplicateHandle(parentProcessHandle,
|
|
stopEventSourceHandle,
|
|
GetCurrentProcess(),
|
|
&stopEventHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
|
|
CloseHandle(parentProcessHandle);
|
|
|
|
if (!success)
|
|
{
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
}
|
|
|
|
// defrag
|
|
ret = Defrag(cDriveGUIDString, fileSystem, stopEventHandle);
|
|
|
|
// if parent process passed in a stop event, close it.
|
|
if (stopEventHandle) {
|
|
CloseHandle(stopEventHandle);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
if (RetCode == 0)
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_LABEL_DEFRAG_COMPLETE, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdOut(g_szTempBuffer);
|
|
}
|
|
}
|
|
if(!BootOptimize)
|
|
{
|
|
wsprintf(g_szTempBuffer, L"ret code=%d\r\n", RetCode);
|
|
PrintOnStdOut(g_szTempBuffer);
|
|
}
|
|
#endif
|
|
|
|
// unload resource DLL
|
|
::FreeLibrary(resdll);
|
|
|
|
// free multi instance semaphore
|
|
ReleaseMISemaphore();
|
|
|
|
CoUninitialize();
|
|
return RetCode;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------*
|
|
// function: DisplayHelp
|
|
//
|
|
// returns:
|
|
// note:
|
|
//-------------------------------------------------------------------*
|
|
static void DisplayHelp(PTCHAR prog)
|
|
{
|
|
WCHAR buf[400];
|
|
UINT lenbuf = sizeof(buf)/sizeof(WCHAR); //135977 pass number of characters not number of bytes
|
|
WCHAR msg[400];
|
|
UINT lenmsg = sizeof(msg)/sizeof(WCHAR); //135977 pass number of characters not number of bytes
|
|
DWORD_PTR dwParams[10];
|
|
DWORD len;
|
|
|
|
dwParams[0] = (DWORD_PTR) prog;
|
|
dwParams[1] = NULL;
|
|
|
|
if(!BootOptimize)
|
|
{
|
|
|
|
LoadString(resdll, IDS_CMDLINE_USAGE, buf, lenbuf);
|
|
assert(wcslen(buf) < lenbuf);
|
|
len = FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
buf,
|
|
0,
|
|
0,
|
|
msg,
|
|
lenmsg,
|
|
(va_list*) dwParams);
|
|
assert(wslen(msg) < lenmsg);
|
|
PrintOnStdOut(msg);
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------*
|
|
// function: ConsoleCtrlHandler
|
|
//
|
|
// returns: TRUE if handled, FALSE otherwise
|
|
// note:
|
|
//-------------------------------------------------------------------*
|
|
BOOL WINAPI ConsoleCtrlHandler(DWORD CtrlType)
|
|
{
|
|
BOOL ret = FALSE;
|
|
WCHAR buf[400];
|
|
UINT lenbuf = sizeof(buf)/sizeof(WCHAR); //135975 pass number of characters not number of bytes
|
|
|
|
switch(CtrlType)
|
|
{
|
|
case CTRL_C_EVENT:
|
|
case CTRL_BREAK_EVENT:
|
|
// on these cases we want to acknowledge the user cancelled
|
|
if(!BootOptimize)
|
|
{
|
|
// Use the OEM code page ...
|
|
setlocale(LC_ALL, ".OCP");
|
|
|
|
// Use the console UI language
|
|
SetThreadUILanguage( 0 );
|
|
|
|
LoadString(resdll, IDS_USER_CANCELLED, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
// fall through on purpose
|
|
|
|
case CTRL_CLOSE_EVENT:
|
|
case CTRL_LOGOFF_EVENT:
|
|
case CTRL_SHUTDOWN_EVENT:
|
|
SetEvent(hEngineDoneEvent);
|
|
RetCode = ENG_USER_CANCELLED;
|
|
ret = TRUE;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------*
|
|
// function: GetMISemaphore
|
|
//
|
|
// returns:
|
|
// note:
|
|
//-------------------------------------------------------------------*
|
|
static int GetMISemaphore()
|
|
{
|
|
int ret = 1;
|
|
WCHAR buf[400];
|
|
UINT lenbuf = sizeof(buf)/sizeof(WCHAR); //135974 pass number of characters not number of bytes
|
|
|
|
SECURITY_ATTRIBUTES saSecurityAttributes;
|
|
SECURITY_DESCRIPTOR sdSecurityDescriptor;
|
|
|
|
ZeroMemory(&sdSecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
|
|
|
|
saSecurityAttributes.nLength = sizeof (saSecurityAttributes);
|
|
saSecurityAttributes.lpSecurityDescriptor = &sdSecurityDescriptor;
|
|
saSecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
if (!ConstructSecurityAttributes(&saSecurityAttributes, esatSemaphore, FALSE)) {
|
|
return 1;
|
|
}
|
|
|
|
#if 1
|
|
|
|
hIsOkToRunSemaphore = CreateSemaphore(&saSecurityAttributes, 1, 1, IS_OK_TO_RUN_SEMAPHORE_NAME);
|
|
|
|
CleanupSecurityAttributes(&saSecurityAttributes);
|
|
ZeroMemory(&sdSecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
|
|
|
|
if (hIsOkToRunSemaphore != NULL)
|
|
{
|
|
// is the semaphore signaled?
|
|
DWORD retValue = WaitForSingleObject(hIsOkToRunSemaphore, 10);
|
|
|
|
// if so, this process is the only one, and the semaphore count is decremented to 0
|
|
if (retValue == WAIT_OBJECT_0)
|
|
{
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
if (ret != 0)
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_CMDLINE_MULTI_INSTANCE, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
}
|
|
|
|
#else
|
|
// try to create the multiple instance semaphore
|
|
hIsOkToRunSemaphore = CreateSemaphore(&saSecurityAttributes, 0, 1, IS_OK_TO_RUN_SEMAPHORE_NAME);
|
|
|
|
// check if someone else has it
|
|
if (hIsOkToRunSemaphore != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_CMDLINE_MULTI_INSTANCE, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
ret = 1;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------*
|
|
// function: ReleaseMISemaphore
|
|
//
|
|
// returns:
|
|
// note:
|
|
//-------------------------------------------------------------------*
|
|
static void ReleaseMISemaphore()
|
|
{
|
|
// if the semaphore was created, nuke it
|
|
if (hIsOkToRunSemaphore != NULL)
|
|
{
|
|
ReleaseSemaphore(hIsOkToRunSemaphore, 1, NULL);
|
|
CloseHandle(hIsOkToRunSemaphore);
|
|
hIsOkToRunSemaphore = NULL;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------*
|
|
// function: Defrag
|
|
//
|
|
// returns: 0 if all is well, otherwise error code
|
|
// note:
|
|
//-------------------------------------------------------------------*
|
|
static int Defrag(PTCHAR cDriveGUIDString, PTCHAR fileSystem, HANDLE hStopEvent)
|
|
{
|
|
int ret = 0;
|
|
TCHAR cmd[200];
|
|
LPDATAOBJECT pDefragEngine = NULL;
|
|
WCHAR buf[400];
|
|
UINT lenbuf = sizeof(buf)/sizeof(WCHAR); //135976 pass number of characters not number of bytes
|
|
NOT_DATA NotData = {0};
|
|
BOOL bReturn;
|
|
ULONG numEvents;
|
|
HANDLE hEvents[2];
|
|
|
|
SECURITY_ATTRIBUTES saSecurityAttributes;
|
|
SECURITY_DESCRIPTOR sdSecurityDescriptor;
|
|
|
|
ZeroMemory(&sdSecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
|
|
|
|
saSecurityAttributes.nLength = sizeof (saSecurityAttributes);
|
|
saSecurityAttributes.lpSecurityDescriptor = &sdSecurityDescriptor;
|
|
saSecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
if (!ConstructSecurityAttributes(&saSecurityAttributes, esatEvent, FALSE)) {
|
|
RetCode = ENGERR_SYSTEM;
|
|
return RetCode;
|
|
}
|
|
|
|
hEngineDoneEvent = CreateEvent(&saSecurityAttributes, TRUE, FALSE, DEFRAG_COMPLETE_EVENT_NAME);
|
|
// create event
|
|
if ((hEngineDoneEvent == NULL) || (ERROR_ALREADY_EXISTS == GetLastError())) {
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_ERR_CREATE_EVENT, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
CleanupSecurityAttributes(&saSecurityAttributes);
|
|
ZeroMemory(&sdSecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
|
|
RetCode = ENGERR_SYSTEM;
|
|
return RetCode;
|
|
}
|
|
|
|
CleanupSecurityAttributes(&saSecurityAttributes);
|
|
ZeroMemory(&sdSecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
|
|
|
|
|
|
// initialize
|
|
DWORD dwInstanceRegister = InitializeDataIo(CLSID_DfrgCtlDataIo, REGCLS_MULTIPLEUSE);
|
|
|
|
// build command string
|
|
// is this an NTFS volume?
|
|
if (_tcscmp(fileSystem, TEXT("NTFS")) == MATCH)
|
|
{
|
|
// start the NTFS command string
|
|
_tcscpy(cmd, TEXT("DfrgNtfs "));
|
|
}
|
|
|
|
// is this a FAT or FAT32 volume?
|
|
else if (_tcscmp(fileSystem, TEXT("FAT")) == MATCH ||
|
|
_tcscmp(fileSystem, TEXT("FAT32")) == MATCH)
|
|
{
|
|
// start the FAT command string
|
|
_tcscpy(cmd, TEXT("DfrgFat "));
|
|
}
|
|
|
|
else
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_VOLUME_TYPE_NOT_SUPPORTED, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
RetCode = ENGERR_BAD_PARAM;
|
|
return RetCode;
|
|
}
|
|
|
|
// finish command line
|
|
_tcscat(cmd, cDriveGUIDString);
|
|
if (AnalyzeOnly) {
|
|
_tcscat(cmd, TEXT(" ANALYZE CMDLINE"));
|
|
}
|
|
else {
|
|
_tcscat(cmd, TEXT(" DEFRAG CMDLINE"));
|
|
}
|
|
|
|
// add boot optimize flag
|
|
if (BootOptimize)
|
|
{
|
|
_tcscat(cmd, TEXT(" BOOT"));
|
|
} else
|
|
{ // add force flag
|
|
if (ForceDefrag)
|
|
{
|
|
_tcscat(cmd, TEXT(" FORCE"));
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
if(!BootOptimize)
|
|
{
|
|
wsprintf(g_szTempBuffer, L"command line: %s\r\n\r\n", cmd);
|
|
PrintOnStdOut(g_szTempBuffer);
|
|
|
|
}
|
|
#endif
|
|
|
|
// Start the volume-oriented communication. Create a guid for communication first.
|
|
CLSID volumeID;
|
|
if (!SUCCEEDED(CoCreateGuid(&volumeID)))
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_ERR_CONNECT_ENGINE, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
RetCode = ENGERR_SYSTEM;
|
|
return RetCode;
|
|
}
|
|
|
|
USES_CONVERSION;
|
|
COleStr VolID;
|
|
StringFromCLSID(volumeID, VolID);
|
|
InitializeDataIo(volumeID, REGCLS_MULTIPLEUSE);
|
|
|
|
// clear the event
|
|
if (!ResetEvent(hEngineDoneEvent))
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_ERR_CREATE_EVENT, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
RetCode = ENGERR_SYSTEM;
|
|
return RetCode;
|
|
}
|
|
|
|
// install handler to make sure engine shuts down if we get killed
|
|
BOOL ok = SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
|
|
|
|
// is this an NTFS volume?
|
|
if (_tcscmp(fileSystem, TEXT("NTFS")) == MATCH)
|
|
{
|
|
// get a pointer to the NTFS engine
|
|
if (!InitializeDataIoClient(CLSID_DfrgNtfs, NULL, &pDefragEngine))
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_ERR_CONNECT_ENGINE, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
RetCode = ENGERR_SYSTEM;
|
|
return RetCode;
|
|
}
|
|
}
|
|
|
|
// is this a FAT or FAT32 volume?
|
|
else
|
|
{
|
|
// get a pointer to the FAT engine
|
|
if (!InitializeDataIoClient(CLSID_DfrgFat, NULL, &pDefragEngine))
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_ERR_CONNECT_ENGINE, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
RetCode = ENGERR_SYSTEM;
|
|
return RetCode;
|
|
}
|
|
}
|
|
|
|
// defrag
|
|
//
|
|
// send the generated clsid to the engine
|
|
DataIoClientSetData(ID_INIT_VOLUME_COMM,
|
|
OLE2T(VolID),
|
|
VolID.GetLength() * sizeof(TCHAR),
|
|
pDefragEngine);
|
|
|
|
// send the command request to the Dfrg engine
|
|
DataIoClientSetData(ID_INITIALIZE_DRIVE,
|
|
cmd,
|
|
_tcslen(cmd) * sizeof(TCHAR),
|
|
pDefragEngine);
|
|
|
|
// wait for engine to signal it is finished
|
|
BOOL bEngineNotDone = TRUE; // not done yet flag
|
|
const DWORD dwWaitMilliSecs = 5000; // milli-seconds to wait
|
|
|
|
// setup the events that may be signaled to stop us.
|
|
|
|
numEvents = 0;
|
|
|
|
hEvents[numEvents] = hEngineDoneEvent;
|
|
numEvents++;
|
|
|
|
if (hStopEvent) {
|
|
hEvents[numEvents] = hStopEvent;
|
|
numEvents++;
|
|
}
|
|
|
|
while (bEngineNotDone)
|
|
{
|
|
// wait for engine to signal it is done, but timeout
|
|
DWORD status = WaitForMultipleObjects(numEvents,
|
|
hEvents,
|
|
FALSE,
|
|
dwWaitMilliSecs);
|
|
|
|
switch (status)
|
|
{
|
|
case WAIT_TIMEOUT: // wait timed out
|
|
// ping engine to see if it is running still
|
|
bReturn = DataIoClientSetData(ID_PING, (PTCHAR) &NotData, sizeof(NOT_DATA), pDefragEngine);
|
|
|
|
// if we cannot ping it, assume it is dead
|
|
if (!bReturn)
|
|
{
|
|
|
|
// to avoid a race condition, check the done event again.
|
|
if (WAIT_OBJECT_0 == WaitForSingleObject(hEngineDoneEvent, 0))
|
|
{
|
|
// engine signaled event and exit just when we timed out.
|
|
// loop and fall through to the "engine done" code path.
|
|
break;
|
|
}
|
|
|
|
if (RetCode == 0)
|
|
{
|
|
RetCode = ENGERR_UNKNOWN;
|
|
}
|
|
|
|
// error message
|
|
if ((ENGERR_UNKNOWN == RetCode) && (!BootOptimize)) {
|
|
LoadString(resdll, IDS_CMDLINE_UNKNOWN_ERR, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
DisplayHelp(prog);
|
|
}
|
|
|
|
bEngineNotDone = FALSE;
|
|
}
|
|
break;
|
|
|
|
case WAIT_OBJECT_0: // engine signaled it is done
|
|
bEngineNotDone = FALSE;
|
|
break;
|
|
|
|
case WAIT_OBJECT_0 + 1: // parent process asked us to stop
|
|
bEngineNotDone = FALSE;
|
|
RetCode = ENG_USER_CANCELLED;
|
|
break;
|
|
|
|
case WAIT_ABANDONED: // engine died
|
|
// error message
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_CMDLINE_UNKNOWN_ERR, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
DisplayHelp(prog);
|
|
}
|
|
RetCode = ENGERR_UNKNOWN;
|
|
bEngineNotDone = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// kill engine
|
|
BOOL bNoGui = TRUE;
|
|
DataIoClientSetData(ID_ABORT, (PTCHAR) &bNoGui, sizeof(BOOL), pDefragEngine);
|
|
|
|
// clean up
|
|
ok = ExitDataIoClient(&pDefragEngine);
|
|
pDefragEngine = NULL;
|
|
|
|
#ifdef _DEBUG
|
|
if (!ok)
|
|
{
|
|
if(!BootOptimize)
|
|
{
|
|
LoadString(resdll, IDS_ERR_RELEASE_ENGINE, buf, lenbuf);
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n", buf);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// don't need event any more
|
|
CloseHandle(hEngineDoneEvent);
|
|
|
|
return RetCode;
|
|
}
|
|
|
|
TCHAR*
|
|
InsertCommaIntoText(
|
|
IN TCHAR* stringBuffer
|
|
)
|
|
{
|
|
TCHAR targetString[256];
|
|
TCHAR sourceString[256];
|
|
TCHAR tcThousandsSep[2] = {TEXT(','), 0};
|
|
|
|
_tcscpy(sourceString, stringBuffer);
|
|
|
|
if(_tcslen(sourceString) == 0) {
|
|
return TEXT("");
|
|
}
|
|
|
|
struct lconv *locals = localeconv();
|
|
if (*(locals->thousands_sep) != 0) {
|
|
_stprintf(tcThousandsSep, TEXT("%C"), *(locals->thousands_sep));
|
|
}
|
|
|
|
UINT uGrouping = atoi(locals->grouping);
|
|
if(uGrouping == 0) {
|
|
uGrouping = 3; //default value if its not supported
|
|
}
|
|
|
|
// point the source pointer at the null terminator
|
|
PTCHAR pSource = sourceString + _tcslen(sourceString);
|
|
|
|
// put the target pointer at the end of the target buffer
|
|
PTCHAR pTarget = targetString + sizeof(targetString) / sizeof(TCHAR) - 1;
|
|
|
|
// write the null terminator
|
|
*pTarget = NULL;
|
|
|
|
for (UINT i=0; i<_tcslen(sourceString); i++){
|
|
if (i>0 && i%uGrouping == 0){
|
|
pTarget--;
|
|
*pTarget = tcThousandsSep[0];
|
|
}
|
|
pTarget--;
|
|
pSource--;
|
|
*pTarget = *pSource;
|
|
}
|
|
|
|
// if (stringBufferLength > _tcslen(pTarget)){
|
|
_tcscpy(stringBuffer, pTarget);
|
|
// }
|
|
// else{
|
|
// _tcscpy(stringBuffer, TEXT(""));
|
|
// }
|
|
return stringBuffer;
|
|
}
|
|
|
|
|
|
LONGLONG checkForNegativeValues(LONGLONG lldatavalue)
|
|
{
|
|
return ((lldatavalue > 0) ? lldatavalue : 0);
|
|
}
|
|
|
|
VOID
|
|
PrintToStdOut(
|
|
IN UINT resourceIDText,
|
|
IN UINT resourceIDSeperator,
|
|
IN TCHAR* pTextStr,
|
|
BOOL bIndentText,
|
|
IN UINT resourceIDPercent = 0
|
|
)
|
|
{
|
|
TCHAR buffer[256];
|
|
TCHAR tempBuffer[270];
|
|
TCHAR buffer2[8];
|
|
|
|
//load the resourceIDText
|
|
LoadString(GetDfrgResHandle(), resourceIDText, buffer, sizeof(buffer)/sizeof(TCHAR));
|
|
if(bIndentText) {
|
|
_stprintf(tempBuffer, TEXT(" %s"), buffer);
|
|
}
|
|
else {
|
|
_stprintf(tempBuffer, TEXT("\r\n%s"), buffer);
|
|
}
|
|
|
|
//
|
|
// Add spaces so that this part occupies at least 35 chars
|
|
//
|
|
int uExtraStrLen = 35 - _tcslen(tempBuffer);
|
|
if (uExtraStrLen > 0) {
|
|
int i = 0;
|
|
for(i=0;i<uExtraStrLen;i++) {
|
|
_tcscat(tempBuffer, TEXT(" "));
|
|
}
|
|
}
|
|
|
|
|
|
//load the resourceIDSeperator
|
|
LoadString(GetDfrgResHandle(), resourceIDSeperator, buffer, sizeof(buffer)/sizeof(TCHAR));
|
|
|
|
if (resourceIDPercent) {
|
|
LoadString(GetDfrgResHandle(), resourceIDPercent, buffer2, sizeof(buffer2)/sizeof(TCHAR));
|
|
wsprintf(g_szTempBuffer, L"%s \t%s %s %s\r\n", tempBuffer, buffer, pTextStr, buffer2);
|
|
PrintOnStdOut(g_szTempBuffer);
|
|
|
|
}
|
|
else {
|
|
wsprintf(g_szTempBuffer, L"%s \t%s %s\r\n", tempBuffer, buffer, pTextStr);
|
|
PrintOnStdOut(g_szTempBuffer);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
VOID
|
|
WriteTextReportToStdOut(
|
|
IN TEXT_DATA *pTextData
|
|
)
|
|
{
|
|
TCHAR buffer[256];
|
|
TCHAR tempBuffer[256];
|
|
static int count = 0;
|
|
|
|
// Use the OEM code page ...
|
|
setlocale(LC_ALL, ".OCP");
|
|
|
|
// Use the console UI language
|
|
SetThreadUILanguage( 0 );
|
|
|
|
// check all the values of textdata to make sure no negative values
|
|
//to fix bug number 35764
|
|
pTextData->DiskSize = checkForNegativeValues(pTextData->DiskSize);
|
|
pTextData->BytesPerCluster = checkForNegativeValues(pTextData->BytesPerCluster);
|
|
pTextData->UsedSpace = checkForNegativeValues(pTextData->UsedSpace);
|
|
pTextData->FreeSpace = checkForNegativeValues(pTextData->FreeSpace);
|
|
pTextData->FreeSpacePercent = checkForNegativeValues(pTextData->FreeSpacePercent);
|
|
pTextData->UsableFreeSpace = checkForNegativeValues(pTextData->UsableFreeSpace);
|
|
pTextData->UsableFreeSpacePercent = checkForNegativeValues(pTextData->UsableFreeSpacePercent);
|
|
pTextData->PagefileBytes = checkForNegativeValues(pTextData->PagefileBytes);
|
|
pTextData->PagefileFrags = checkForNegativeValues(pTextData->PagefileFrags);
|
|
pTextData->TotalDirectories = checkForNegativeValues(pTextData->TotalDirectories);
|
|
pTextData->FragmentedDirectories = checkForNegativeValues(pTextData->FragmentedDirectories);
|
|
pTextData->ExcessDirFrags = checkForNegativeValues(pTextData->ExcessDirFrags);
|
|
pTextData->TotalFiles = checkForNegativeValues(pTextData->TotalFiles);
|
|
pTextData->AvgFileSize = checkForNegativeValues(pTextData->AvgFileSize);
|
|
pTextData->NumFraggedFiles = checkForNegativeValues(pTextData->NumFraggedFiles);
|
|
pTextData->NumExcessFrags = checkForNegativeValues(pTextData->NumExcessFrags);
|
|
pTextData->PercentDiskFragged = checkForNegativeValues(pTextData->PercentDiskFragged);
|
|
pTextData->AvgFragsPerFile = checkForNegativeValues(pTextData->AvgFragsPerFile);
|
|
pTextData->MFTBytes = checkForNegativeValues(pTextData->MFTBytes);
|
|
pTextData->InUseMFTRecords = checkForNegativeValues(pTextData->InUseMFTRecords);
|
|
pTextData->TotalMFTRecords = checkForNegativeValues(pTextData->TotalMFTRecords);
|
|
pTextData->MFTExtents = checkForNegativeValues(pTextData->MFTExtents);
|
|
pTextData->FreeSpaceFragPercent = checkForNegativeValues(pTextData->FreeSpaceFragPercent);
|
|
|
|
|
|
|
|
if (!VerboseOutput) {
|
|
|
|
TCHAR buffer1[24];
|
|
|
|
if (AnalyzeOnly || !count) {
|
|
PrintToStdOut(IDS_ANALYSIS_REPORT_TITLE, NULL, TEXT(""), FALSE);
|
|
}
|
|
else {
|
|
PrintToStdOut(IDS_DEFRAG_REPORT_TITLE, NULL, TEXT(""), FALSE);
|
|
}
|
|
++count;
|
|
|
|
LoadString(GetDfrgResHandle(), IDS_CONCISE_OUTPUT_FORMAT, buffer, sizeof(buffer)/sizeof(TCHAR));
|
|
|
|
// Volume Size
|
|
_stprintf(buffer1, TEXT("%I64d"), pTextData->DiskSize);
|
|
InsertCommaIntoText(buffer1);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->DiskSize, buffer1);
|
|
|
|
// Free Space
|
|
_stprintf(tempBuffer, TEXT("%I64d"), pTextData->FreeSpace);
|
|
InsertCommaIntoText(tempBuffer);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->FreeSpace, tempBuffer);
|
|
|
|
wsprintf(g_szTempBuffer,
|
|
buffer,
|
|
buffer1,
|
|
tempBuffer,
|
|
pTextData->FreeSpacePercent,
|
|
((pTextData->PercentDiskFragged + pTextData->FreeSpaceFragPercent) / 2),
|
|
pTextData->PercentDiskFragged
|
|
);
|
|
|
|
PrintOnStdOut(g_szTempBuffer);
|
|
|
|
if (AnalyzeOnly) {
|
|
if(((pTextData->PercentDiskFragged + pTextData->FreeSpaceFragPercent) / 2) > 10){
|
|
//If the fragmentation on the disk exceeds 10% fragmentation, then recommend defragging.
|
|
PrintToStdOut(IDS_LABEL_CHOOSE_DEFRAGMENT, NULL, TEXT(""), FALSE);
|
|
}
|
|
else{
|
|
//Otherwise tell the user he doesn't need to defragment at this time.
|
|
PrintToStdOut(IDS_LABEL_NO_CHOOSE_DEFRAGMENT, NULL, TEXT(""), FALSE);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (AnalyzeOnly || !count) {
|
|
PrintToStdOut(IDS_ANALYSIS_REPORT_TITLE, NULL, TEXT("\r\n"), FALSE);
|
|
}
|
|
else {
|
|
PrintOnStdOut(L"\r\n\r\n");
|
|
PrintToStdOut(IDS_DEFRAG_REPORT_TITLE, NULL, TEXT("\r\n"), FALSE);
|
|
}
|
|
++count;
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Volume Size
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->DiskSize);
|
|
InsertCommaIntoText(buffer);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->DiskSize, buffer);
|
|
PrintToStdOut(IDS_LABEL_VOLUME_SIZE,IDS_LABEL_EQUAL_SIGN,
|
|
buffer,TRUE);
|
|
|
|
// Cluster Size
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->BytesPerCluster);
|
|
InsertCommaIntoText(buffer);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->BytesPerCluster, buffer);
|
|
PrintToStdOut(IDS_LABEL_CLUSTER_SIZE,IDS_LABEL_EQUAL_SIGN,
|
|
buffer,TRUE);
|
|
|
|
// Used Space
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->UsedSpace);
|
|
InsertCommaIntoText(buffer);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->UsedSpace, buffer);
|
|
PrintToStdOut(IDS_LABEL_USED_SPACE,IDS_LABEL_EQUAL_SIGN,
|
|
buffer,TRUE);
|
|
|
|
// Free Space
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->FreeSpace);
|
|
InsertCommaIntoText(buffer);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->FreeSpace, buffer);
|
|
PrintToStdOut(IDS_LABEL_FREE_SPACE,IDS_LABEL_EQUAL_SIGN,
|
|
buffer,TRUE);
|
|
|
|
// % Free Space
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->FreeSpacePercent);
|
|
PrintToStdOut(IDS_LABEL_PERCENT_FREE_SPACE,
|
|
IDS_LABEL_EQUAL_SIGN,buffer,TRUE,IDS_LABEL_PERCENT_SIGN);
|
|
|
|
// Volume Fragmentation Header
|
|
PrintToStdOut(IDS_LABEL_VOLUME_FRAGMENTATION_HEADING,NULL,TEXT(""),FALSE);
|
|
|
|
// % Total Fragmentation
|
|
_stprintf(buffer, TEXT("%I64d"), (pTextData->PercentDiskFragged + pTextData->FreeSpaceFragPercent) / 2);
|
|
PrintToStdOut(IDS_LABEL_TOTAL_FRAGMENTATION,
|
|
IDS_LABEL_EQUAL_SIGN,buffer,TRUE,IDS_LABEL_PERCENT_SIGN);
|
|
|
|
// % File Fragmentation
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->PercentDiskFragged);
|
|
PrintToStdOut(IDS_LABEL_FILE_FRAGMENTATION,
|
|
IDS_LABEL_EQUAL_SIGN,buffer,TRUE,IDS_LABEL_PERCENT_SIGN);
|
|
|
|
// % Free Space Fragmentation
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->FreeSpaceFragPercent);
|
|
PrintToStdOut(IDS_LABEL_FREE_SPACE_FRAGMENTATION,
|
|
IDS_LABEL_EQUAL_SIGN,buffer,TRUE,IDS_LABEL_PERCENT_SIGN);
|
|
|
|
// File Fragmentation Header
|
|
PrintToStdOut(IDS_LABEL_FILE_FRAGMENTATION_HEADING ,NULL,TEXT(""),FALSE);
|
|
|
|
|
|
// Total Files
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->TotalFiles);
|
|
InsertCommaIntoText(buffer);
|
|
PrintToStdOut(IDS_LABEL_TOTAL_FILES ,IDS_LABEL_EQUAL_SIGN,buffer,TRUE);
|
|
|
|
// Average Files Size
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->AvgFileSize);
|
|
InsertCommaIntoText(buffer);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->AvgFileSize, buffer);
|
|
PrintToStdOut(IDS_LABEL_AVERAGE_FILE_SIZE, IDS_LABEL_EQUAL_SIGN,buffer,TRUE);
|
|
|
|
// Total fragmented files
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->NumFraggedFiles);
|
|
PrintToStdOut(IDS_LABEL_TOTAL_FRAGMENTED_FILES, IDS_LABEL_EQUAL_SIGN,InsertCommaIntoText(buffer),TRUE);
|
|
|
|
// Total excess fragments
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->NumExcessFrags);
|
|
PrintToStdOut(IDS_LABEL_TOTAL_EXCESS_FRAGMENTS, IDS_LABEL_EQUAL_SIGN,InsertCommaIntoText(buffer),TRUE);
|
|
|
|
// Average Fragments per File
|
|
struct lconv *locals = localeconv();
|
|
_stprintf(buffer, TEXT("%d%c%02d"), (UINT)pTextData->AvgFragsPerFile/100,
|
|
((locals && (locals->decimal_point)) ? *(locals->decimal_point) : '.'),
|
|
(UINT)pTextData->AvgFragsPerFile%100);
|
|
PrintToStdOut(IDS_LABEL_AVERAGE_FRAGMENTS_PER_FILE, IDS_LABEL_EQUAL_SIGN,buffer,TRUE);
|
|
|
|
// Pagefile Fragmentation Header
|
|
PrintToStdOut(IDS_LABEL_PAGEFILE_FRAGMENTATION_HEADING, NULL,TEXT(""),FALSE);
|
|
|
|
// Pagefile Size
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->PagefileBytes);
|
|
InsertCommaIntoText(buffer);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->PagefileBytes, buffer);
|
|
PrintToStdOut(IDS_LABEL_PAGEFILE_SIZE, IDS_LABEL_EQUAL_SIGN,buffer,TRUE);
|
|
|
|
// Total Fragments
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->PagefileFrags);
|
|
PrintToStdOut(IDS_LABEL_TOTAL_FRAGMENTS, IDS_LABEL_EQUAL_SIGN,InsertCommaIntoText(buffer),TRUE);
|
|
|
|
// Directory Fragmentation Header
|
|
PrintToStdOut(IDS_LABEL_DIRECTORY_FRAGMENTATION_HEADING,NULL,TEXT(""),FALSE);
|
|
|
|
// Total Directories
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->TotalDirectories);
|
|
PrintToStdOut(IDS_LABEL_TOTAL_DIRECTORIES,
|
|
IDS_LABEL_EQUAL_SIGN,InsertCommaIntoText(buffer),TRUE);
|
|
|
|
|
|
// Fragmented Directories
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->FragmentedDirectories);
|
|
PrintToStdOut(IDS_LABEL_FRAGMENTED_DIRECTORIES,
|
|
IDS_LABEL_EQUAL_SIGN,InsertCommaIntoText(buffer),TRUE);
|
|
|
|
// Excess directory fragments
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->ExcessDirFrags);
|
|
PrintToStdOut(IDS_LABEL_EXCESS_DIRECTORY_FRAGMENTS, IDS_LABEL_EQUAL_SIGN,
|
|
InsertCommaIntoText(buffer), TRUE);
|
|
|
|
|
|
//Only display MFT data if this is an NTFS drive.
|
|
if(_tcscmp(pTextData->cFileSystem, TEXT("NTFS")) == 0) {
|
|
|
|
// MFT Fragmentation Header
|
|
PrintToStdOut(IDS_LABEL_MFT_FRAGMENTATION_HEADING, NULL, TEXT(""), FALSE);
|
|
|
|
// Total MFT Size
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->MFTBytes);
|
|
InsertCommaIntoText(buffer);
|
|
FormatNumber(GetDfrgResHandle(), pTextData->MFTBytes, buffer);
|
|
PrintToStdOut(IDS_LABEL_TOTAL_MFT_SIZE, IDS_LABEL_EQUAL_SIGN,
|
|
buffer, TRUE);
|
|
|
|
// Number of MFT records
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->InUseMFTRecords);
|
|
PrintToStdOut(IDS_LABEL_MFT_RECORD_COUNT, IDS_LABEL_EQUAL_SIGN,
|
|
InsertCommaIntoText(buffer), TRUE);
|
|
|
|
// Percent of MFT in use
|
|
_stprintf(buffer, TEXT("%I64d"), 100*pTextData->InUseMFTRecords/pTextData->TotalMFTRecords);
|
|
PrintToStdOut(IDS_LABEL_PERCENT_MFT_IN_USE, IDS_LABEL_EQUAL_SIGN,
|
|
buffer, TRUE);
|
|
|
|
// Total MFT fragments
|
|
_stprintf(buffer, TEXT("%I64d"), pTextData->MFTExtents);
|
|
PrintToStdOut(IDS_LABEL_TOTAL_MFT_FRAGMENTS, IDS_LABEL_EQUAL_SIGN,
|
|
InsertCommaIntoText(buffer),TRUE);
|
|
|
|
}
|
|
|
|
if (AnalyzeOnly) {
|
|
if(((pTextData->PercentDiskFragged + pTextData->FreeSpaceFragPercent) / 2) > 10){
|
|
//If the fragmentation on the disk exceeds 10% fragmentation, then recommend defragging.
|
|
PrintToStdOut(IDS_LABEL_CHOOSE_DEFRAGMENT, NULL, TEXT(""), FALSE);
|
|
}
|
|
else{
|
|
//Otherwise tell the user he doesn't need to defragment at this time.
|
|
PrintToStdOut(IDS_LABEL_NO_CHOOSE_DEFRAGMENT, NULL, TEXT(""), FALSE);
|
|
}
|
|
}
|
|
|
|
/* // if there are >1 mount points, print them all out
|
|
// yes, this will duplicate the same as the display label
|
|
|
|
// refresh the mount point list
|
|
#ifndef VER4
|
|
pLocalVolume->GetVolumeMountPointList();
|
|
if (pLocalVolume->MountPointCount() > 1){
|
|
for (UINT i=0; i<pLocalVolume->MountPointCount(); i++){
|
|
LoadString(GetDfrgResHandle(), IDS_MOUNTED_VOLUME, tempBuffer, sizeof(tempBuffer)/sizeof(TCHAR));
|
|
_stprintf(buffer, TEXT("%s %s"), tempBuffer, pLocalVolume->MountPoint(i));
|
|
PrintToStdOut(buffer,
|
|
NULL,TEXT(""),FALSE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// write out the display label (usually the drive letter/label)
|
|
LoadString(GetDfrgResHandle(), IDS_LABEL_VOLUME, tempBuffer, sizeof(tempBuffer)/sizeof(TCHAR));
|
|
_stprintf(buffer, TEXT("%s %s"), tempBuffer,pLocalVolume->DisplayLabel());
|
|
PrintToStdOut(buffer
|
|
,NULL,TEXT(""),FALSE);
|
|
*/
|
|
// PrintToStdOut(IDS_PRODUCT_NAME,NULL,TEXT(""),FALSE);
|
|
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*
|
|
ROUTINE DESCRIPTION:
|
|
This routines handles the 're-routed' window posted command messages. The general DataIo (DCOM)
|
|
routines that we use would normally call the PostMessage() routine to handle incoming data requests.
|
|
But for console applications, there is NO windows application to handle the PostMessage() commands,
|
|
so in DataIo.cpp (SetData() routine), it was modified to call a locally define PostMessageConsole()
|
|
routine instead if the user defines "ConsoleApplication" at build time.
|
|
|
|
GLOBAL DATA:
|
|
None
|
|
|
|
INPUT:
|
|
hWnd - Handle to the window - always NULL
|
|
uMsg - The message.
|
|
wParam - The word parameter for the message.
|
|
lParam - the long parameter for the message.
|
|
|
|
Note: These are the same inputs that the WndProc() routine would handle for PostMessage() commands.
|
|
|
|
RETURN:
|
|
TRUE
|
|
|
|
REVISION HISTORY:
|
|
0.0E00 23 September 1997 - Andy Staffer - modified for the DfrgSnap
|
|
0.0E00 15 July 1997 - Gary Quan - Created
|
|
|
|
---------------------------------------------------------------------*/
|
|
|
|
BOOL PostMessageLocal (
|
|
IN HWND hWnd,
|
|
IN UINT Msg,
|
|
IN WPARAM wParam,
|
|
IN LPARAM lParam
|
|
)
|
|
{
|
|
DATA_IO* pDataIo;
|
|
|
|
switch (LOWORD(wParam))
|
|
{
|
|
// says that the engine is instantiated, but not defragging or analyzing
|
|
case ID_ENGINE_START:
|
|
{
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
// defrag has actually started
|
|
case ID_BEGIN_SCAN:
|
|
{
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
|
|
// defrag has ended
|
|
case ID_END_SCAN:
|
|
{
|
|
END_SCAN_DATA* pEndScanData;
|
|
|
|
// Get a pointer to the data sent via DCOM.
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
pEndScanData = (END_SCAN_DATA*)&(pDataIo->cData);
|
|
|
|
// Make sure this is a valid size packet.
|
|
EF_ASSERT(pDataIo->ulDataSize >= sizeof(END_SCAN_DATA));
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
// engine died
|
|
case ID_TERMINATING:
|
|
{
|
|
NOT_DATA* pNotData;
|
|
|
|
// Get a pointer to the data sent via DCOM.
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
pNotData = (NOT_DATA*)&(pDataIo->cData);
|
|
|
|
// Make sure this is a valid size packet.
|
|
EF_ASSERT(pDataIo->ulDataSize >= sizeof(NOT_DATA));
|
|
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
// engine error data
|
|
case ID_ERROR:
|
|
{
|
|
ERROR_DATA* pErrorData;
|
|
|
|
// Get a pointer to the data sent via DCOM.
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
pErrorData = (ERROR_DATA*)&(pDataIo->cData);
|
|
|
|
// Make sure this is a valid size packet.
|
|
EF_ASSERT(pDataIo->ulDataSize >= sizeof(ERROR_DATA));
|
|
|
|
// Save error code
|
|
RetCode = pErrorData->dwErrCode;
|
|
if(!BootOptimize)
|
|
{
|
|
wsprintf(g_szTempBuffer, L"\r\n%s\r\n\r\n", pErrorData->cErrText);
|
|
PrintOnStdErr(g_szTempBuffer);
|
|
}
|
|
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
// sends the progress bar setting and the status that is
|
|
// displayed in the list view
|
|
case ID_STATUS:
|
|
{
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
// sends the list of most fragged files
|
|
case ID_FRAGGED_DATA:
|
|
{
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
// sends the data displayed in the graphic wells (cluster maps)
|
|
case ID_DISP_DATA:
|
|
{
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
// sends the text data that is displayed on the report
|
|
case ID_REPORT_TEXT_DATA:
|
|
{
|
|
TEXT_DATA* pTextData = NULL;
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
|
|
pTextData = (TEXT_DATA*)&(pDataIo->cData);
|
|
EF_ASSERT(pDataIo->ulDataSize >= sizeof(TEXT_DATA));
|
|
|
|
WriteTextReportToStdOut(pTextData);
|
|
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
case ID_PING:
|
|
// Do nothing.
|
|
// This is just a ping sent by the engine to make sure the UI is still up.
|
|
{
|
|
NOT_DATA* pNotData;
|
|
|
|
// Get a pointer to the data sent via DCOM.
|
|
pDataIo = (DATA_IO*)GlobalLock((void*)lParam);
|
|
pNotData = (NOT_DATA*)&(pDataIo->cData);
|
|
|
|
// Make sure this is a valid size packet.
|
|
EF_ASSERT(pDataIo->ulDataSize >= sizeof(NOT_DATA));
|
|
|
|
EH_ASSERT(GlobalUnlock((void*)lParam) == FALSE);
|
|
EH_ASSERT(GlobalFree((void*)lParam) == NULL);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
EF_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|