|
|
/*
**------------------------------------------------------------------------------ ** Module: Disk Cleanup Applet ** File: cleanmgr.cpp ** ** Purpose: WinMain for the Disk Cleanup applet. ** Notes: ** Mod Log: Created by Jason Cobb (2/97) ** ** Copyright (c)1997 Microsoft Corporation, All Rights Reserved **------------------------------------------------------------------------------ */
/*
**------------------------------------------------------------------------------ ** Project include files **------------------------------------------------------------------------------ */ #include "common.h"
#define CPP_FUNCTIONS
#include "crtfree.h"
#include "dmgrinfo.h"
#include "diskguid.h"
#include "resource.h"
#include "textout.h"
#include "dmgrdlg.h"
#include "msprintf.h"
#include "diskutil.h"
#include "seldrive.h"
#include "drivlist.h"
/*
**------------------------------------------------------------------------------ ** Global Defines **------------------------------------------------------------------------------ */ #define SWITCH_HIDEUI 'N'
#define SWITCH_HIDEMOREOPTIONS 'M'
#define SWITCH_DRIVE 'D'
#define SZ_SAGESET TEXT("/SAGESET")
#define SZ_SAGERUN TEXT("/SAGERUN")
#define SZ_TUNEUP TEXT("/TUNEUP")
#define SZ_SETUP TEXT("/SETUP")
#define SZ_LOWDISK TEXT("/LOWDISK")
#define SZ_VERYLOWDISK TEXT("/VERYLOWDISK")
/*
**------------------------------------------------------------------------------ ** Global variables **------------------------------------------------------------------------------ */ HINSTANCE g_hInstance = NULL; HWND g_hDlg = NULL; BOOL g_bAlreadyRunning = FALSE;
/*
**------------------------------------------------------------------------------ ** ParseCommandLine ** ** Purpose: Parses command line for switches ** Parameters: ** lpCmdLine command line string ** pdwFlags pointer to flags DWORD ** pDrive pointer to a character that the drive letter ** is returned in ** Return: TRUE if command line contains /SAGESET or ** /SAGERUN ** FALSE on failure ** Notes; ** Mod Log: Created by Jason Cobb (7/97) **------------------------------------------------------------------------------ */ BOOL ParseCommandLine( LPTSTR lpCmdLine, PDWORD pdwFlags, PULONG pulProfile ) { LPTSTR lpStr = lpCmdLine; BOOL bRet = FALSE; int i; TCHAR szProfile[4];
*pulProfile = 0;
//
//Look for /SAGESET:n on the command line
//
if ((lpStr = StrStrI(lpCmdLine, SZ_SAGESET)) != NULL) { lpStr += lstrlen(SZ_SAGESET); if (*lpStr && *lpStr == ':') { lpStr++; i = 0; while (*lpStr && *lpStr != ' ' && i < 4) { szProfile[i] = *lpStr; lpStr++; i++; }
*pulProfile = StrToInt(szProfile); }
*pdwFlags = FLAG_SAGESET; bRet = TRUE; }
//
//Look for /SAGERUN:n on the command line
//
else if ((lpStr = StrStrI(lpCmdLine, SZ_SAGERUN)) != NULL) { lpStr += lstrlen(SZ_SAGERUN); if (*lpStr && *lpStr == ':') { lpStr++; i = 0; while (*lpStr && *lpStr != ' ' && i < 4) { szProfile[i] = *lpStr; lpStr++; i++; }
*pulProfile = StrToInt(szProfile); }
*pdwFlags = FLAG_SAGERUN; bRet = TRUE; }
//
//Look for /TUNEUP:n
//
else if ((lpStr = StrStrI(lpCmdLine, SZ_TUNEUP)) != NULL) { lpStr += lstrlen(SZ_TUNEUP); if (*lpStr && *lpStr == ':') { lpStr++; i = 0; while (*lpStr && *lpStr != ' ' && i < 4) { szProfile[i] = *lpStr; lpStr++; i++; }
*pulProfile = StrToInt(szProfile); }
*pdwFlags = FLAG_TUNEUP | FLAG_SAGESET; bRet = TRUE; }
//
//Look for /LOWDISK
//
else if ((lpStr = StrStrI(lpCmdLine, SZ_LOWDISK)) != NULL) { lpStr += lstrlen(SZ_LOWDISK); *pdwFlags = FLAG_LOWDISK; bRet = TRUE; }
//
//Look for /VERYLOWDISK
//
else if ((lpStr = StrStrI(lpCmdLine, SZ_VERYLOWDISK)) != NULL) { lpStr += lstrlen(SZ_VERYLOWDISK); *pdwFlags = FLAG_VERYLOWDISK | FLAG_SAGERUN; bRet = TRUE; }
//
//Look for /SETUP
//
else if ((lpStr = StrStrI(lpCmdLine, SZ_SETUP)) != NULL) { lpStr += lstrlen(SZ_SETUP); *pdwFlags = FLAG_SETUP | FLAG_SAGERUN; bRet = TRUE; }
return bRet; }
/*
**------------------------------------------------------------------------------ ** ParseForDrive ** ** Purpose: Parses command line for switches ** Parameters: ** lpCmdLine command line string ** pDrive Buffer that the drive string will be returned ** in, the format will be x:\ ** Return: TRUE on sucess ** FALSE on failure ** Notes; ** Mod Log: Created by Jason Cobb (7/97) **------------------------------------------------------------------------------ */ BOOL ParseForDrive( LPTSTR lpCmdLine, PTCHAR pDrive ) { LPTSTR lpStr = lpCmdLine;
GetBootDrive(pDrive, 4);
while (*lpStr) { //
//Did we find a '-' or a '/'?
//
if ((*lpStr == '-') || (*lpStr == '/')) { lpStr++;
//
//Is this the Drive switch?
//
if (*lpStr && (toupper(*lpStr) == SWITCH_DRIVE)) { //
//Skip any white space
//
lpStr++; while (*lpStr && *lpStr == ' ') lpStr++;
//
//The next character is the driver letter
//
if (*lpStr) { pDrive[0] = (TCHAR)toupper(*lpStr); pDrive[1] = ':'; pDrive[2] = '\\'; pDrive[3] = '\0'; return TRUE; } } }
lpStr++; }
return FALSE; }
BOOL CALLBACK EnumWindowsProc( HWND hWnd, LPARAM lParam ) { TCHAR szWindowTitle[260];
GetWindowText(hWnd, szWindowTitle, ARRAYSIZE(szWindowTitle)); if (StrCmp(szWindowTitle, (LPTSTR)lParam) == 0) { MiDebugMsg((0, "There is already an instance of cleanmgr.exe running on this drive!")); SetForegroundWindow(hWnd); g_bAlreadyRunning = TRUE; return FALSE; }
return TRUE; }
/*
**------------------------------------------------------------------------------ ** ** ProcessMessagesUntilEvent() - This does a message loop until an event or a ** timeout occurs. ** **------------------------------------------------------------------------------ */
DWORD ProcessMessagesUntilEvent(HWND hwnd, HANDLE hEvent, DWORD dwTimeout) { MSG msg; DWORD dwEndTime = GetTickCount() + dwTimeout; LONG lWait = (LONG)dwTimeout; DWORD dwReturn;
for (;;) { dwReturn = MsgWaitForMultipleObjects(1, &hEvent, FALSE, lWait, QS_ALLINPUT);
// were we signalled or did we time out?
if (dwReturn != (WAIT_OBJECT_0 + 1)) { break; }
// we woke up because of messages.
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); if (msg.message == WM_SETCURSOR) { SetCursor(LoadCursor(NULL, IDC_WAIT)); } else { DispatchMessage(&msg); } }
// calculate new timeout value
if (dwTimeout != INFINITE) { lWait = (LONG)dwEndTime - GetTickCount(); } } return dwReturn; }
/*
**------------------------------------------------------------------------------ ** ** WaitForARP() - Waits for the "Add/Remove Programs" Control Panel applet to ** be closed by the user. ** **------------------------------------------------------------------------------ */
void WaitForARP() { HWND hwndARP = NULL; HANDLE hProcessARP = NULL; DWORD dwProcId = 0; TCHAR szARPTitle[128];
// We want to wait until the user closes "Add/Remove Programs" to continue.
// To do this, we must first get an HWND to the dialog window. This is
// accomplished by trying to find the window by its title for no more than
// about 5 seconds (looping 10 times with a 0.5 second delay between attempts).
LoadString(g_hInstance, IDS_ADDREMOVE_TITLE, szARPTitle, ARRAYSIZE(szARPTitle)); for (int i = 0; (i < 10) && (!hwndARP); i++) { hwndARP = FindWindow(NULL, szARPTitle); Sleep(500); }
// If we got the HWND, then we can get the process handle, and wait
// until the Add/Remove process goes away to continue.
if (hwndARP) { GetWindowThreadProcessId(hwndARP, &dwProcId); hProcessARP = OpenProcess(SYNCHRONIZE, FALSE, dwProcId); if (hProcessARP) { ProcessMessagesUntilEvent(hwndARP, hProcessARP, INFINITE); CloseHandle(hProcessARP); } } }
int APIENTRY WinMainT(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { DWORD dwFlags = 0; CleanupMgrInfo *pcmi = NULL; TCHAR szDrive[4]; TCHAR szSageDrive[4]; TCHAR szCaption[64]; TCHAR szInitialMessage[812]; TCHAR szFinalMessage[830]; ULONG ulProfile = 0; WNDCLASS cls = {0}; TCHAR *psz; TCHAR szVolumeName[MAX_PATH]; int RetCode = RETURN_SUCCESS; int nDoAgain = IDYES; ULARGE_INTEGER ulFreeBytesAvailable, ulTotalNumberOfBytes, ulTotalNumberOfFreeBytes; UINT uiTotalFreeMB; STARTUPINFO si; PROCESS_INFORMATION pi; BOOL fFirstInstance = TRUE; HWND hwnd = NULL; HANDLE hEvent = NULL;
//
// Decide if this is the first instance
//
hEvent = CreateEvent (NULL, FALSE, FALSE, TEXT("Cleanmgr: Instance event"));
if (hEvent) { if (GetLastError() == ERROR_ALREADY_EXISTS) { fFirstInstance = FALSE; } }
g_hInstance = hInstance;
InitCommonControls();
//
//Initialize support classes
//
CleanupMgrInfo::Register(hInstance);
cls.lpszClassName = SZ_CLASSNAME; cls.hCursor = LoadCursor(NULL, IDC_ARROW); cls.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ICON_CLEANMGR)); cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); cls.hInstance = hInstance; cls.style = CS_HREDRAW | CS_VREDRAW; cls.lpfnWndProc = DefDlgProc; cls.cbWndExtra = DLGWINDOWEXTRA; RegisterClass(&cls);
//
//Parse the command line
//
ParseCommandLine(lpCmdLine, &dwFlags, &ulProfile);
if (!ParseForDrive(lpCmdLine, szDrive) && !(dwFlags & FLAG_SAGESET) && !(dwFlags & FLAG_SAGERUN)) { PromptForDisk: if (!SelectDrive(szDrive)) goto Cleanup_Exit; } //
//Create window title for comparison
//
if (dwFlags & FLAG_SAGESET) { psz = SHFormatMessage( MSG_APP_SETTINGS_TITLE ); } else { GetVolumeInformation(szDrive, szVolumeName, ARRAYSIZE(szVolumeName), NULL, NULL, NULL, NULL, 0); psz = SHFormatMessage( MSG_APP_TITLE, szVolumeName, szDrive[0] ); }
if (psz) { EnumWindows(EnumWindowsProc, (LPARAM)psz); LocalFree(psz); }
// Also check for any of the final series of dialogs which may display after the main UI has gone away
if (!g_bAlreadyRunning) { LoadString(g_hInstance, IDS_LOWDISK_CAPTION, szCaption, ARRAYSIZE(szCaption)); EnumWindows(EnumWindowsProc, (LPARAM)szCaption);
LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_CAPTION, szCaption, ARRAYSIZE(szCaption)); EnumWindows(EnumWindowsProc, (LPARAM)szCaption); } // If we didn't catch another instance of cleanmgr via EnumWindows(), we catch it with a
// named event. We wait until now to do it so EnumWindows() can bring the other instance's
// window to the foreground if it is up.
if (!fFirstInstance) { g_bAlreadyRunning = TRUE; }
if (g_bAlreadyRunning) { RetCode = FALSE; goto Cleanup_Exit; } if (dwFlags & FLAG_SAGERUN) { szSageDrive[1] = TCHAR(':'); szSageDrive[2] = TCHAR('\\'); szSageDrive[3] = TCHAR('\0');
for (TCHAR c = 'A'; c <= 'Z'; c++) { szSageDrive[0] = c;
//
//Create CleanupMgrInfo object for this drive
//
pcmi = new CleanupMgrInfo(szSageDrive, dwFlags, ulProfile); if (pcmi != NULL && pcmi->isAbortScan() == FALSE && pcmi->isValid()) { pcmi->purgeClients(); }
// Keep the latest scan window handle (but hide the window)
if (pcmi && pcmi->hAbortScanWnd) { hwnd = pcmi->hAbortScanWnd; ShowWindow(hwnd, SW_HIDE); } //
//Destroy the CleanupMgrInfo object for this drive
//
if (pcmi) { RetCode = pcmi->dwReturnCode; delete pcmi; pcmi = NULL; } } } else { //
//Create CleanupMgrInfo object
//
pcmi = new CleanupMgrInfo(szDrive, dwFlags, ulProfile); if (pcmi != NULL && pcmi->isAbortScan() == FALSE) { //
//User specified an invalid drive letter
//
if (!(pcmi->isValid())) { // dismiss the dialog first
if ( pcmi->hAbortScanWnd ) { pcmi->bAbortScan = TRUE;
//
//Wait for scan thread to finish
//
WaitForSingleObject(pcmi->hAbortScanThread, INFINITE);
pcmi->bAbortScan = FALSE; } TCHAR szWarningTitle[256]; TCHAR *pszWarning; pszWarning = SHFormatMessage( MSG_BAD_DRIVE_LETTER, szDrive ); LoadString(g_hInstance, IDS_TITLE, szWarningTitle, ARRAYSIZE(szWarningTitle));
MessageBox(NULL, pszWarning, szWarningTitle, MB_OK | MB_SETFOREGROUND); LocalFree(pszWarning);
if (pcmi) { delete pcmi; pcmi = NULL; goto PromptForDisk; } } else { //Bring up the main dialog
int nResult = DisplayCleanMgrProperties(NULL, (LPARAM)pcmi); if (nResult) { pcmi->dwUIFlags |= FLAG_SAVE_STATE; //
//Need to purge the clients if we are NOT
//in the SAGE settings mode.
//
if (!(dwFlags & FLAG_SAGESET) && !(dwFlags & FLAG_TUNEUP) && pcmi->bPurgeFiles) pcmi->purgeClients(); } } }
//
//Destroy the CleanupMgrInfo object
//
if (pcmi) { RetCode = pcmi->dwReturnCode; delete pcmi; pcmi = NULL; } }
GetStartupInfo(&si);
// If we were called on a low free disk space case, we want to inform the user of how much space remains,
// and encourage them to free up space via Add/Remove programs until they reach 200MB free in the /LOWDISK
// case, or 50MB free in the /VERYLOWDISK case.
while (nDoAgain == IDYES) { BOOL bFinalTime = FALSE; nDoAgain = IDNO;
// Bring up the Low Disk message box
if (dwFlags & FLAG_LOWDISK) { GetDiskFreeSpaceEx(szDrive, &ulFreeBytesAvailable, &ulTotalNumberOfBytes, &ulTotalNumberOfFreeBytes); uiTotalFreeMB = (UINT) (ulTotalNumberOfFreeBytes.QuadPart / (NUM_BYTES_IN_MB)); if (uiTotalFreeMB < 200) { if (uiTotalFreeMB < 80) { LoadString(g_hInstance, IDS_LOWDISK_MESSAGE2, szInitialMessage, ARRAYSIZE(szInitialMessage)); } else { LoadString(g_hInstance, IDS_LOWDISK_MESSAGE, szInitialMessage, ARRAYSIZE(szInitialMessage)); }
LoadString(g_hInstance, IDS_LOWDISK_CAPTION, szCaption, ARRAYSIZE(szCaption)); wsprintf(szFinalMessage, szInitialMessage, uiTotalFreeMB); nDoAgain = MessageBox(hwnd, szFinalMessage, szCaption, MB_YESNO | MB_ICONWARNING | MB_TOPMOST); } else { LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_CAPTION, szCaption, ARRAYSIZE(szCaption)); LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_MESSAGE, szInitialMessage, ARRAYSIZE(szInitialMessage)); wsprintf(szFinalMessage, szInitialMessage, uiTotalFreeMB); nDoAgain = MessageBox(hwnd, szFinalMessage, szCaption, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_TOPMOST); bFinalTime = TRUE; } } else if (dwFlags & FLAG_VERYLOWDISK) { // Bring up the Very Low Disk message box
GetDiskFreeSpaceEx(szDrive, &ulFreeBytesAvailable, &ulTotalNumberOfBytes, &ulTotalNumberOfFreeBytes); uiTotalFreeMB = (UINT) (ulTotalNumberOfFreeBytes.QuadPart / (NUM_BYTES_IN_MB)); if (uiTotalFreeMB < 50) { LoadString(g_hInstance, IDS_LOWDISK_CAPTION, szCaption, ARRAYSIZE(szCaption)); LoadString(g_hInstance, IDS_VERYLOWDISK_MESSAGE, szInitialMessage, ARRAYSIZE(szInitialMessage)); wsprintf(szFinalMessage, szInitialMessage, uiTotalFreeMB); nDoAgain = MessageBox(hwnd, szFinalMessage, szCaption, MB_YESNO | MB_ICONSTOP | MB_TOPMOST); } else { LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_CAPTION, szCaption, ARRAYSIZE(szCaption)); LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_MESSAGE, szInitialMessage, ARRAYSIZE(szInitialMessage)); wsprintf(szFinalMessage, szInitialMessage, uiTotalFreeMB); nDoAgain = MessageBox(hwnd, szFinalMessage, szCaption, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_TOPMOST); bFinalTime = TRUE; } }
if (nDoAgain == IDYES) { // Launch the Add/Remove Programs dialog
lstrcpy(szInitialMessage, SZ_RUN_INSTALLED_PROGRAMS); if (CreateProcess(NULL, szInitialMessage, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread);
// Only bother to wait around if it is not our final time through
if (! bFinalTime) { WaitForARP(); } else { // If this was our final time through, then set the flag
// to break out of the loop
nDoAgain = IDNO; } } else { // If we cannot launch Add/Remove programs for some reason, we break
// out of the loop
nDoAgain = IDNO; } } }
Cleanup_Exit:
if (hEvent) { CloseHandle (hEvent); }
CleanupMgrInfo::Unregister();
return RetCode; }
STDAPI_(int) ModuleEntry(void) { int i; STARTUPINFOA si; LPTSTR pszCmdLine = GetCommandLine();
//
// We don't want the "No disk in drive X:" requesters, so we set
// the critical error mask such that calls will just silently fail
//
SetErrorMode(SEM_FAILCRITICALERRORS);
if ( *pszCmdLine == TEXT('\"') ) { /*
* Scan, and skip over, subsequent characters until * another double-quote or a null is encountered. */ while ( *++pszCmdLine && (*pszCmdLine != TEXT('\"')) ); /*
* If we stopped on a double-quote (usual case), skip * over it. */ if ( *pszCmdLine == TEXT('\"') ) pszCmdLine++; } else { while (*pszCmdLine > TEXT(' ')) pszCmdLine++; }
/*
* Skip past any white space preceeding the second token. */ while (*pszCmdLine && (*pszCmdLine <= TEXT(' '))) { pszCmdLine++; }
si.dwFlags = 0; GetStartupInfoA(&si);
i = WinMainT(GetModuleHandle(NULL), NULL, pszCmdLine, si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);
// Since we now have a way for an extension to tell us when it is finished,
// we will terminate all processes when the main thread goes away.
return i; }
void _cdecl main() { ModuleEntry(); }
|