mirror of
https://github.com/fadden/ciderpress.git
synced 2024-11-23 11:33:58 +00:00
4638d03d6c
In the past, CiderPress managed its own file associations. This is the feature that launches CiderPress when you double-click on a ".shk" file. The installer ran "CiderPress -install" and "-uninstall" during installation and removal to give CP a chance to establish and clean up the necessary registry entries. The code built with VS6 works fine. The code built with VS2013 fails with an access denied error. It appears there have been some access policy changes, and the older code is getting "grandfathered in". This is really something that the installer ought to be handling, though, so rather than figure out how to fix CiderPress, I'm removing the file type association code from CiderPress and letting DeployMaster handle it. This may be slightly less convenient for anyone who had reason to change type associations frequently. Modern versions of Windows have relatively easy to use control panel UIs for adjusting types, and the "advanced installation" feature of DeployMaster allows you to un-check the types that you don't want to have associated with CiderPress. (...with one minor hitch: DeployMaster 4.2.2 only shows the first 9 associations, and CiderPress has 18.) This change renders most of the registry-handling code obsolete, as well as the "-install" / "-uninstall" handling. I'm 99% sure I want to go this way, but I'm keeping things #ifdefed rather than deleted for the moment.
625 lines
19 KiB
C++
625 lines
19 KiB
C++
/*
|
|
* CiderPress
|
|
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
|
|
* See the file LICENSE for distribution terms.
|
|
*/
|
|
/*
|
|
* Windows Registry operations.
|
|
*/
|
|
#include "stdafx.h"
|
|
#ifdef CAN_UPDATE_FILE_ASSOC
|
|
#include "Registry.h"
|
|
#include "Main.h"
|
|
#include "MyApp.h"
|
|
|
|
#define kRegAppName L"CiderPress"
|
|
#define kRegExeName L"CiderPress.exe"
|
|
#define kCompanyName L"faddenSoft"
|
|
|
|
static const WCHAR kRegKeyCPKVersions[] = L"vrs";
|
|
static const WCHAR kRegKeyCPKExpire[] = L"epr";
|
|
|
|
/*
|
|
* Application path. Add two keys:
|
|
*
|
|
* (default) = FullPathName
|
|
* Full pathname of the executable file.
|
|
* Path = Path
|
|
* The $PATH that will be in effect when the program starts (but only if
|
|
* launched from the Windows explorer).
|
|
*/
|
|
static const WCHAR kAppKeyBase[] =
|
|
L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" kRegExeName;
|
|
|
|
/*
|
|
* Local settings. App stuff goes in the per-user key, registration info is
|
|
* in the per-machine key.
|
|
*/
|
|
static const WCHAR kMachineSettingsBaseKey[] =
|
|
L"HKEY_LOCAL_MACHINE\\SOFTWARE\\" kCompanyName L"\\" kRegAppName;
|
|
static const WCHAR kUserSettingsBaseKey[] =
|
|
L"HKEY_CURRENT_USER\\Software\\" kCompanyName L"\\" kRegAppName;
|
|
|
|
/*
|
|
* Set this key + ".XXX" to (Default)=AppID. This associates the file
|
|
* type with kRegAppID.
|
|
*/
|
|
//static const char* kFileExtensionBase = L"HKEY_CLASSES_ROOT";
|
|
|
|
/*
|
|
* Description of data files. Set this key + AppID to 40-char string, e.g.
|
|
* (Default)=CompanyName AppName Version DataType
|
|
*
|
|
* Can also set DefaultIcon = Pathname [,Index]
|
|
*/
|
|
//static const char* kAppIDBase = L"HKEY_CLASSES_ROOT";
|
|
|
|
/*
|
|
* Put one of these under the AppID to specify the icon for a file type.
|
|
*/
|
|
static const WCHAR kDefaultIcon[] = L"DefaultIcon";
|
|
|
|
static const WCHAR kRegKeyCPKStr[] = L"CPK";
|
|
|
|
/*
|
|
* Table of file type associations. They will appear in the UI in the same
|
|
* order that they appear here, so try to maintain alphabetical order.
|
|
*/
|
|
#define kAppIDNuFX L"CiderPress.NuFX"
|
|
#define kAppIDDiskImage L"CiderPress.DiskImage"
|
|
#define kAppIDBinaryII L"CiderPress.BinaryII"
|
|
#define kNoAssociation L"(no association)"
|
|
const MyRegistry::FileTypeAssoc MyRegistry::kFileTypeAssoc[] = {
|
|
{ L".2MG", kAppIDDiskImage },
|
|
{ L".APP", kAppIDDiskImage },
|
|
{ L".BNY", kAppIDBinaryII },
|
|
{ L".BQY", kAppIDBinaryII },
|
|
{ L".BSE", kAppIDNuFX },
|
|
{ L".BXY", kAppIDNuFX },
|
|
{ L".D13", kAppIDDiskImage },
|
|
{ L".DDD", kAppIDDiskImage },
|
|
{ L".DO", kAppIDDiskImage },
|
|
{ L".DSK", kAppIDDiskImage },
|
|
{ L".FDI", kAppIDDiskImage },
|
|
{ L".HDV", kAppIDDiskImage },
|
|
{ L".IMG", kAppIDDiskImage },
|
|
{ L".NIB", kAppIDDiskImage },
|
|
{ L".PO", kAppIDDiskImage },
|
|
{ L".SDK", kAppIDDiskImage },
|
|
{ L".SEA", kAppIDNuFX },
|
|
{ L".SHK", kAppIDNuFX },
|
|
// { L".DC", kAppIDDiskImage },
|
|
// { L".DC6", kAppIDDiskImage },
|
|
// { L".GZ", kAppIDDiskImage },
|
|
// { L".NB2", kAppIDDiskImage },
|
|
// { L".RAW", kAppIDDiskImage },
|
|
// { L".ZIP", kAppIDDiskImage },
|
|
};
|
|
|
|
static const struct {
|
|
const char* user;
|
|
const char* reg;
|
|
} gBadKeys[] = {
|
|
{ "Nimrod Bonehead", "CP1-68C069-62CC9444" },
|
|
{ "Connie Tan", "CP1-877B2C-A428FFD6" },
|
|
};
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* One-time install/uninstall
|
|
* ==========================================================================
|
|
*/
|
|
|
|
void MyRegistry::OneTimeInstall(void) const
|
|
{
|
|
/* start by stomping on our appIDs */
|
|
LOGI(" Removing appIDs");
|
|
RegDeleteKeyNT(HKEY_CLASSES_ROOT, kAppIDNuFX);
|
|
RegDeleteKeyNT(HKEY_CLASSES_ROOT, kAppIDDiskImage);
|
|
RegDeleteKeyNT(HKEY_CLASSES_ROOT, kAppIDBinaryII);
|
|
|
|
/* configure the appIDs */
|
|
FixBasicSettings();
|
|
|
|
/* configure extensions */
|
|
int i, res;
|
|
for (i = 0; i < NELEM(kFileTypeAssoc); i++) {
|
|
HKEY hExtKey;
|
|
res = RegOpenKeyEx(HKEY_CLASSES_ROOT, kFileTypeAssoc[i].ext, 0,
|
|
KEY_READ, &hExtKey);
|
|
if (res == ERROR_SUCCESS) {
|
|
LOGI(" Found existing HKCR\\'%ls', leaving alone",
|
|
kFileTypeAssoc[i].ext);
|
|
RegCloseKey(hExtKey);
|
|
} else if (res == ERROR_FILE_NOT_FOUND) {
|
|
OwnExtension(kFileTypeAssoc[i].ext, kFileTypeAssoc[i].appID);
|
|
} else {
|
|
LOGI(" Got error %ld opening HKCR\\'%ls', leaving alone",
|
|
res, kFileTypeAssoc[i].ext);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MyRegistry::OneTimeUninstall(void) const
|
|
{
|
|
/* drop any associations we hold */
|
|
int i;
|
|
for (i = 0; i < NELEM(kFileTypeAssoc); i++) {
|
|
CString ext, handler;
|
|
bool ours;
|
|
|
|
GetFileAssoc(i, &ext, &handler, &ours);
|
|
if (ours) {
|
|
DisownExtension(ext);
|
|
}
|
|
}
|
|
|
|
/* remove our appIDs */
|
|
LOGI(" Removing appIDs");
|
|
RegDeleteKeyNT(HKEY_CLASSES_ROOT, kAppIDNuFX);
|
|
RegDeleteKeyNT(HKEY_CLASSES_ROOT, kAppIDDiskImage);
|
|
RegDeleteKeyNT(HKEY_CLASSES_ROOT, kAppIDBinaryII);
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* Shareware registration logic
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/* [removed] */
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* Windows shell game
|
|
* ==========================================================================
|
|
*/
|
|
|
|
const WCHAR* MyRegistry::GetAppRegistryKey(void) const
|
|
{
|
|
return kCompanyName;
|
|
}
|
|
|
|
bool MyRegistry::IsOurAppID(const WCHAR* id) const
|
|
{
|
|
return (wcsicmp(id, kAppIDNuFX) == 0 ||
|
|
wcsicmp(id, kAppIDDiskImage) == 0 ||
|
|
wcsicmp(id, kAppIDBinaryII) == 0);
|
|
}
|
|
|
|
void MyRegistry::FixBasicSettings(void) const
|
|
{
|
|
/*
|
|
* Fix the basic registry settings, e.g. our AppID classes.
|
|
*
|
|
* We don't overwrite values that already exist. We want to hold on to the
|
|
* installer's settings, which should get whacked if the program is
|
|
* uninstalled or reinstalled. This is here for "installer-less" environments
|
|
* and to cope with registry damage.
|
|
*/
|
|
|
|
const WCHAR* exeName = gMyApp.GetExeFileName();
|
|
ASSERT(exeName != NULL && wcslen(exeName) > 0);
|
|
|
|
LOGI("Fixing any missing file type AppID entries in registry");
|
|
|
|
ConfigureAppID(kAppIDNuFX, L"NuFX Archive (CiderPress)", exeName, 1);
|
|
ConfigureAppID(kAppIDBinaryII, L"Binary II (CiderPress)", exeName, 2);
|
|
ConfigureAppID(kAppIDDiskImage, L"Disk Image (CiderPress)", exeName, 3);
|
|
}
|
|
|
|
void MyRegistry::ConfigureAppID(const WCHAR* appID, const WCHAR* descr,
|
|
const WCHAR* exeName, int iconIdx) const
|
|
{
|
|
LOGI(" ConfigureAppID '%ls' for '%ls'", appID, exeName);
|
|
|
|
HKEY hAppKey = NULL;
|
|
HKEY hIconKey = NULL;
|
|
|
|
DWORD dw;
|
|
LONG result;
|
|
if ((result = RegCreateKeyEx(HKEY_CLASSES_ROOT, appID, 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hAppKey, &dw)) == ERROR_SUCCESS)
|
|
{
|
|
ConfigureAppIDSubFields(hAppKey, descr, exeName);
|
|
|
|
if (RegCreateKeyEx(hAppKey, kDefaultIcon, 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hIconKey, &dw) == ERROR_SUCCESS)
|
|
{
|
|
DWORD type, size;
|
|
unsigned char buf[512];
|
|
long res;
|
|
|
|
size = sizeof(buf); // size in bytes
|
|
res = RegQueryValueEx(hIconKey, L"", NULL, &type, buf, &size);
|
|
if (res == ERROR_SUCCESS && size > 1) {
|
|
LOGI(" Icon for '%ls' already exists, not altering", appID);
|
|
} else {
|
|
CString iconStr;
|
|
iconStr.Format(L"%ls,%d", exeName, iconIdx);
|
|
|
|
if (RegSetValueEx(hIconKey, L"", 0, REG_SZ,
|
|
(const BYTE*)(LPCWSTR) iconStr,
|
|
wcslen(iconStr) * sizeof(WCHAR)) == ERROR_SUCCESS)
|
|
{
|
|
LOGI(" Set icon for '%ls' to '%ls'", appID,
|
|
(LPCWSTR) iconStr);
|
|
} else {
|
|
LOGW(" WARNING: unable to set DefaultIcon for '%ls' to '%ls'",
|
|
appID, (LPCWSTR) iconStr);
|
|
}
|
|
}
|
|
} else {
|
|
LOGW("WARNING: couldn't set up DefaultIcon for '%ls'", appID);
|
|
}
|
|
} else {
|
|
LOGW("WARNING: couldn't create AppID='%ls' (err=%ld)", appID, result);
|
|
}
|
|
|
|
RegCloseKey(hIconKey);
|
|
RegCloseKey(hAppKey);
|
|
}
|
|
|
|
void MyRegistry::ConfigureAppIDSubFields(HKEY hAppKey, const WCHAR* descr,
|
|
const WCHAR* exeName) const
|
|
{
|
|
HKEY hShellKey, hOpenKey, hCommandKey;
|
|
DWORD dw;
|
|
|
|
ASSERT(hAppKey != NULL);
|
|
ASSERT(descr != NULL);
|
|
ASSERT(exeName != NULL);
|
|
hShellKey = hOpenKey = hCommandKey = NULL;
|
|
|
|
if (RegSetValueEx(hAppKey, L"", 0, REG_SZ, (const BYTE*) descr,
|
|
wcslen(descr) * sizeof(WCHAR)) != ERROR_SUCCESS)
|
|
{
|
|
LOGI(" WARNING: unable to set description to '%ls'", descr);
|
|
}
|
|
|
|
if (RegCreateKeyEx(hAppKey, L"shell", 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hShellKey, &dw) == ERROR_SUCCESS)
|
|
{
|
|
if (RegCreateKeyEx(hShellKey, L"open", 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hOpenKey, &dw) == ERROR_SUCCESS)
|
|
{
|
|
if (RegCreateKeyEx(hOpenKey, L"command", 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hCommandKey, &dw) == ERROR_SUCCESS)
|
|
{
|
|
DWORD type, size;
|
|
WCHAR buf[MAX_PATH+8];
|
|
long res;
|
|
|
|
size = sizeof(buf); // size in bytes
|
|
res = RegQueryValueEx(hCommandKey, L"", NULL, &type, (LPBYTE) buf,
|
|
&size);
|
|
if (res == ERROR_SUCCESS && size > 1) {
|
|
LOGI(" Command already exists, not altering ('%ls')", buf);
|
|
} else {
|
|
CString openCmd;
|
|
|
|
openCmd.Format(L"\"%ls\" \"%%1\"", exeName);
|
|
if (RegSetValueEx(hCommandKey, L"", 0, REG_SZ,
|
|
(LPBYTE)(LPCWSTR) openCmd,
|
|
wcslen(openCmd) * sizeof(WCHAR)) == ERROR_SUCCESS)
|
|
{
|
|
LOGI(" Set command to '%ls'", (LPCWSTR) openCmd);
|
|
} else {
|
|
LOGW(" WARNING: unable to set open cmd '%ls'",
|
|
(LPCWSTR) openCmd);
|
|
}
|
|
}
|
|
RegCloseKey(hCommandKey);
|
|
}
|
|
RegCloseKey(hOpenKey);
|
|
}
|
|
RegCloseKey(hShellKey);
|
|
}
|
|
}
|
|
|
|
|
|
int MyRegistry::GetNumFileAssocs(void) const
|
|
{
|
|
return NELEM(kFileTypeAssoc);
|
|
}
|
|
|
|
void MyRegistry::GetFileAssoc(int idx, CString* pExt, CString* pHandler,
|
|
bool* pOurs) const
|
|
{
|
|
/*
|
|
* We check to see if the file extension is associated with one of our
|
|
* application ID strings. We don't bother to check whether the appID
|
|
* strings are still associated with CiderPress, since nobody should be
|
|
* messing with those.
|
|
*
|
|
* BUG: we should be checking to see what the shell actually does to
|
|
* take into account the overrides that users can set.
|
|
*/
|
|
|
|
ASSERT(idx >= 0 && idx < NELEM(kFileTypeAssoc));
|
|
long res;
|
|
|
|
*pExt = kFileTypeAssoc[idx].ext;
|
|
*pHandler = "";
|
|
|
|
CString appID;
|
|
HKEY hExtKey = NULL;
|
|
|
|
res = RegOpenKeyEx(HKEY_CLASSES_ROOT, *pExt, 0, KEY_READ, &hExtKey);
|
|
if (res == ERROR_SUCCESS) {
|
|
WCHAR buf[260];
|
|
DWORD type;
|
|
DWORD size = sizeof(buf); // size in bytes
|
|
|
|
res = RegQueryValueEx(hExtKey, L"", NULL, &type, (LPBYTE)buf, &size);
|
|
if (res == ERROR_SUCCESS) {
|
|
LOGI(" Got '%ls'", buf);
|
|
appID = buf;
|
|
|
|
if (GetAssocAppName(appID, pHandler) != 0)
|
|
*pHandler = appID;
|
|
} else {
|
|
LOGI("RegQueryValueEx failed on '%ls'", (LPCWSTR) *pExt);
|
|
}
|
|
} else {
|
|
LOGW(" RegOpenKeyEx failed on '%ls'", (LPCWSTR) *pExt);
|
|
}
|
|
|
|
*pOurs = false;
|
|
if (pHandler->IsEmpty()) {
|
|
*pHandler = kNoAssociation;
|
|
} else {
|
|
*pOurs = IsOurAppID(appID);
|
|
}
|
|
|
|
RegCloseKey(hExtKey);
|
|
}
|
|
|
|
int MyRegistry::GetAssocAppName(const CString& appID, CString* pCmd) const
|
|
{
|
|
CString keyName;
|
|
WCHAR buf[260];
|
|
HKEY hAppKey = NULL;
|
|
long res;
|
|
int result = -1;
|
|
|
|
keyName = appID + L"\\shell\\open\\command";
|
|
|
|
res = RegOpenKeyEx(HKEY_CLASSES_ROOT, keyName, 0, KEY_READ, &hAppKey);
|
|
if (res == ERROR_SUCCESS) {
|
|
DWORD type;
|
|
DWORD size = sizeof(buf); // size in bytes
|
|
|
|
res = RegQueryValueEx(hAppKey, L"", NULL, &type, (LPBYTE) buf, &size);
|
|
if (res == ERROR_SUCCESS) {
|
|
CString cmd(buf);
|
|
int pos;
|
|
|
|
/* cut it down to just the EXE name */
|
|
ReduceToToken(&cmd);
|
|
|
|
pos = cmd.ReverseFind('\\');
|
|
if (pos != -1 && pos != cmd.GetLength()-1) {
|
|
cmd = cmd.Right(cmd.GetLength() - pos -1);
|
|
}
|
|
|
|
*pCmd = cmd;
|
|
result = 0;
|
|
} else {
|
|
LOGW("Unable to open shell\\open\\command for '%ls'",
|
|
(LPCWSTR) appID);
|
|
}
|
|
} else {
|
|
CString errBuf;
|
|
GetWin32ErrorString(res, &errBuf);
|
|
|
|
LOGW("Unable to open AppID key '%ls' (%ls)",
|
|
(LPCWSTR) keyName, (LPCWSTR) errBuf);
|
|
}
|
|
|
|
RegCloseKey(hAppKey);
|
|
return result;
|
|
}
|
|
|
|
void MyRegistry::ReduceToToken(CString* pStr) const
|
|
{
|
|
WCHAR* argv[1];
|
|
int argc = 1;
|
|
WCHAR* mangle = wcsdup(*pStr);
|
|
|
|
VectorizeString(mangle, argv, &argc);
|
|
|
|
if (argc == 1)
|
|
*pStr = argv[0];
|
|
|
|
free(mangle);
|
|
}
|
|
|
|
int MyRegistry::SetFileAssoc(int idx, bool wantIt) const
|
|
{
|
|
/*
|
|
* Set the state of a file association. There are four possibilities:
|
|
*
|
|
* - We own it, we want to keep owning it: do nothing.
|
|
* - We don't own it, we want to keep not owning it: do nothing.
|
|
* - We own it, we don't want it anymore: remove ".xxx" entry.
|
|
* - We don't own it, we want to own it: remove ".xxx" entry and replace it.
|
|
*
|
|
* Returns 0 on success, nonzero on failure.
|
|
*/
|
|
|
|
const WCHAR* ext;
|
|
bool weOwnIt;
|
|
int result = 0;
|
|
|
|
ASSERT(idx >= 0 && idx < NELEM(kFileTypeAssoc));
|
|
|
|
ext = kFileTypeAssoc[idx].ext;
|
|
weOwnIt = GetAssocState(ext);
|
|
LOGI("SetFileAssoc: ext='%ls' own=%d want=%d", ext, weOwnIt, wantIt);
|
|
|
|
if (weOwnIt && !wantIt) {
|
|
/* reset it */
|
|
LOGI(" SetFileAssoc: clearing '%ls'", ext);
|
|
result = DisownExtension(ext);
|
|
} else if (!weOwnIt && wantIt) {
|
|
/* take it */
|
|
LOGI(" SetFileAssoc: taking '%ls'", ext);
|
|
result = OwnExtension(ext, kFileTypeAssoc[idx].appID);
|
|
} else {
|
|
LOGI(" SetFileAssoc: do nothing with '%ls'", ext);
|
|
/* do nothing */
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool MyRegistry::GetAssocState(const WCHAR* ext) const
|
|
{
|
|
WCHAR buf[260];
|
|
HKEY hExtKey = NULL;
|
|
int res;
|
|
bool result = false;
|
|
|
|
res = RegOpenKeyEx(HKEY_CLASSES_ROOT, ext, 0, KEY_READ, &hExtKey);
|
|
if (res == ERROR_SUCCESS) {
|
|
DWORD type;
|
|
DWORD size = sizeof(buf); // size in bytes
|
|
res = RegQueryValueEx(hExtKey, L"", NULL, &type, (LPBYTE) buf, &size);
|
|
if (res == ERROR_SUCCESS && type == REG_SZ) {
|
|
/* compare it to known appID values */
|
|
LOGI(" Found '%ls', testing '%ls'", ext, buf);
|
|
if (IsOurAppID((WCHAR*)buf))
|
|
result = true;
|
|
}
|
|
RegCloseKey(hExtKey);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int MyRegistry::DisownExtension(const WCHAR* ext) const
|
|
{
|
|
ASSERT(ext != NULL);
|
|
ASSERT(ext[0] == '.');
|
|
if (ext == NULL || wcslen(ext) < 2)
|
|
return -1;
|
|
|
|
if (RegDeleteKeyNT(HKEY_CLASSES_ROOT, ext) == ERROR_SUCCESS) {
|
|
LOGI(" HKCR\\%ls subtree deleted", ext);
|
|
} else {
|
|
LOGW(" Failed deleting HKCR\\'%ls'", ext);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int MyRegistry::OwnExtension(const WCHAR* ext, const WCHAR* appID) const
|
|
{
|
|
ASSERT(ext != NULL);
|
|
ASSERT(ext[0] == '.');
|
|
if (ext == NULL || wcslen(ext) < 2)
|
|
return -1;
|
|
|
|
HKEY hExtKey = NULL;
|
|
DWORD dw;
|
|
int res, result = -1;
|
|
|
|
/* delete the old key (which might be a hierarchy) */
|
|
res = RegDeleteKeyNT(HKEY_CLASSES_ROOT, ext);
|
|
if (res == ERROR_SUCCESS) {
|
|
LOGI(" HKCR\\%ls subtree deleted", ext);
|
|
} else if (res == ERROR_FILE_NOT_FOUND) {
|
|
LOGI(" No HKCR\\%ls subtree to delete", ext);
|
|
} else {
|
|
LOGW(" Failed deleting HKCR\\'%ls'", ext);
|
|
goto bail;
|
|
}
|
|
|
|
/* set the new key */
|
|
if (RegCreateKeyEx(HKEY_CLASSES_ROOT, ext, 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hExtKey, &dw) == ERROR_SUCCESS)
|
|
{
|
|
res = RegSetValueEx(hExtKey, L"", 0, REG_SZ,
|
|
(LPBYTE) appID, wcslen(appID) * sizeof(WCHAR));
|
|
if (res == ERROR_SUCCESS) {
|
|
LOGI(" Set '%ls' to '%ls'", ext, appID);
|
|
result = 0;
|
|
} else {
|
|
LOGW("Failed setting '%ls' to '%ls' (res=%d)", ext, appID, res);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
bail:
|
|
RegCloseKey(hExtKey);
|
|
return result;
|
|
}
|
|
|
|
|
|
// (This comes from the MSDN sample sources.)
|
|
//
|
|
// The sample code makes no attempt to check or recover from partial
|
|
// deletions.
|
|
//
|
|
// A registry key that is opened by an application can be deleted
|
|
// without error by another application in both Windows 95 and
|
|
// Windows NT. This is by design.
|
|
//
|
|
#define MAX_KEY_LENGTH 256 // not in any header I can find ++ATM
|
|
DWORD MyRegistry::RegDeleteKeyNT(HKEY hStartKey, LPCTSTR pKeyName) const
|
|
{
|
|
DWORD dwRtn, dwSubKeyLength;
|
|
LPTSTR pSubKey = NULL;
|
|
TCHAR szSubKey[MAX_KEY_LENGTH]; // (256) this should be dynamic.
|
|
HKEY hKey;
|
|
|
|
// Do not allow NULL or empty key name
|
|
if ( pKeyName && lstrlen(pKeyName))
|
|
{
|
|
if( (dwRtn=RegOpenKeyEx(hStartKey,pKeyName,
|
|
0, KEY_ENUMERATE_SUB_KEYS | DELETE, &hKey )) == ERROR_SUCCESS)
|
|
{
|
|
while (dwRtn == ERROR_SUCCESS )
|
|
{
|
|
dwSubKeyLength = MAX_KEY_LENGTH;
|
|
dwRtn=RegEnumKeyEx(
|
|
hKey,
|
|
0, // always index zero, because we're deleting it
|
|
szSubKey,
|
|
&dwSubKeyLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if(dwRtn == ERROR_NO_MORE_ITEMS)
|
|
{
|
|
dwRtn = RegDeleteKey(hStartKey, pKeyName);
|
|
break;
|
|
}
|
|
else if(dwRtn == ERROR_SUCCESS)
|
|
dwRtn=RegDeleteKeyNT(hKey, szSubKey);
|
|
}
|
|
RegCloseKey(hKey);
|
|
// Do not save return code because error
|
|
// has already occurred
|
|
}
|
|
}
|
|
else
|
|
dwRtn = ERROR_BADKEY;
|
|
|
|
return dwRtn;
|
|
}
|
|
#endif
|