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.

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