WindowsXP/Source/XPSP1/NT/sdktools/tweakui/addrm.cpp
2024-08-03 16:30:48 +02:00

599 lines
16 KiB
C++

/*
* addrm - Dialog box property sheet for "Add/Remove"
*/
#include "tweakui.h"
#pragma BEGIN_CONST_DATA
const static DWORD CODESEG rgdwHelp[] = {
IDC_UNINSTALL, IDH_UNINSTALL,
IDC_UNINSTALLTEXT, IDH_UNINSTALL,
IDC_UNINSTALLNEW, IDH_UNINSTALLNEW,
IDC_LVDELETE, IDH_UNINSTALLDELETE,
IDC_UNINSTALLEDIT, IDH_UNINSTALLEDIT,
0, 0,
};
#pragma END_CONST_DATA
typedef unsigned char ARIFL; /* Random flags */
#define ariflDelPending 2 /* Delete this on apply */
#define ariflEdited 4 /* Rewrite this key */
#define ctchKeyMac 63 /* Maximum length supported by shell32 */
typedef struct ARI { /* ari - add/remove info */
ARIFL arifl;
TCH tszKey[MAX_PATH];
TCH tszCmd[MAX_PATH];
} ARI, *PARI;
#define iariPlvi(plvi) ((UINT)(plvi)->lParam)
#define pariIari(iari) (&padii->pari[iari])
#define pariPlvi(plvi) pariIari(iariPlvi(plvi))
typedef struct ADII {
BOOL fDamaged;
Declare_Gxa(ARI, ari);
} ADII;
ADII adii;
#define padii (&adii)
/*****************************************************************************
*
* AddRm_AddKey
*
* Add an add/remove entry. We use a listview control instead of a listbox,
* because we need to be able to process right-clicks in order to display a
* context menu.
*
* Returns the resulting item number.
*
*****************************************************************************/
int PASCAL
AddRm_AddKey(HWND hwnd, LPCTSTR ptszDesc)
{
return LV_AddItem(hwnd, padii->cari++, ptszDesc, -1, -1);
}
/*****************************************************************************
*
* AddRm_OnInitDialog
*
* Create and fill the Add/Remove list box.
*
*****************************************************************************/
BOOL PASCAL
AddRm_OnInitDialog(HWND hwnd)
{
padii->fDamaged = FALSE;
if (Misc_InitPgxa(&padii->gxa, cbX(ARI))) {
HKEY hk;
if (_RegOpenKey(g_hkLMSMWCV, c_tszUninstall, &hk) == 0) {
int ihk;
PARI pari;
for (ihk = 0;
(pari = (PARI)Misc_AllocPx(&padii->gxa)) &&
(RegEnumKey(hk, ihk, pari->tszKey, cbX(pari->tszKey)) == 0);
ihk++) {
if (pari->tszKey[0]) { /* Don't want default key */
TCH tszDesc[MAX_PATH];
if (GetRegStr(hk, pari->tszKey, c_tszDisplayName,
tszDesc, cbX(tszDesc)) &&
GetRegStr(hk, pari->tszKey, c_tszUninstallString,
pari->tszCmd, cbX(pari->tszCmd))) {
pari->arifl = 0;
AddRm_AddKey(hwnd, tszDesc);
if (lstrlen(pari->tszKey) > ctchKeyMac) {
padii->fDamaged = TRUE;
}
}
}
}
RegCloseKey(hk);
}
return 1;
} else {
return 0;
}
}
/*****************************************************************************
*
* AddRm_Dirtify
*
* Mark an entry as dirty. Used when somebody does an in-place edit
* of an entry.
*
*****************************************************************************/
void PASCAL
AddRm_Dirtify(LPARAM iari)
{
pariIari(iari)->arifl |= ariflEdited;
}
/*****************************************************************************
*
* DIGRESSION
*
* The Edit dialog box.
*
*****************************************************************************/
typedef struct AREI { /* Add/Remove Edit Info */
HWND hwnd; /* List view */
int iItem; /* Item number being edited (-1 if add) */
} AREI, *PAREI;
/*****************************************************************************
*
* AddRm_Edit_IsDlgItemPresent
*
* Leading and trailing spaces are ignored.
*
*****************************************************************************/
BOOL PASCAL
AddRm_Edit_IsDlgItemPresent(HWND hdlg, int id)
{
TCH tsz[MAX_PATH];
GetDlgItemText(hdlg, id, tsz, cA(tsz));
return Misc_Trim(tsz)[0];
}
/*****************************************************************************
*
* AddRm_Edit_OnCommand_OnEditChange
*
* Enable/disable the OK button based on whether the texts are present.
*
*****************************************************************************/
void PASCAL
AddRm_Edit_OnCommand_OnEditChange(HWND hdlg)
{
EnableDlgItem(hdlg, IDOK,
AddRm_Edit_IsDlgItemPresent(hdlg, IDC_UNINSTALLDESC) &&
AddRm_Edit_IsDlgItemPresent(hdlg, IDC_UNINSTALLCMD));
}
/*****************************************************************************
*
* AddRm_Edit_OnInitDialog
*
* Fill in the fields with stuff.
*
*****************************************************************************/
void PASCAL
AddRm_Edit_OnInitDialog(HWND hdlg, PAREI parei)
{
SetWindowLongPtr(hdlg, DWLP_USER, (LPARAM)parei);
if (parei->iItem != -1) {
LV_ITEM lvi;
TCH tszDesc[MAX_PATH];
lvi.pszText = tszDesc;
lvi.cchTextMax = cA(tszDesc);
Misc_LV_GetItemInfo(parei->hwnd, &lvi, parei->iItem,
LVIF_PARAM | LVIF_TEXT);
SetDlgItemTextLimit(hdlg, IDC_UNINSTALLDESC, lvi.pszText, MAX_PATH);
SetDlgItemTextLimit(hdlg, IDC_UNINSTALLCMD,
pariPlvi(&lvi)->tszCmd,
cA(pariPlvi(&lvi)->tszCmd));
}
AddRm_Edit_OnCommand_OnEditChange(hdlg);
}
/*****************************************************************************
*
* AddRm_Edit_OnOk
*
* Save the information back out.
*
*****************************************************************************/
void PASCAL
AddRm_Edit_OnOk(HWND hdlg)
{
PAREI parei = (PAREI)GetWindowLongPtr(hdlg, DWLP_USER);
LV_ITEM lvi;
TCH tszDesc[MAX_PATH];
/*
* Need to get the description early, so that the new entry sorts
* into the right place.
*/
lvi.pszText = tszDesc;
GetDlgItemText(hdlg, IDC_UNINSTALLDESC, lvi.pszText, cA(tszDesc));
if (parei->iItem == -1) {
PARI pari = (PARI)Misc_AllocPx(&padii->gxa);
if (pari) {
pari->arifl = 0;
pari->tszKey[0] = TEXT('\0');
parei->iItem = AddRm_AddKey(parei->hwnd, lvi.pszText);
Misc_LV_SetCurSel(parei->hwnd, parei->iItem);
} else {
goto failed;
}
}
lvi.iItem = parei->iItem;
Misc_LV_GetItemInfo(parei->hwnd, &lvi, parei->iItem, LVIF_PARAM);
pariPlvi(&lvi)->arifl |= ariflEdited;
GetDlgItemText(hdlg, IDC_UNINSTALLCMD,
pariPlvi(&lvi)->tszCmd, cA(pariPlvi(&lvi)->tszCmd));
lvi.mask ^= LVIF_TEXT ^ LVIF_PARAM;
ListView_SetItem(parei->hwnd, &lvi);
Common_SetDirty(GetParent(parei->hwnd));
failed:;
}
/*****************************************************************************
*
* AddRm_Edit_OnCommand
*
*****************************************************************************/
void PASCAL
AddRm_Edit_OnCommand(HWND hdlg, int id, UINT codeNotify)
{
switch (id) {
case IDCANCEL:
EndDialog(hdlg, 0); break;
case IDOK:
AddRm_Edit_OnOk(hdlg);
EndDialog(hdlg, 0);
break;
case IDC_UNINSTALLDESC:
case IDC_UNINSTALLCMD:
if (codeNotify == EN_CHANGE) AddRm_Edit_OnCommand_OnEditChange(hdlg);
break;
}
}
/*****************************************************************************
*
* AddRm_Edit_DlgProc
*
* Dialog procedure.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
const static DWORD CODESEG rgdwHelpEdit[] = {
IDC_UNINSTALLDESCTEXT, IDH_UNINSTALLEDITDESC,
IDC_UNINSTALLCMDTEXT, IDH_UNINSTALLEDITCOMMAND,
0, 0,
};
#pragma END_CONST_DATA
/*
* The HANDLE_WM_* macros weren't designed to be used from a dialog
* proc, so we need to handle the messages manually. (But carefully.)
*/
INT_PTR EXPORT
AddRm_Edit_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
switch (wm) {
case WM_INITDIALOG: AddRm_Edit_OnInitDialog(hdlg, (PAREI)lParam); break;
case WM_COMMAND:
AddRm_Edit_OnCommand(hdlg,
(int)GET_WM_COMMAND_ID(wParam, lParam),
(UINT)GET_WM_COMMAND_CMD(wParam, lParam));
break;
case WM_CONTEXTMENU: Common_OnContextMenu(wParam, &rgdwHelpEdit[0]); break;
default: return 0; /* Unhandled */
}
return 1; /* Handled */
}
/*****************************************************************************
*
* END DIGRESSION
*
*****************************************************************************/
/*****************************************************************************
*
* AddRm_OnEdit
*
*****************************************************************************/
void PASCAL
AddRm_OnEdit(HWND hwnd, int iItem)
{
AREI arei = { hwnd, iItem };
DialogBoxParam(hinstCur, MAKEINTRESOURCE(IDD_UNINSTALLEDIT), hwnd,
AddRm_Edit_DlgProc, (LPARAM)&arei);
}
/*****************************************************************************
*
* AddRm_OnNew
*
*****************************************************************************/
#define AddRm_OnNew(hdlg) AddRm_OnEdit(GetDlgItem(hdlg, IDC_UNINSTALL), -1)
/*****************************************************************************
*
* AddRm_OnDelete
*
* Mark it for deletion, but don't actually nuke it until later.
*
*****************************************************************************/
void PASCAL
AddRm_OnDelete(HWND hwnd, int iItem)
{
LV_ITEM lvi;
TCH tszDesc[MAX_PATH];
lvi.pszText = tszDesc;
lvi.cchTextMax = cA(tszDesc);
Misc_LV_GetItemInfo(hwnd, &lvi, iItem, LVIF_PARAM | LVIF_TEXT);
if (MessageBoxId(GetParent(hwnd), IDS_ADDRMWARN, lvi.pszText,
MB_YESNO | MB_DEFBUTTON2) == IDYES) {
pariPlvi(&lvi)->arifl |= ariflDelPending;
ListView_DeleteItem(hwnd, iItem);
Misc_LV_EnsureSel(hwnd, iItem);
Common_SetDirty(GetParent(hwnd));
}
}
/*****************************************************************************
*
* AddRm_CreateUniqueKeyName
*
* Find a name that doesn't yet exist.
*
* Search numerically until something works, or a weird error occurs.
*
#if 0
* To avoid O(n^2) behavior, we start with the number of keys.
#endif
*
*****************************************************************************/
BOOL PASCAL
AddRm_CreateUniqueKeyName(HKEY hkUninst, LPTSTR ptsz)
{
BOOL fRc;
int i;
for (i = 0; ; i++) {
LONG cb;
wsprintf(ptsz, c_tszPercentU, i);
cb = 0;
switch (RegQueryValue(hkUninst, ptsz, 0, &cb)) {
case ERROR_SUCCESS: break;
case ERROR_FILE_NOT_FOUND: fRc = 1; goto done;
default: fRc = 0; goto done; /* Unknown error */
}
}
done:;
return fRc;
}
/*****************************************************************************
*
* AddRm_OnApply_DoDeletes
*
* Delete the keys that are marked as "delete pending".
*
* pari->tszKey[0] is TEXT('\0') if the key was never in the registry.
* (E.g., you "New" it, and then delete it.)
*
*****************************************************************************/
void PASCAL
AddRm_OnApply_DoDeletes(HKEY hkUninst)
{
int iari;
for (iari = 0; iari < padii->cari; iari++) {
if (pariIari(iari)->arifl & ariflDelPending) {
if (pariIari(iari)->tszKey[0]) {
RegDeleteTree(hkUninst, pariIari(iari)->tszKey);
}
}
}
}
/*****************************************************************************
*
* AddRm_OnApply_DoEdits
*
* Apply all the edits.
*
*****************************************************************************/
void PASCAL
AddRm_OnApply_DoEdits(HWND hdlg, HKEY hkUninst)
{
HWND hwnd = GetDlgItem(hdlg, IDC_UNINSTALL);
int cItems = ListView_GetItemCount(hwnd);
LV_ITEM lvi;
for (lvi.iItem = 0; lvi.iItem < cItems; lvi.iItem++) {
TCH tsz[MAX_PATH];
PARI pari;
lvi.pszText = tsz;
lvi.cchTextMax = cA(tsz);
Misc_LV_GetItemInfo(hwnd, &lvi, lvi.iItem, LVIF_PARAM | LVIF_TEXT);
pari = pariPlvi(&lvi);
if (pari->arifl & ariflEdited) {
HKEY hk;
if (pari->tszKey[0] == TEXT('\0')) {
if (AddRm_CreateUniqueKeyName(hkUninst, pari->tszKey)) {
} else {
break; /* Error! */
}
}
if (RegCreateKey(hkUninst, pari->tszKey, &hk) == 0) {
RegSetValuePtsz(hk, c_tszDisplayName, tsz);
RegSetValuePtsz(hk, c_tszUninstallString, pari->tszCmd);
RegCloseKey(hk);
}
}
}
}
/*****************************************************************************
*
* AddRm_OnApply
*
*****************************************************************************/
void PASCAL
AddRm_OnApply(HWND hdlg)
{
HKEY hkUninst;
if (RegCreateKey(g_hkLMSMWCV, c_tszUninstall, &hkUninst) == 0) {
AddRm_OnApply_DoDeletes(hkUninst);
AddRm_OnApply_DoEdits(hdlg, hkUninst);
RegCloseKey(hkUninst);
}
}
/*****************************************************************************
*
* AddRm_OnDestroy
*
* Free the memory we allocated.
*
*****************************************************************************/
void PASCAL
AddRm_OnDestroy(HWND hdlg)
{
Misc_FreePgxa(&padii->gxa);
}
/*****************************************************************************
*
* AddRm_OnRepair
*
* Take all the keys that are too long and shorten them.
*
*****************************************************************************/
void PASCAL
AddRm_OnRepair(HWND hdlg)
{
if (padii->fDamaged &&
MessageBoxId(hdlg, IDS_ASKREPAIRADDRM, g_tszName, MB_YESNO)) {
HKEY hkUninst;
if (RegCreateKey(g_hkLMSMWCV, c_tszUninstall, &hkUninst) == 0) {
int iari;
for (iari = 0; iari < padii->cari; iari++) {
TCHAR tszNewKey[MAX_PATH];
PARI pari = pariIari(iari);
if (lstrlen(pari->tszKey) > ctchKeyMac &&
AddRm_CreateUniqueKeyName(hkUninst, tszNewKey) &&
Misc_RenameReg(hkUninst, 0, pari->tszKey, tszNewKey)) {
lstrcpy(pari->tszKey, tszNewKey);
}
}
RegCloseKey(hkUninst);
}
}
padii->fDamaged = FALSE; /* Ask only once */
}
/*****************************************************************************
*
* AddRm_OnCommand
*
*****************************************************************************/
void PASCAL
AddRm_OnCommand(HWND hdlg, int id, UINT codeNotify)
{
switch (id) {
case IDC_UNINSTALLNEW:
if (codeNotify == BN_CLICKED) {
AddRm_OnNew(hdlg);
}
break;
case IDC_UNINSTALLCHECK:
AddRm_OnRepair(hdlg);
break;
}
}
/*****************************************************************************
*
* Oh yeah, we need this too.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
LVCI lvciAddRm[] = {
{ IDC_UNINSTALLEDIT, AddRm_OnEdit },
{ IDC_LVDELETE, AddRm_OnDelete },
{ 0, 0 },
};
LVV lvvAddRm = {
AddRm_OnCommand,
0, /* AddRm_OnInitContextMenu */
AddRm_Dirtify,
0, /* AddRm_GetIcon */
AddRm_OnInitDialog,
AddRm_OnApply,
AddRm_OnDestroy,
0, /* AddRm_OnSelChange */
2, /* iMenu */
rgdwHelp,
IDC_UNINSTALLEDIT, /* Double-click action */
lvvflCanDelete | lvvflCanRename,
lvciAddRm,
};
#pragma END_CONST_DATA
/*****************************************************************************
*
* Our window procedure.
*
*****************************************************************************/
INT_PTR EXPORT
AddRm_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
switch (wm) {
case WM_SHOWWINDOW:
if (wParam) {
FORWARD_WM_COMMAND(hdlg, IDC_UNINSTALLCHECK, 0, 0, PostMessage);
}
break;
}
return LV_DlgProc(&lvvAddRm, hdlg, wm, wParam, lParam);
}