Source code of Windows XP (NT5)
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.

647 lines
17 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. alert.c
  5. Abstract:
  6. Implements the code to alert the user to problems that might be
  7. left on the system after uninstall.
  8. Author:
  9. Jim Schmidt (jimschm) 07-Mar-2001
  10. Revision History:
  11. --*/
  12. #include "pch.h"
  13. #include "undop.h"
  14. #include "resource.h"
  15. typedef struct {
  16. ULONGLONG Checksum;
  17. BOOL InOldList;
  18. BOOL InNewList;
  19. BOOL ChangedFlag;
  20. BOOL Duplicate;
  21. UINT OldReferences;
  22. UINT NewReferences;
  23. WCHAR DisplayString[];
  24. } APPLISTITEM, *PAPPLISTITEM;
  25. typedef enum {
  26. OLD_LIST,
  27. NEW_LIST
  28. } APPLIST;
  29. BOOL
  30. pAppendAppToGrowList (
  31. IN OUT PGROWLIST List,
  32. IN PCWSTR DisplayString,
  33. IN ULONGLONG Checksum,
  34. IN APPLIST WhichList
  35. )
  36. /*++
  37. Routine Description:
  38. pAppendAppToGrowList manages a private list of installed apps. The list
  39. contains information about the app, such as if it was installed originally,
  40. if it is installed now, what its name is, and if it has changed.
  41. Arguments:
  42. List - Specifies the grow list containing items; receives the updated list
  43. DisplayString - Specifies the Add/Remove Programs display string
  44. Checksum - Specifies the checksum of the Add/Remove Programs config data
  45. WhichList - Specifies OLD_LIST for an app that was originally on the
  46. Add/Remove Programs list, or NEW_LIST for an app that is now on the
  47. ARP list.
  48. Return Value:
  49. TRUE - Success
  50. FALSE - Memory alloc failure (might not even be possible; see mem fns)
  51. --*/
  52. {
  53. PAPPLISTITEM appListItem;
  54. UINT itemSize;
  55. PBYTE result;
  56. UINT count;
  57. UINT u;
  58. //
  59. // Search for an existing identical list item, and update it if found
  60. //
  61. count = GrowListGetSize (List);
  62. for (u = 0 ; u < count ; u++) {
  63. appListItem = (PAPPLISTITEM) GrowListGetItem (List, u);
  64. if (StringIMatchW (DisplayString, appListItem->DisplayString)) {
  65. if (appListItem->Checksum == Checksum) {
  66. break;
  67. }
  68. }
  69. }
  70. if (u < count) {
  71. if (WhichList == OLD_LIST) {
  72. appListItem->OldReferences += 1;
  73. appListItem->InOldList = TRUE;
  74. } else {
  75. appListItem->NewReferences += 1;
  76. appListItem->InNewList = TRUE;
  77. }
  78. return TRUE;
  79. }
  80. //
  81. // This item is not on the list; add it now. First construct a structure
  82. // in a temporary buffer, then put it in the list.
  83. //
  84. itemSize = SizeOfStringW (DisplayString) + sizeof (APPLISTITEM);
  85. appListItem = (PAPPLISTITEM) MemAllocZeroed (itemSize);
  86. if (!appListItem) {
  87. return FALSE;
  88. }
  89. appListItem->Checksum = Checksum;
  90. if (WhichList == OLD_LIST) {
  91. appListItem->OldReferences = 1;
  92. appListItem->InOldList = TRUE;
  93. } else {
  94. appListItem->NewReferences = 1;
  95. appListItem->InNewList = TRUE;
  96. }
  97. StringCopyW (appListItem->DisplayString, DisplayString);
  98. result = GrowListAppend (List, (PBYTE) appListItem, itemSize);
  99. FreeMem (appListItem);
  100. return result != NULL;
  101. }
  102. VOID
  103. pIdentifyDuplicates (
  104. IN OUT PGROWLIST List
  105. )
  106. /*++
  107. Routine Description:
  108. pIdentifyDuplicates scans the apps in the specified list, and merges them so
  109. that duplicates are ignored. Duplicates are determined by comparing the
  110. application title name only. When they are found, the flags are merged into
  111. the first instance.
  112. When merging the first instance with a duplicate, the following combinations
  113. are possible:
  114. unchanged = InOldList && InNewList && !ChangedFlag
  115. new = !InOldList && InNewList (force ChangedFlag=TRUE)
  116. removed = InOldList && !InNewList (force ChangedFlag=TRUE)
  117. changed = InOldList && InNewList && ChangedFlag
  118. | Dup Inst
  119. 1st Inst | unchanged | new | removed | changed
  120. -----------------------------------------------------------------------
  121. unchanged | UNCHANGED | changed | changed | changed
  122. -----------------------------------------------------------------------
  123. new | changed | NEW | changed | changed
  124. -----------------------------------------------------------------------
  125. removed | changed | changed | REMOVED | changed
  126. -----------------------------------------------------------------------
  127. changed | changed | changed | changed | CHANGED
  128. -----------------------------------------------------------------------
  129. Any time there is a conflict between two identically named entries, we have
  130. to assume "changed" because we cannot tell exactly what happened.
  131. Arguments:
  132. List - Specifies the list of apps. Receives updated flags.
  133. Return Value:
  134. None.
  135. --*/
  136. {
  137. UINT count;
  138. UINT u;
  139. UINT v;
  140. PAPPLISTITEM appListItem;
  141. PAPPLISTITEM lookAheadItem;
  142. count = GrowListGetSize (List);
  143. for (u = 0 ; u < count ; u++) {
  144. appListItem = (PAPPLISTITEM) GrowListGetItem (List, u);
  145. if (appListItem->Duplicate) {
  146. continue;
  147. }
  148. if (appListItem->InOldList != appListItem->InNewList) {
  149. appListItem->ChangedFlag = TRUE;
  150. }
  151. for (v = u + 1 ; v < count ; v++) {
  152. lookAheadItem = (PAPPLISTITEM) GrowListGetItem (List, v);
  153. if (lookAheadItem->Duplicate) {
  154. continue;
  155. }
  156. if (StringIMatchW (appListItem->DisplayString, lookAheadItem->DisplayString)) {
  157. lookAheadItem->Duplicate = TRUE;
  158. appListItem->InOldList |= lookAheadItem->InOldList;
  159. appListItem->InNewList |= lookAheadItem->InNewList;
  160. appListItem->ChangedFlag |= lookAheadItem->ChangedFlag;
  161. if (lookAheadItem->InOldList != lookAheadItem->InNewList) {
  162. appListItem->ChangedFlag = TRUE;
  163. }
  164. }
  165. }
  166. }
  167. }
  168. INT_PTR
  169. CALLBACK
  170. pDisplayProgramsProc (
  171. IN HWND hwndDlg,
  172. IN UINT uMsg,
  173. IN WPARAM wParam,
  174. IN LPARAM lParam
  175. )
  176. {
  177. PCWSTR text;
  178. switch (uMsg) {
  179. case WM_INITDIALOG:
  180. text = (PCWSTR) lParam;
  181. MYASSERT (text);
  182. SetDlgItemTextW (hwndDlg, IDC_EDIT1, text);
  183. return TRUE;
  184. case WM_COMMAND:
  185. switch (LOWORD (wParam)) {
  186. case IDOK:
  187. EndDialog (hwndDlg, IDOK);
  188. break;
  189. case IDCANCEL:
  190. EndDialog (hwndDlg, IDCANCEL);
  191. break;
  192. }
  193. }
  194. return FALSE;
  195. }
  196. BOOL
  197. pDisplayProgramsDlg (
  198. IN HWND UiParent,
  199. IN PCWSTR ReportText
  200. )
  201. {
  202. INT_PTR result;
  203. result = DialogBoxParam (
  204. g_hInst,
  205. MAKEINTRESOURCE(IDD_APP_CHANGES),
  206. UiParent,
  207. pDisplayProgramsProc,
  208. (LPARAM) ReportText
  209. );
  210. return result == IDOK;
  211. }
  212. BOOL
  213. pProvideAppInstallAlert (
  214. IN HWND UiParent
  215. )
  216. /*++
  217. Routine Description:
  218. pProvideAppInstallAlert generates a dialog whenever the Add/Remove Programs
  219. list is different from what exists at the time of the upgrade. The user has
  220. the ability to quit uninstall.
  221. Arguments:
  222. UiParent - Specifies the HWND of the parent window, typically the desktop
  223. Return Value:
  224. TRUE to continue with uninstall
  225. FALSE to quit uninstall
  226. --*/
  227. {
  228. GROWLIST appList = GROWLIST_INIT;
  229. GROWBUFFER installedApps = GROWBUF_INIT;
  230. GROWBUFFER newBuf = GROWBUF_INIT;
  231. GROWBUFFER changedBuf = GROWBUF_INIT;
  232. GROWBUFFER delBuf = GROWBUF_INIT;
  233. BOOL result = FALSE;
  234. GROWBUFFER completeText = GROWBUF_INIT;
  235. WCHAR titleBuffer[256];
  236. PINSTALLEDAPPW installedAppList;
  237. UINT appCount;
  238. UINT count;
  239. UINT u;
  240. PAPPLISTITEM appListItem;
  241. HKEY key = NULL;
  242. PBYTE data;
  243. PCWSTR nextStr;
  244. ULONGLONG *ullPtr;
  245. BOOL failed;
  246. UINT size;
  247. PBYTE endOfLastString;
  248. //
  249. // Provide an alert whenever entries in Add/Remove Programs have changed.
  250. // Get the original list from the registry. Then compare that list to what
  251. // is presently installed.
  252. //
  253. __try {
  254. //
  255. // Add the apps recorded in the registry
  256. //
  257. key = OpenRegKeyStr (S_REGKEY_WIN_SETUP);
  258. if (!key) {
  259. DEBUGMSG ((DBG_ERROR, "Can't open %s", S_REGKEY_WIN_SETUP));
  260. __leave; // fail uninstall; this should never happen
  261. }
  262. if (!GetRegValueTypeAndSize (key, S_REG_KEY_UNDO_APP_LIST, NULL, &size)) {
  263. DEBUGMSG ((DBG_ERROR, "Can't query app list in %s", S_REGKEY_WIN_SETUP));
  264. result = TRUE;
  265. __leave; // continue with uninstall skipping the app alert
  266. } else {
  267. data = GetRegValueBinary (key, S_REG_KEY_UNDO_APP_LIST);
  268. if (!data) {
  269. result = TRUE;
  270. __leave; // continue with uninstall skipping the app alert
  271. }
  272. //
  273. // Compute the address of the first byte beyond the nul terminator for
  274. // the last printable display name string, so we can protect ourselves
  275. // from bad registry data.
  276. //
  277. endOfLastString = data + size - sizeof (ULONGLONG) - sizeof (WCHAR);
  278. }
  279. __try {
  280. //
  281. // Read in the app list stored in our registry blob
  282. //
  283. failed = FALSE;
  284. nextStr = (PCWSTR) data;
  285. while (*nextStr) {
  286. // this might throw an exception:
  287. ullPtr = (ULONGLONG *) (GetEndOfStringW (nextStr) + 1);
  288. //
  289. // ensure the checksum pointer is not beyond what we expect to be
  290. // the first byte past the last non-empty display name string
  291. //
  292. if ((PBYTE) ullPtr > endOfLastString) {
  293. failed = TRUE;
  294. break;
  295. }
  296. DEBUGMSGW ((DBG_NAUSEA, "Original app: %s", nextStr));
  297. pAppendAppToGrowList (
  298. &appList,
  299. nextStr,
  300. *ullPtr,
  301. OLD_LIST
  302. );
  303. nextStr = (PCWSTR) (ullPtr + 1);
  304. }
  305. }
  306. __except (TRUE) {
  307. failed = TRUE;
  308. }
  309. FreeMem (data);
  310. if (failed) {
  311. DEBUGMSG ((DBG_ERROR, "App key in %s is invalid", S_REGKEY_WIN_SETUP));
  312. result = TRUE;
  313. __leave; // continue with uninstall skipping the app alert
  314. }
  315. //
  316. // Add all the *current* apps to the list
  317. //
  318. CoInitialize (NULL);
  319. installedAppList = GetInstalledAppsW (&installedApps, &appCount);
  320. if (installedAppList) {
  321. for (u = 0 ; u < appCount ; u++) {
  322. DEBUGMSGW ((DBG_NAUSEA, "Identified %s", installedAppList->DisplayName));
  323. pAppendAppToGrowList (
  324. &appList,
  325. installedAppList->DisplayName,
  326. installedAppList->Checksum,
  327. NEW_LIST
  328. );
  329. installedAppList++;
  330. }
  331. } else {
  332. result = TRUE;
  333. __leave; // continue with uninstall skipping the app alert
  334. }
  335. //
  336. // Compute the overlap of the original and current apps
  337. //
  338. pIdentifyDuplicates (&appList);
  339. //
  340. // Produce a formatted list for each of the three possible cases
  341. // (new, remove, change)
  342. //
  343. count = GrowListGetSize (&appList);
  344. for (u = 0 ; u < count ; u++) {
  345. appListItem = (PAPPLISTITEM) GrowListGetItem (&appList, u);
  346. if (appListItem->Duplicate) {
  347. continue;
  348. }
  349. DEBUGMSGW ((DBG_NAUSEA, "Processing %s", appListItem->DisplayString));
  350. if (appListItem->InOldList && appListItem->InNewList) {
  351. if (appListItem->ChangedFlag ||
  352. (appListItem->OldReferences != appListItem->NewReferences)
  353. ) {
  354. DEBUGMSG_IF ((
  355. appListItem->ChangedFlag,
  356. DBG_VERBOSE,
  357. "%ws has change flag",
  358. appListItem->DisplayString
  359. ));
  360. DEBUGMSG_IF ((
  361. appListItem->OldReferences != appListItem->NewReferences,
  362. DBG_VERBOSE,
  363. "%ws has different ref count (old=%u vs new=%u)",
  364. appListItem->DisplayString,
  365. appListItem->OldReferences,
  366. appListItem->NewReferences
  367. ));
  368. GrowBufAppendStringW (&changedBuf, L" ");
  369. GrowBufAppendStringW (&changedBuf, appListItem->DisplayString);
  370. GrowBufAppendStringW (&changedBuf, L"\r\n");
  371. } else {
  372. DEBUGMSG ((DBG_VERBOSE, "%ws has not changed", appListItem->DisplayString));
  373. }
  374. } else if (appListItem->InOldList) {
  375. DEBUGMSG ((DBG_VERBOSE, "%ws was removed", appListItem->DisplayString));
  376. GrowBufAppendStringW (&delBuf, L" ");
  377. GrowBufAppendStringW (&delBuf, appListItem->DisplayString);
  378. GrowBufAppendStringW (&delBuf, L"\r\n");
  379. } else if (appListItem->InNewList) {
  380. DEBUGMSG ((DBG_VERBOSE, "%ws was added", appListItem->DisplayString));
  381. GrowBufAppendStringW (&newBuf, L" ");
  382. GrowBufAppendStringW (&newBuf, appListItem->DisplayString);
  383. GrowBufAppendStringW (&newBuf, L"\r\n");
  384. } else {
  385. MYASSERT (FALSE);
  386. }
  387. }
  388. //
  389. // Build the report text in a single buffer
  390. //
  391. if (newBuf.End) {
  392. //
  393. // Append software that is newly installed
  394. //
  395. __try {
  396. if (!LoadStringW (g_hInst, IDS_NEW_PROGRAMS, titleBuffer, ARRAYSIZE(titleBuffer))) {
  397. DEBUGMSG ((DBG_ERROR, "Can't load New Programs heading text"));
  398. __leave;
  399. }
  400. GrowBufAppendStringW (&completeText, titleBuffer);
  401. GrowBufAppendStringW (&completeText, L"\r\n");
  402. GrowBufAppendStringW (&completeText, (PCWSTR) newBuf.Buf);
  403. }
  404. __finally {
  405. }
  406. }
  407. if (delBuf.End) {
  408. //
  409. // Append software that was removed
  410. //
  411. __try {
  412. if (!LoadStringW (g_hInst, IDS_DELETED_PROGRAMS, titleBuffer, ARRAYSIZE(titleBuffer))) {
  413. DEBUGMSG ((DBG_ERROR, "Can't load Deleted Programs heading text"));
  414. __leave;
  415. }
  416. if (completeText.End) {
  417. GrowBufAppendStringW (&completeText, L"\r\n");
  418. }
  419. GrowBufAppendStringW (&completeText, titleBuffer);
  420. GrowBufAppendStringW (&completeText, L"\r\n");
  421. GrowBufAppendStringW (&completeText, (PCWSTR) delBuf.Buf);
  422. }
  423. __finally {
  424. }
  425. }
  426. if (changedBuf.End) {
  427. //
  428. // Append software that was altered
  429. //
  430. __try {
  431. if (!LoadStringW (g_hInst, IDS_CHANGED_PROGRAMS, titleBuffer, ARRAYSIZE(titleBuffer))) {
  432. DEBUGMSG ((DBG_ERROR, "Can't load Changed Programs heading text"));
  433. __leave;
  434. }
  435. if (completeText.End) {
  436. GrowBufAppendStringW (&completeText, L"\r\n");
  437. }
  438. GrowBufAppendStringW (&completeText, titleBuffer);
  439. GrowBufAppendStringW (&completeText, L"\r\n");
  440. GrowBufAppendStringW (&completeText, (PCWSTR) changedBuf.Buf);
  441. }
  442. __finally {
  443. }
  444. }
  445. //
  446. // Display UI
  447. //
  448. if (completeText.End) {
  449. result = pDisplayProgramsDlg (UiParent, (PCWSTR) completeText.Buf);
  450. } else {
  451. DEBUGMSG ((DBG_VERBOSE, "No app conflicts; continuing without UI alert"));
  452. result = TRUE;
  453. }
  454. }
  455. __finally {
  456. //
  457. // Done
  458. //
  459. FreeGrowBuffer (&newBuf);
  460. FreeGrowBuffer (&changedBuf);
  461. FreeGrowBuffer (&delBuf);
  462. FreeGrowBuffer (&completeText);
  463. FreeGrowBuffer (&installedApps);
  464. FreeGrowList (&appList);
  465. CloseRegKey (key);
  466. }
  467. return result;
  468. }
  469. BOOL
  470. ProvideUiAlerts (
  471. IN HWND ParentWindow
  472. )
  473. /*++
  474. Routine Description:
  475. ProvideUiAlerts executes the functions that produce UI after the user has
  476. chosen to uninstall the current operating system. The goal is to warn the
  477. user about problems known to exist after the uninstall is complete.
  478. This function is called before any changes are made to the system.
  479. Arguments:
  480. ParentWindow - Specifies the HWND to the parent for the UI, normally the
  481. desktop window
  482. Return Value:
  483. TRUE - Continue with uninstall
  484. FALSE - Quit uninstall
  485. --*/
  486. {
  487. DeferredInit();
  488. //
  489. // Add other UI alerts here
  490. //
  491. return pProvideAppInstallAlert (ParentWindow);
  492. }