599 lines
16 KiB
C++
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);
|
|
}
|