Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

756 lines
22 KiB

  1. /*
  2. **------------------------------------------------------------------------------
  3. ** Module: Disk Cleanup Applet
  4. ** File: cleanmgr.cpp
  5. **
  6. ** Purpose: WinMain for the Disk Cleanup applet.
  7. ** Notes:
  8. ** Mod Log: Created by Jason Cobb (2/97)
  9. **
  10. ** Copyright (c)1997 Microsoft Corporation, All Rights Reserved
  11. **------------------------------------------------------------------------------
  12. */
  13. /*
  14. **------------------------------------------------------------------------------
  15. ** Project include files
  16. **------------------------------------------------------------------------------
  17. */
  18. #include "common.h"
  19. #define CPP_FUNCTIONS
  20. #include "crtfree.h"
  21. #include "dmgrinfo.h"
  22. #include "diskguid.h"
  23. #include "resource.h"
  24. #include "textout.h"
  25. #include "dmgrdlg.h"
  26. #include "msprintf.h"
  27. #include "diskutil.h"
  28. #include "seldrive.h"
  29. #include "drivlist.h"
  30. /*
  31. **------------------------------------------------------------------------------
  32. ** Global Defines
  33. **------------------------------------------------------------------------------
  34. */
  35. #define SWITCH_HIDEUI 'N'
  36. #define SWITCH_HIDEMOREOPTIONS 'M'
  37. #define SWITCH_DRIVE 'D'
  38. #define SZ_SAGESET TEXT("/SAGESET")
  39. #define SZ_SAGERUN TEXT("/SAGERUN")
  40. #define SZ_TUNEUP TEXT("/TUNEUP")
  41. #define SZ_SETUP TEXT("/SETUP")
  42. #define SZ_LOWDISK TEXT("/LOWDISK")
  43. #define SZ_VERYLOWDISK TEXT("/VERYLOWDISK")
  44. /*
  45. **------------------------------------------------------------------------------
  46. ** Global variables
  47. **------------------------------------------------------------------------------
  48. */
  49. HINSTANCE g_hInstance = NULL;
  50. HWND g_hDlg = NULL;
  51. BOOL g_bAlreadyRunning = FALSE;
  52. /*
  53. **------------------------------------------------------------------------------
  54. ** ParseCommandLine
  55. **
  56. ** Purpose: Parses command line for switches
  57. ** Parameters:
  58. ** lpCmdLine command line string
  59. ** pdwFlags pointer to flags DWORD
  60. ** pDrive pointer to a character that the drive letter
  61. ** is returned in
  62. ** Return: TRUE if command line contains /SAGESET or
  63. ** /SAGERUN
  64. ** FALSE on failure
  65. ** Notes;
  66. ** Mod Log: Created by Jason Cobb (7/97)
  67. **------------------------------------------------------------------------------
  68. */
  69. BOOL
  70. ParseCommandLine(
  71. LPTSTR lpCmdLine,
  72. PDWORD pdwFlags,
  73. PULONG pulProfile
  74. )
  75. {
  76. LPTSTR lpStr = lpCmdLine;
  77. BOOL bRet = FALSE;
  78. int i;
  79. TCHAR szProfile[4];
  80. *pulProfile = 0;
  81. //
  82. //Look for /SAGESET:n on the command line
  83. //
  84. if ((lpStr = StrStrI(lpCmdLine, SZ_SAGESET)) != NULL)
  85. {
  86. lpStr += lstrlen(SZ_SAGESET);
  87. if (*lpStr && *lpStr == ':')
  88. {
  89. lpStr++;
  90. i = 0;
  91. while (*lpStr && *lpStr != ' ' && i < 4)
  92. {
  93. szProfile[i] = *lpStr;
  94. lpStr++;
  95. i++;
  96. }
  97. *pulProfile = StrToInt(szProfile);
  98. }
  99. *pdwFlags = FLAG_SAGESET;
  100. bRet = TRUE;
  101. }
  102. //
  103. //Look for /SAGERUN:n on the command line
  104. //
  105. else if ((lpStr = StrStrI(lpCmdLine, SZ_SAGERUN)) != NULL)
  106. {
  107. lpStr += lstrlen(SZ_SAGERUN);
  108. if (*lpStr && *lpStr == ':')
  109. {
  110. lpStr++;
  111. i = 0;
  112. while (*lpStr && *lpStr != ' ' && i < 4)
  113. {
  114. szProfile[i] = *lpStr;
  115. lpStr++;
  116. i++;
  117. }
  118. *pulProfile = StrToInt(szProfile);
  119. }
  120. *pdwFlags = FLAG_SAGERUN;
  121. bRet = TRUE;
  122. }
  123. //
  124. //Look for /TUNEUP:n
  125. //
  126. else if ((lpStr = StrStrI(lpCmdLine, SZ_TUNEUP)) != NULL)
  127. {
  128. lpStr += lstrlen(SZ_TUNEUP);
  129. if (*lpStr && *lpStr == ':')
  130. {
  131. lpStr++;
  132. i = 0;
  133. while (*lpStr && *lpStr != ' ' && i < 4)
  134. {
  135. szProfile[i] = *lpStr;
  136. lpStr++;
  137. i++;
  138. }
  139. *pulProfile = StrToInt(szProfile);
  140. }
  141. *pdwFlags = FLAG_TUNEUP | FLAG_SAGESET;
  142. bRet = TRUE;
  143. }
  144. //
  145. //Look for /LOWDISK
  146. //
  147. else if ((lpStr = StrStrI(lpCmdLine, SZ_LOWDISK)) != NULL)
  148. {
  149. lpStr += lstrlen(SZ_LOWDISK);
  150. *pdwFlags = FLAG_LOWDISK;
  151. bRet = TRUE;
  152. }
  153. //
  154. //Look for /VERYLOWDISK
  155. //
  156. else if ((lpStr = StrStrI(lpCmdLine, SZ_VERYLOWDISK)) != NULL)
  157. {
  158. lpStr += lstrlen(SZ_VERYLOWDISK);
  159. *pdwFlags = FLAG_VERYLOWDISK | FLAG_SAGERUN;
  160. bRet = TRUE;
  161. }
  162. //
  163. //Look for /SETUP
  164. //
  165. else if ((lpStr = StrStrI(lpCmdLine, SZ_SETUP)) != NULL)
  166. {
  167. lpStr += lstrlen(SZ_SETUP);
  168. *pdwFlags = FLAG_SETUP | FLAG_SAGERUN;
  169. bRet = TRUE;
  170. }
  171. return bRet;
  172. }
  173. /*
  174. **------------------------------------------------------------------------------
  175. ** ParseForDrive
  176. **
  177. ** Purpose: Parses command line for switches
  178. ** Parameters:
  179. ** lpCmdLine command line string
  180. ** pDrive Buffer that the drive string will be returned
  181. ** in, the format will be x:\
  182. ** Return: TRUE on sucess
  183. ** FALSE on failure
  184. ** Notes;
  185. ** Mod Log: Created by Jason Cobb (7/97)
  186. **------------------------------------------------------------------------------
  187. */
  188. BOOL
  189. ParseForDrive(
  190. LPTSTR lpCmdLine,
  191. PTCHAR pDrive
  192. )
  193. {
  194. LPTSTR lpStr = lpCmdLine;
  195. GetBootDrive(pDrive, 4);
  196. while (*lpStr)
  197. {
  198. //
  199. //Did we find a '-' or a '/'?
  200. //
  201. if ((*lpStr == '-') || (*lpStr == '/'))
  202. {
  203. lpStr++;
  204. //
  205. //Is this the Drive switch?
  206. //
  207. if (*lpStr && (toupper(*lpStr) == SWITCH_DRIVE))
  208. {
  209. //
  210. //Skip any white space
  211. //
  212. lpStr++;
  213. while (*lpStr && *lpStr == ' ')
  214. lpStr++;
  215. //
  216. //The next character is the driver letter
  217. //
  218. if (*lpStr)
  219. {
  220. pDrive[0] = (TCHAR)toupper(*lpStr);
  221. pDrive[1] = ':';
  222. pDrive[2] = '\\';
  223. pDrive[3] = '\0';
  224. return TRUE;
  225. }
  226. }
  227. }
  228. lpStr++;
  229. }
  230. return FALSE;
  231. }
  232. BOOL CALLBACK EnumWindowsProc(
  233. HWND hWnd,
  234. LPARAM lParam
  235. )
  236. {
  237. TCHAR szWindowTitle[260];
  238. GetWindowText(hWnd, szWindowTitle, ARRAYSIZE(szWindowTitle));
  239. if (StrCmp(szWindowTitle, (LPTSTR)lParam) == 0)
  240. {
  241. MiDebugMsg((0, "There is already an instance of cleanmgr.exe running on this drive!"));
  242. SetForegroundWindow(hWnd);
  243. g_bAlreadyRunning = TRUE;
  244. return FALSE;
  245. }
  246. return TRUE;
  247. }
  248. /*
  249. **------------------------------------------------------------------------------
  250. **
  251. ** ProcessMessagesUntilEvent() - This does a message loop until an event or a
  252. ** timeout occurs.
  253. **
  254. **------------------------------------------------------------------------------
  255. */
  256. DWORD ProcessMessagesUntilEvent(HWND hwnd, HANDLE hEvent, DWORD dwTimeout)
  257. {
  258. MSG msg;
  259. DWORD dwEndTime = GetTickCount() + dwTimeout;
  260. LONG lWait = (LONG)dwTimeout;
  261. DWORD dwReturn;
  262. for (;;)
  263. {
  264. dwReturn = MsgWaitForMultipleObjects(1, &hEvent,
  265. FALSE, lWait, QS_ALLINPUT);
  266. // were we signalled or did we time out?
  267. if (dwReturn != (WAIT_OBJECT_0 + 1))
  268. {
  269. break;
  270. }
  271. // we woke up because of messages.
  272. while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
  273. {
  274. TranslateMessage(&msg);
  275. if (msg.message == WM_SETCURSOR)
  276. {
  277. SetCursor(LoadCursor(NULL, IDC_WAIT));
  278. }
  279. else
  280. {
  281. DispatchMessage(&msg);
  282. }
  283. }
  284. // calculate new timeout value
  285. if (dwTimeout != INFINITE)
  286. {
  287. lWait = (LONG)dwEndTime - GetTickCount();
  288. }
  289. }
  290. return dwReturn;
  291. }
  292. /*
  293. **------------------------------------------------------------------------------
  294. **
  295. ** WaitForARP() - Waits for the "Add/Remove Programs" Control Panel applet to
  296. ** be closed by the user.
  297. **
  298. **------------------------------------------------------------------------------
  299. */
  300. void WaitForARP()
  301. {
  302. HWND hwndARP = NULL;
  303. HANDLE hProcessARP = NULL;
  304. DWORD dwProcId = 0;
  305. TCHAR szARPTitle[128];
  306. // We want to wait until the user closes "Add/Remove Programs" to continue.
  307. // To do this, we must first get an HWND to the dialog window. This is
  308. // accomplished by trying to find the window by its title for no more than
  309. // about 5 seconds (looping 10 times with a 0.5 second delay between attempts).
  310. LoadString(g_hInstance, IDS_ADDREMOVE_TITLE, szARPTitle, ARRAYSIZE(szARPTitle));
  311. for (int i = 0; (i < 10) && (!hwndARP); i++)
  312. {
  313. hwndARP = FindWindow(NULL, szARPTitle);
  314. Sleep(500);
  315. }
  316. // If we got the HWND, then we can get the process handle, and wait
  317. // until the Add/Remove process goes away to continue.
  318. if (hwndARP)
  319. {
  320. GetWindowThreadProcessId(hwndARP, &dwProcId);
  321. hProcessARP = OpenProcess(SYNCHRONIZE, FALSE, dwProcId);
  322. if (hProcessARP)
  323. {
  324. ProcessMessagesUntilEvent(hwndARP, hProcessARP, INFINITE);
  325. CloseHandle(hProcessARP);
  326. }
  327. }
  328. }
  329. int APIENTRY WinMainT(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
  330. {
  331. DWORD dwFlags = 0;
  332. CleanupMgrInfo *pcmi = NULL;
  333. TCHAR szDrive[4];
  334. TCHAR szSageDrive[4];
  335. TCHAR szCaption[64];
  336. TCHAR szInitialMessage[812];
  337. TCHAR szFinalMessage[830];
  338. ULONG ulProfile = 0;
  339. WNDCLASS cls = {0};
  340. TCHAR szVolumeName[MAX_PATH];
  341. int RetCode = RETURN_SUCCESS;
  342. int nDoAgain = IDYES;
  343. ULARGE_INTEGER ulFreeBytesAvailable,
  344. ulTotalNumberOfBytes,
  345. ulTotalNumberOfFreeBytes;
  346. UINT uiTotalFreeMB;
  347. STARTUPINFO si;
  348. PROCESS_INFORMATION pi;
  349. BOOL fFirstInstance = TRUE;
  350. HWND hwnd = NULL;
  351. HANDLE hEvent = NULL;
  352. //
  353. // Decide if this is the first instance
  354. //
  355. hEvent = CreateEvent (NULL, FALSE, FALSE, TEXT("Cleanmgr: Instance event"));
  356. if (hEvent)
  357. {
  358. if (GetLastError() == ERROR_ALREADY_EXISTS)
  359. {
  360. fFirstInstance = FALSE;
  361. }
  362. }
  363. g_hInstance = hInstance;
  364. InitCommonControls();
  365. //
  366. //Initialize support classes
  367. //
  368. CleanupMgrInfo::Register(hInstance);
  369. cls.lpszClassName = SZ_CLASSNAME;
  370. cls.hCursor = LoadCursor(NULL, IDC_ARROW);
  371. cls.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(ICON_CLEANMGR));
  372. cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  373. cls.hInstance = hInstance;
  374. cls.style = CS_HREDRAW | CS_VREDRAW;
  375. cls.lpfnWndProc = DefDlgProc;
  376. cls.cbWndExtra = DLGWINDOWEXTRA;
  377. RegisterClass(&cls);
  378. //
  379. //Parse the command line
  380. //
  381. ParseCommandLine(lpCmdLine, &dwFlags, &ulProfile);
  382. if (!ParseForDrive(lpCmdLine, szDrive) &&
  383. !(dwFlags & FLAG_SAGESET) &&
  384. !(dwFlags & FLAG_SAGERUN))
  385. {
  386. PromptForDisk:
  387. if (!SelectDrive(szDrive))
  388. goto Cleanup_Exit;
  389. }
  390. // Also check for any of the final series of dialogs which may display after the main UI has gone away
  391. if (!g_bAlreadyRunning)
  392. {
  393. LoadString(g_hInstance, IDS_LOWDISK_CAPTION, szCaption, ARRAYSIZE(szCaption));
  394. EnumWindows(EnumWindowsProc, (LPARAM)szCaption);
  395. LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_CAPTION, szCaption, ARRAYSIZE(szCaption));
  396. EnumWindows(EnumWindowsProc, (LPARAM)szCaption);
  397. }
  398. // If we didn't catch another instance of cleanmgr via EnumWindows(), we catch it with a
  399. // named event. We wait until now to do it so EnumWindows() can bring the other instance's
  400. // window to the foreground if it is up.
  401. if (!fFirstInstance)
  402. {
  403. g_bAlreadyRunning = TRUE;
  404. }
  405. if (g_bAlreadyRunning)
  406. {
  407. RetCode = FALSE;
  408. goto Cleanup_Exit;
  409. }
  410. if (dwFlags & FLAG_SAGERUN)
  411. {
  412. szSageDrive[1] = TCHAR(':');
  413. szSageDrive[2] = TCHAR('\\');
  414. szSageDrive[3] = TCHAR('\0');
  415. for (TCHAR c = 'A'; c <= 'Z'; c++)
  416. {
  417. szSageDrive[0] = c;
  418. //
  419. //Create CleanupMgrInfo object for this drive
  420. //
  421. pcmi = new CleanupMgrInfo(szSageDrive, dwFlags, ulProfile);
  422. if (pcmi != NULL && pcmi->isAbortScan() == FALSE && pcmi->isValid())
  423. {
  424. pcmi->purgeClients();
  425. }
  426. // Keep the latest scan window handle (but hide the window)
  427. if (pcmi && pcmi->hAbortScanWnd)
  428. {
  429. hwnd = pcmi->hAbortScanWnd;
  430. ShowWindow(hwnd, SW_HIDE);
  431. }
  432. //
  433. //Destroy the CleanupMgrInfo object for this drive
  434. //
  435. if (pcmi)
  436. {
  437. RetCode = pcmi->dwReturnCode;
  438. if ( pcmi->hAbortScanWnd )
  439. {
  440. pcmi->bAbortScan = TRUE;
  441. WaitForSingleObject(pcmi->hAbortScanThread, INFINITE);
  442. pcmi->bAbortScan = FALSE;
  443. }
  444. delete pcmi;
  445. pcmi = NULL;
  446. }
  447. }
  448. }
  449. else
  450. {
  451. //
  452. //Create CleanupMgrInfo object
  453. //
  454. pcmi = new CleanupMgrInfo(szDrive, dwFlags, ulProfile);
  455. if (pcmi != NULL && pcmi->isAbortScan() == FALSE)
  456. {
  457. //
  458. //User specified an invalid drive letter
  459. //
  460. if (!(pcmi->isValid()))
  461. {
  462. // dismiss the dialog first
  463. if ( pcmi->hAbortScanWnd )
  464. {
  465. pcmi->bAbortScan = TRUE;
  466. //
  467. //Wait for scan thread to finish
  468. //
  469. WaitForSingleObject(pcmi->hAbortScanThread, INFINITE);
  470. pcmi->bAbortScan = FALSE;
  471. }
  472. TCHAR szWarningTitle[256];
  473. TCHAR *pszWarning;
  474. pszWarning = SHFormatMessage( MSG_BAD_DRIVE_LETTER, szDrive );
  475. LoadString(g_hInstance, IDS_TITLE, szWarningTitle, ARRAYSIZE(szWarningTitle));
  476. MessageBox(NULL, pszWarning, szWarningTitle, MB_OK | MB_SETFOREGROUND);
  477. LocalFree(pszWarning);
  478. if (pcmi)
  479. {
  480. delete pcmi;
  481. pcmi = NULL;
  482. goto PromptForDisk;
  483. }
  484. }
  485. else
  486. {
  487. //Bring up the main dialog
  488. int nResult = DisplayCleanMgrProperties(NULL, (LPARAM)pcmi);
  489. if (nResult)
  490. {
  491. pcmi->dwUIFlags |= FLAG_SAVE_STATE;
  492. //
  493. //Need to purge the clients if we are NOT
  494. //in the SAGE settings mode.
  495. //
  496. if (!(dwFlags & FLAG_SAGESET) && !(dwFlags & FLAG_TUNEUP) && pcmi->bPurgeFiles)
  497. pcmi->purgeClients();
  498. }
  499. }
  500. }
  501. //
  502. //Destroy the CleanupMgrInfo object
  503. //
  504. if (pcmi)
  505. {
  506. RetCode = pcmi->dwReturnCode;
  507. delete pcmi;
  508. pcmi = NULL;
  509. }
  510. }
  511. GetStartupInfo(&si);
  512. // If we were called on a low free disk space case, we want to inform the user of how much space remains,
  513. // and encourage them to free up space via Add/Remove programs until they reach 200MB free in the /LOWDISK
  514. // case, or 50MB free in the /VERYLOWDISK case.
  515. while (nDoAgain == IDYES)
  516. {
  517. BOOL bFinalTime = FALSE;
  518. nDoAgain = IDNO;
  519. // Bring up the Low Disk message box
  520. if (dwFlags & FLAG_LOWDISK)
  521. {
  522. GetDiskFreeSpaceEx(szDrive, &ulFreeBytesAvailable, &ulTotalNumberOfBytes, &ulTotalNumberOfFreeBytes);
  523. uiTotalFreeMB = (UINT) (ulTotalNumberOfFreeBytes.QuadPart / (NUM_BYTES_IN_MB));
  524. if (uiTotalFreeMB < 200)
  525. {
  526. if (uiTotalFreeMB < 80)
  527. {
  528. LoadString(g_hInstance, IDS_LOWDISK_MESSAGE2, szInitialMessage, ARRAYSIZE(szInitialMessage));
  529. }
  530. else
  531. {
  532. LoadString(g_hInstance, IDS_LOWDISK_MESSAGE, szInitialMessage, ARRAYSIZE(szInitialMessage));
  533. }
  534. LoadString(g_hInstance, IDS_LOWDISK_CAPTION, szCaption, ARRAYSIZE(szCaption));
  535. StringCchPrintf(szFinalMessage, ARRAYSIZE(szFinalMessage), szInitialMessage, uiTotalFreeMB);
  536. nDoAgain = MessageBox(hwnd, szFinalMessage, szCaption, MB_YESNO | MB_ICONWARNING | MB_TOPMOST);
  537. }
  538. else
  539. {
  540. LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_CAPTION, szCaption, ARRAYSIZE(szCaption));
  541. LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_MESSAGE, szInitialMessage, ARRAYSIZE(szInitialMessage));
  542. StringCchPrintf(szFinalMessage, ARRAYSIZE(szFinalMessage), szInitialMessage, uiTotalFreeMB);
  543. nDoAgain = MessageBox(hwnd, szFinalMessage, szCaption, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_TOPMOST);
  544. bFinalTime = TRUE;
  545. }
  546. }
  547. else if (dwFlags & FLAG_VERYLOWDISK)
  548. {
  549. // Bring up the Very Low Disk message box
  550. GetDiskFreeSpaceEx(szDrive, &ulFreeBytesAvailable, &ulTotalNumberOfBytes, &ulTotalNumberOfFreeBytes);
  551. uiTotalFreeMB = (UINT) (ulTotalNumberOfFreeBytes.QuadPart / (NUM_BYTES_IN_MB));
  552. if (uiTotalFreeMB < 50)
  553. {
  554. LoadString(g_hInstance, IDS_LOWDISK_CAPTION, szCaption, ARRAYSIZE(szCaption));
  555. LoadString(g_hInstance, IDS_VERYLOWDISK_MESSAGE, szInitialMessage, ARRAYSIZE(szInitialMessage));
  556. StringCchPrintf(szFinalMessage, ARRAYSIZE(szFinalMessage), szInitialMessage, uiTotalFreeMB);
  557. nDoAgain = MessageBox(hwnd, szFinalMessage, szCaption, MB_YESNO | MB_ICONSTOP | MB_TOPMOST);
  558. }
  559. else
  560. {
  561. LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_CAPTION, szCaption, ARRAYSIZE(szCaption));
  562. LoadString(g_hInstance, IDS_LOWDISK_SUCCESS_MESSAGE, szInitialMessage, ARRAYSIZE(szInitialMessage));
  563. StringCchPrintf(szFinalMessage, ARRAYSIZE(szFinalMessage), szInitialMessage, uiTotalFreeMB);
  564. nDoAgain = MessageBox(hwnd, szFinalMessage, szCaption, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_TOPMOST);
  565. bFinalTime = TRUE;
  566. }
  567. }
  568. if (nDoAgain == IDYES)
  569. {
  570. // Launch the Add/Remove Programs dialog
  571. TCHAR szFullPath[MAX_PATH];
  572. HRESULT hr = StringCchCopy(szInitialMessage, ARRAYSIZE(szInitialMessage), SZ_RUN_INSTALLED_PROGRAMS);
  573. hr = GetSystemDirectory(szFullPath, ARRAYSIZE(szFullPath))? S_OK : E_FAIL;
  574. if (SUCCEEDED(hr))
  575. {
  576. hr = PathAppend(szFullPath, szInitialMessage) ? S_OK : E_FAIL;
  577. }
  578. if (SUCCEEDED(hr) && CreateProcess(NULL, szFullPath, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
  579. {
  580. CloseHandle(pi.hProcess);
  581. CloseHandle(pi.hThread);
  582. // Only bother to wait around if it is not our final time through
  583. if (! bFinalTime)
  584. {
  585. WaitForARP();
  586. }
  587. else
  588. {
  589. // If this was our final time through, then set the flag
  590. // to break out of the loop
  591. nDoAgain = IDNO;
  592. }
  593. }
  594. else
  595. {
  596. // If we cannot launch Add/Remove programs for some reason, we break
  597. // out of the loop
  598. nDoAgain = IDNO;
  599. }
  600. }
  601. }
  602. Cleanup_Exit:
  603. if (hEvent)
  604. {
  605. CloseHandle (hEvent);
  606. }
  607. CleanupMgrInfo::Unregister();
  608. return RetCode;
  609. }
  610. STDAPI_(int) ModuleEntry(void)
  611. {
  612. int i;
  613. STARTUPINFOA si;
  614. LPTSTR pszCmdLine = GetCommandLine();
  615. //
  616. // We don't want the "No disk in drive X:" requesters, so we set
  617. // the critical error mask such that calls will just silently fail
  618. //
  619. SetErrorMode(SEM_FAILCRITICALERRORS);
  620. if ( *pszCmdLine == TEXT('\"') ) {
  621. /*
  622. * Scan, and skip over, subsequent characters until
  623. * another double-quote or a null is encountered.
  624. */
  625. while ( *++pszCmdLine && (*pszCmdLine
  626. != TEXT('\"')) );
  627. /*
  628. * If we stopped on a double-quote (usual case), skip
  629. * over it.
  630. */
  631. if ( *pszCmdLine == TEXT('\"') )
  632. pszCmdLine++;
  633. }
  634. else {
  635. while (*pszCmdLine > TEXT(' '))
  636. pszCmdLine++;
  637. }
  638. /*
  639. * Skip past any white space preceeding the second token.
  640. */
  641. while (*pszCmdLine && (*pszCmdLine <= TEXT(' '))) {
  642. pszCmdLine++;
  643. }
  644. si.dwFlags = 0;
  645. GetStartupInfoA(&si);
  646. i = WinMainT(GetModuleHandle(NULL), NULL, pszCmdLine,
  647. si.dwFlags & STARTF_USESHOWWINDOW ? si.wShowWindow : SW_SHOWDEFAULT);
  648. // Since we now have a way for an extension to tell us when it is finished,
  649. // we will terminate all processes when the main thread goes away.
  650. return i;
  651. }
  652. void _cdecl main()
  653. {
  654. ModuleEntry();
  655. }