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.

598 lines
16 KiB

  1. /*
  2. * addrm - Dialog box property sheet for "Add/Remove"
  3. */
  4. #include "tweakui.h"
  5. #pragma BEGIN_CONST_DATA
  6. const static DWORD CODESEG rgdwHelp[] = {
  7. IDC_UNINSTALL, IDH_UNINSTALL,
  8. IDC_UNINSTALLTEXT, IDH_UNINSTALL,
  9. IDC_UNINSTALLNEW, IDH_UNINSTALLNEW,
  10. IDC_LVDELETE, IDH_UNINSTALLDELETE,
  11. IDC_UNINSTALLEDIT, IDH_UNINSTALLEDIT,
  12. 0, 0,
  13. };
  14. #pragma END_CONST_DATA
  15. typedef unsigned char ARIFL; /* Random flags */
  16. #define ariflDelPending 2 /* Delete this on apply */
  17. #define ariflEdited 4 /* Rewrite this key */
  18. #define ctchKeyMac 63 /* Maximum length supported by shell32 */
  19. typedef struct ARI { /* ari - add/remove info */
  20. ARIFL arifl;
  21. TCH tszKey[MAX_PATH];
  22. TCH tszCmd[MAX_PATH];
  23. } ARI, *PARI;
  24. #define iariPlvi(plvi) ((UINT)(plvi)->lParam)
  25. #define pariIari(iari) (&padii->pari[iari])
  26. #define pariPlvi(plvi) pariIari(iariPlvi(plvi))
  27. typedef struct ADII {
  28. BOOL fDamaged;
  29. Declare_Gxa(ARI, ari);
  30. } ADII;
  31. ADII adii;
  32. #define padii (&adii)
  33. /*****************************************************************************
  34. *
  35. * AddRm_AddKey
  36. *
  37. * Add an add/remove entry. We use a listview control instead of a listbox,
  38. * because we need to be able to process right-clicks in order to display a
  39. * context menu.
  40. *
  41. * Returns the resulting item number.
  42. *
  43. *****************************************************************************/
  44. int PASCAL
  45. AddRm_AddKey(HWND hwnd, LPCTSTR ptszDesc)
  46. {
  47. return LV_AddItem(hwnd, padii->cari++, ptszDesc, -1, -1);
  48. }
  49. /*****************************************************************************
  50. *
  51. * AddRm_OnInitDialog
  52. *
  53. * Create and fill the Add/Remove list box.
  54. *
  55. *****************************************************************************/
  56. BOOL PASCAL
  57. AddRm_OnInitDialog(HWND hwnd)
  58. {
  59. padii->fDamaged = FALSE;
  60. if (Misc_InitPgxa(&padii->gxa, cbX(ARI))) {
  61. HKEY hk;
  62. if (_RegOpenKey(g_hkLMSMWCV, c_tszUninstall, &hk) == 0) {
  63. int ihk;
  64. PARI pari;
  65. for (ihk = 0;
  66. (pari = (PARI)Misc_AllocPx(&padii->gxa)) &&
  67. (RegEnumKey(hk, ihk, pari->tszKey, cbX(pari->tszKey)) == 0);
  68. ihk++) {
  69. if (pari->tszKey[0]) { /* Don't want default key */
  70. TCH tszDesc[MAX_PATH];
  71. if (GetRegStr(hk, pari->tszKey, c_tszDisplayName,
  72. tszDesc, cbX(tszDesc)) &&
  73. GetRegStr(hk, pari->tszKey, c_tszUninstallString,
  74. pari->tszCmd, cbX(pari->tszCmd))) {
  75. pari->arifl = 0;
  76. AddRm_AddKey(hwnd, tszDesc);
  77. if (lstrlen(pari->tszKey) > ctchKeyMac) {
  78. padii->fDamaged = TRUE;
  79. }
  80. }
  81. }
  82. }
  83. RegCloseKey(hk);
  84. }
  85. return 1;
  86. } else {
  87. return 0;
  88. }
  89. }
  90. /*****************************************************************************
  91. *
  92. * AddRm_Dirtify
  93. *
  94. * Mark an entry as dirty. Used when somebody does an in-place edit
  95. * of an entry.
  96. *
  97. *****************************************************************************/
  98. void PASCAL
  99. AddRm_Dirtify(LPARAM iari)
  100. {
  101. pariIari(iari)->arifl |= ariflEdited;
  102. }
  103. /*****************************************************************************
  104. *
  105. * DIGRESSION
  106. *
  107. * The Edit dialog box.
  108. *
  109. *****************************************************************************/
  110. typedef struct AREI { /* Add/Remove Edit Info */
  111. HWND hwnd; /* List view */
  112. int iItem; /* Item number being edited (-1 if add) */
  113. } AREI, *PAREI;
  114. /*****************************************************************************
  115. *
  116. * AddRm_Edit_IsDlgItemPresent
  117. *
  118. * Leading and trailing spaces are ignored.
  119. *
  120. *****************************************************************************/
  121. BOOL PASCAL
  122. AddRm_Edit_IsDlgItemPresent(HWND hdlg, int id)
  123. {
  124. TCH tsz[MAX_PATH];
  125. GetDlgItemText(hdlg, id, tsz, cA(tsz));
  126. return Misc_Trim(tsz)[0];
  127. }
  128. /*****************************************************************************
  129. *
  130. * AddRm_Edit_OnCommand_OnEditChange
  131. *
  132. * Enable/disable the OK button based on whether the texts are present.
  133. *
  134. *****************************************************************************/
  135. void PASCAL
  136. AddRm_Edit_OnCommand_OnEditChange(HWND hdlg)
  137. {
  138. EnableDlgItem(hdlg, IDOK,
  139. AddRm_Edit_IsDlgItemPresent(hdlg, IDC_UNINSTALLDESC) &&
  140. AddRm_Edit_IsDlgItemPresent(hdlg, IDC_UNINSTALLCMD));
  141. }
  142. /*****************************************************************************
  143. *
  144. * AddRm_Edit_OnInitDialog
  145. *
  146. * Fill in the fields with stuff.
  147. *
  148. *****************************************************************************/
  149. void PASCAL
  150. AddRm_Edit_OnInitDialog(HWND hdlg, PAREI parei)
  151. {
  152. SetWindowLongPtr(hdlg, DWLP_USER, (LPARAM)parei);
  153. if (parei->iItem != -1) {
  154. LV_ITEM lvi;
  155. TCH tszDesc[MAX_PATH];
  156. lvi.pszText = tszDesc;
  157. lvi.cchTextMax = cA(tszDesc);
  158. Misc_LV_GetItemInfo(parei->hwnd, &lvi, parei->iItem,
  159. LVIF_PARAM | LVIF_TEXT);
  160. SetDlgItemTextLimit(hdlg, IDC_UNINSTALLDESC, lvi.pszText, MAX_PATH);
  161. SetDlgItemTextLimit(hdlg, IDC_UNINSTALLCMD,
  162. pariPlvi(&lvi)->tszCmd,
  163. cA(pariPlvi(&lvi)->tszCmd));
  164. }
  165. AddRm_Edit_OnCommand_OnEditChange(hdlg);
  166. }
  167. /*****************************************************************************
  168. *
  169. * AddRm_Edit_OnOk
  170. *
  171. * Save the information back out.
  172. *
  173. *****************************************************************************/
  174. void PASCAL
  175. AddRm_Edit_OnOk(HWND hdlg)
  176. {
  177. PAREI parei = (PAREI)GetWindowLongPtr(hdlg, DWLP_USER);
  178. LV_ITEM lvi;
  179. TCH tszDesc[MAX_PATH];
  180. /*
  181. * Need to get the description early, so that the new entry sorts
  182. * into the right place.
  183. */
  184. lvi.pszText = tszDesc;
  185. GetDlgItemText(hdlg, IDC_UNINSTALLDESC, lvi.pszText, cA(tszDesc));
  186. if (parei->iItem == -1) {
  187. PARI pari = (PARI)Misc_AllocPx(&padii->gxa);
  188. if (pari) {
  189. pari->arifl = 0;
  190. pari->tszKey[0] = TEXT('\0');
  191. parei->iItem = AddRm_AddKey(parei->hwnd, lvi.pszText);
  192. Misc_LV_SetCurSel(parei->hwnd, parei->iItem);
  193. } else {
  194. goto failed;
  195. }
  196. }
  197. lvi.iItem = parei->iItem;
  198. Misc_LV_GetItemInfo(parei->hwnd, &lvi, parei->iItem, LVIF_PARAM);
  199. pariPlvi(&lvi)->arifl |= ariflEdited;
  200. GetDlgItemText(hdlg, IDC_UNINSTALLCMD,
  201. pariPlvi(&lvi)->tszCmd, cA(pariPlvi(&lvi)->tszCmd));
  202. lvi.mask ^= LVIF_TEXT ^ LVIF_PARAM;
  203. ListView_SetItem(parei->hwnd, &lvi);
  204. Common_SetDirty(GetParent(parei->hwnd));
  205. failed:;
  206. }
  207. /*****************************************************************************
  208. *
  209. * AddRm_Edit_OnCommand
  210. *
  211. *****************************************************************************/
  212. void PASCAL
  213. AddRm_Edit_OnCommand(HWND hdlg, int id, UINT codeNotify)
  214. {
  215. switch (id) {
  216. case IDCANCEL:
  217. EndDialog(hdlg, 0); break;
  218. case IDOK:
  219. AddRm_Edit_OnOk(hdlg);
  220. EndDialog(hdlg, 0);
  221. break;
  222. case IDC_UNINSTALLDESC:
  223. case IDC_UNINSTALLCMD:
  224. if (codeNotify == EN_CHANGE) AddRm_Edit_OnCommand_OnEditChange(hdlg);
  225. break;
  226. }
  227. }
  228. /*****************************************************************************
  229. *
  230. * AddRm_Edit_DlgProc
  231. *
  232. * Dialog procedure.
  233. *
  234. *****************************************************************************/
  235. #pragma BEGIN_CONST_DATA
  236. const static DWORD CODESEG rgdwHelpEdit[] = {
  237. IDC_UNINSTALLDESCTEXT, IDH_UNINSTALLEDITDESC,
  238. IDC_UNINSTALLCMDTEXT, IDH_UNINSTALLEDITCOMMAND,
  239. 0, 0,
  240. };
  241. #pragma END_CONST_DATA
  242. /*
  243. * The HANDLE_WM_* macros weren't designed to be used from a dialog
  244. * proc, so we need to handle the messages manually. (But carefully.)
  245. */
  246. INT_PTR EXPORT
  247. AddRm_Edit_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
  248. {
  249. switch (wm) {
  250. case WM_INITDIALOG: AddRm_Edit_OnInitDialog(hdlg, (PAREI)lParam); break;
  251. case WM_COMMAND:
  252. AddRm_Edit_OnCommand(hdlg,
  253. (int)GET_WM_COMMAND_ID(wParam, lParam),
  254. (UINT)GET_WM_COMMAND_CMD(wParam, lParam));
  255. break;
  256. case WM_CONTEXTMENU: Common_OnContextMenu(wParam, &rgdwHelpEdit[0]); break;
  257. default: return 0; /* Unhandled */
  258. }
  259. return 1; /* Handled */
  260. }
  261. /*****************************************************************************
  262. *
  263. * END DIGRESSION
  264. *
  265. *****************************************************************************/
  266. /*****************************************************************************
  267. *
  268. * AddRm_OnEdit
  269. *
  270. *****************************************************************************/
  271. void PASCAL
  272. AddRm_OnEdit(HWND hwnd, int iItem)
  273. {
  274. AREI arei = { hwnd, iItem };
  275. DialogBoxParam(hinstCur, MAKEINTRESOURCE(IDD_UNINSTALLEDIT), hwnd,
  276. AddRm_Edit_DlgProc, (LPARAM)&arei);
  277. }
  278. /*****************************************************************************
  279. *
  280. * AddRm_OnNew
  281. *
  282. *****************************************************************************/
  283. #define AddRm_OnNew(hdlg) AddRm_OnEdit(GetDlgItem(hdlg, IDC_UNINSTALL), -1)
  284. /*****************************************************************************
  285. *
  286. * AddRm_OnDelete
  287. *
  288. * Mark it for deletion, but don't actually nuke it until later.
  289. *
  290. *****************************************************************************/
  291. void PASCAL
  292. AddRm_OnDelete(HWND hwnd, int iItem)
  293. {
  294. LV_ITEM lvi;
  295. TCH tszDesc[MAX_PATH];
  296. lvi.pszText = tszDesc;
  297. lvi.cchTextMax = cA(tszDesc);
  298. Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_PARAM | LVIF_TEXT);
  299. if (MessageBoxId(GetParent(hwnd), IDS_ADDRMWARN, lvi.pszText,
  300. MB_YESNO | MB_DEFBUTTON2) == IDYES) {
  301. pariPlvi(&lvi)->arifl |= ariflDelPending;
  302. ListView_DeleteItem(hwnd, iItem);
  303. Misc_LV_EnsureSel(hwnd, iItem);
  304. Common_SetDirty(GetParent(hwnd));
  305. }
  306. }
  307. /*****************************************************************************
  308. *
  309. * AddRm_CreateUniqueKeyName
  310. *
  311. * Find a name that doesn't yet exist.
  312. *
  313. * Search numerically until something works, or a weird error occurs.
  314. *
  315. #if 0
  316. * To avoid O(n^2) behavior, we start with the number of keys.
  317. #endif
  318. *
  319. *****************************************************************************/
  320. BOOL PASCAL
  321. AddRm_CreateUniqueKeyName(HKEY hkUninst, LPTSTR ptsz)
  322. {
  323. BOOL fRc;
  324. int i;
  325. for (i = 0; ; i++) {
  326. LONG cb;
  327. wsprintf(ptsz, c_tszPercentU, i);
  328. cb = 0;
  329. switch (RegQueryValue(hkUninst, ptsz, 0, &cb)) {
  330. case ERROR_SUCCESS: break;
  331. case ERROR_FILE_NOT_FOUND: fRc = 1; goto done;
  332. default: fRc = 0; goto done; /* Unknown error */
  333. }
  334. }
  335. done:;
  336. return fRc;
  337. }
  338. /*****************************************************************************
  339. *
  340. * AddRm_OnApply_DoDeletes
  341. *
  342. * Delete the keys that are marked as "delete pending".
  343. *
  344. * pari->tszKey[0] is TEXT('\0') if the key was never in the registry.
  345. * (E.g., you "New" it, and then delete it.)
  346. *
  347. *****************************************************************************/
  348. void PASCAL
  349. AddRm_OnApply_DoDeletes(HKEY hkUninst)
  350. {
  351. int iari;
  352. for (iari = 0; iari < padii->cari; iari++) {
  353. if (pariIari(iari)->arifl & ariflDelPending) {
  354. if (pariIari(iari)->tszKey[0]) {
  355. RegDeleteTree(hkUninst, pariIari(iari)->tszKey);
  356. }
  357. }
  358. }
  359. }
  360. /*****************************************************************************
  361. *
  362. * AddRm_OnApply_DoEdits
  363. *
  364. * Apply all the edits.
  365. *
  366. *****************************************************************************/
  367. void PASCAL
  368. AddRm_OnApply_DoEdits(HWND hdlg, HKEY hkUninst)
  369. {
  370. HWND hwnd = GetDlgItem(hdlg, IDC_UNINSTALL);
  371. int cItems = ListView_GetItemCount(hwnd);
  372. LV_ITEM lvi;
  373. for (lvi.iItem = 0; lvi.iItem < cItems; lvi.iItem++) {
  374. TCH tsz[MAX_PATH];
  375. PARI pari;
  376. lvi.pszText = tsz;
  377. lvi.cchTextMax = cA(tsz);
  378. Misc_LV_GetItemInfo(hwnd, &lvi, lvi.iItem, LVIF_PARAM | LVIF_TEXT);
  379. pari = pariPlvi(&lvi);
  380. if (pari->arifl & ariflEdited) {
  381. HKEY hk;
  382. if (pari->tszKey[0] == TEXT('\0')) {
  383. if (AddRm_CreateUniqueKeyName(hkUninst, pari->tszKey)) {
  384. } else {
  385. break; /* Error! */
  386. }
  387. }
  388. if (RegCreateKey(hkUninst, pari->tszKey, &hk) == 0) {
  389. RegSetValuePtsz(hk, c_tszDisplayName, tsz);
  390. RegSetValuePtsz(hk, c_tszUninstallString, pari->tszCmd);
  391. RegCloseKey(hk);
  392. }
  393. }
  394. }
  395. }
  396. /*****************************************************************************
  397. *
  398. * AddRm_OnApply
  399. *
  400. *****************************************************************************/
  401. void PASCAL
  402. AddRm_OnApply(HWND hdlg)
  403. {
  404. HKEY hkUninst;
  405. if (RegCreateKey(g_hkLMSMWCV, c_tszUninstall, &hkUninst) == 0) {
  406. AddRm_OnApply_DoDeletes(hkUninst);
  407. AddRm_OnApply_DoEdits(hdlg, hkUninst);
  408. RegCloseKey(hkUninst);
  409. }
  410. }
  411. /*****************************************************************************
  412. *
  413. * AddRm_OnDestroy
  414. *
  415. * Free the memory we allocated.
  416. *
  417. *****************************************************************************/
  418. void PASCAL
  419. AddRm_OnDestroy(HWND hdlg)
  420. {
  421. Misc_FreePgxa(&padii->gxa);
  422. }
  423. /*****************************************************************************
  424. *
  425. * AddRm_OnRepair
  426. *
  427. * Take all the keys that are too long and shorten them.
  428. *
  429. *****************************************************************************/
  430. void PASCAL
  431. AddRm_OnRepair(HWND hdlg)
  432. {
  433. if (padii->fDamaged &&
  434. MessageBoxId(hdlg, IDS_ASKREPAIRADDRM, g_tszName, MB_YESNO)) {
  435. HKEY hkUninst;
  436. if (RegCreateKey(g_hkLMSMWCV, c_tszUninstall, &hkUninst) == 0) {
  437. int iari;
  438. for (iari = 0; iari < padii->cari; iari++) {
  439. TCHAR tszNewKey[MAX_PATH];
  440. PARI pari = pariIari(iari);
  441. if (lstrlen(pari->tszKey) > ctchKeyMac &&
  442. AddRm_CreateUniqueKeyName(hkUninst, tszNewKey) &&
  443. Misc_RenameReg(hkUninst, 0, pari->tszKey, tszNewKey)) {
  444. lstrcpy(pari->tszKey, tszNewKey);
  445. }
  446. }
  447. RegCloseKey(hkUninst);
  448. }
  449. }
  450. padii->fDamaged = FALSE; /* Ask only once */
  451. }
  452. /*****************************************************************************
  453. *
  454. * AddRm_OnCommand
  455. *
  456. *****************************************************************************/
  457. void PASCAL
  458. AddRm_OnCommand(HWND hdlg, int id, UINT codeNotify)
  459. {
  460. switch (id) {
  461. case IDC_UNINSTALLNEW:
  462. if (codeNotify == BN_CLICKED) {
  463. AddRm_OnNew(hdlg);
  464. }
  465. break;
  466. case IDC_UNINSTALLCHECK:
  467. AddRm_OnRepair(hdlg);
  468. break;
  469. }
  470. }
  471. /*****************************************************************************
  472. *
  473. * Oh yeah, we need this too.
  474. *
  475. *****************************************************************************/
  476. #pragma BEGIN_CONST_DATA
  477. LVCI lvciAddRm[] = {
  478. { IDC_UNINSTALLEDIT, AddRm_OnEdit },
  479. { IDC_LVDELETE, AddRm_OnDelete },
  480. { 0, 0 },
  481. };
  482. LVV lvvAddRm = {
  483. AddRm_OnCommand,
  484. 0, /* AddRm_OnInitContextMenu */
  485. AddRm_Dirtify,
  486. 0, /* AddRm_GetIcon */
  487. AddRm_OnInitDialog,
  488. AddRm_OnApply,
  489. AddRm_OnDestroy,
  490. 0, /* AddRm_OnSelChange */
  491. 2, /* iMenu */
  492. rgdwHelp,
  493. IDC_UNINSTALLEDIT, /* Double-click action */
  494. lvvflCanDelete | lvvflCanRename,
  495. lvciAddRm,
  496. };
  497. #pragma END_CONST_DATA
  498. /*****************************************************************************
  499. *
  500. * Our window procedure.
  501. *
  502. *****************************************************************************/
  503. INT_PTR EXPORT
  504. AddRm_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
  505. {
  506. switch (wm) {
  507. case WM_SHOWWINDOW:
  508. if (wParam) {
  509. FORWARD_WM_COMMAND(hdlg, IDC_UNINSTALLCHECK, 0, 0, PostMessage);
  510. }
  511. break;
  512. }
  513. return LV_DlgProc(&lvvAddRm, hdlg, wm, wParam, lParam);
  514. }