mirror of
https://github.com/marketideas/qasm.git
synced 2024-12-29 12:33:03 +00:00
737 lines
23 KiB
C++
737 lines
23 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 kCompanyName L"faddenSoft"
|
|
|
|
#if 0
|
|
#define kRegAppName L"CiderPress"
|
|
#define kRegExeName L"CiderPress.exe"
|
|
|
|
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;
|
|
|
|
static const WCHAR kRegKeyCPKStr[] = L"CPK";
|
|
#endif
|
|
|
|
/*
|
|
* ProgID fields.
|
|
*
|
|
* See http://msdn.microsoft.com/en-us/library/windows/desktop/cc144152%28v=vs.85%29.aspx
|
|
*/
|
|
|
|
static const WCHAR kDefaultIcon[] = L"DefaultIcon";
|
|
static const WCHAR kFriendlyTypeName[] = L"FriendlyTypeName";
|
|
static const WCHAR kInfoTip[] = L"InfoTip";
|
|
|
|
static const WCHAR kShellOpenCommand[] = L"\\shell\\open\\command";
|
|
|
|
|
|
/*
|
|
* ProgID key names.
|
|
*
|
|
* We used to open HKEY_CLASSES_ROOT, which provides a "merged" view of
|
|
* HKEY_LOCAL_MACHINE\Software\Classes and HKEY_CURRENT_USER\Software\Classes.
|
|
* The HKLM entries provided defaults for all users on the machine, while the
|
|
* HKCU entries were specific to the current user.
|
|
*
|
|
* It appears that Windows no longer likes it when executables other than the
|
|
* app installer (which can run privileged) mess with HKLM, so we just work
|
|
* with HKCU directly now.
|
|
*/
|
|
static const WCHAR kProgIdKeyNuFX[] = L"CiderPress.NuFX.4";
|
|
static const WCHAR kProgIdKeyDiskImage[] = L"CiderPress.DiskImage.4";
|
|
static const WCHAR kProgIdKeyBinaryII[] = L"CiderPress.BinaryII.4";
|
|
|
|
/* file associations go here under HKCU */
|
|
static const WCHAR kFileAssocBase[] = L"Software\\Classes";
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
const MyRegistry::FileTypeAssoc MyRegistry::kFileTypeAssoc[] = {
|
|
{ L".2MG", kProgIdKeyDiskImage },
|
|
{ L".APP", kProgIdKeyDiskImage },
|
|
{ L".BNY", kProgIdKeyBinaryII },
|
|
{ L".BQY", kProgIdKeyBinaryII },
|
|
{ L".BSE", kProgIdKeyNuFX },
|
|
{ L".BXY", kProgIdKeyNuFX },
|
|
{ L".D13", kProgIdKeyDiskImage },
|
|
{ L".DDD", kProgIdKeyDiskImage },
|
|
{ L".DO", kProgIdKeyDiskImage },
|
|
{ L".DSK", kProgIdKeyDiskImage },
|
|
{ L".FDI", kProgIdKeyDiskImage },
|
|
{ L".HDV", kProgIdKeyDiskImage },
|
|
{ L".IMG", kProgIdKeyDiskImage },
|
|
{ L".NIB", kProgIdKeyDiskImage },
|
|
{ L".PO", kProgIdKeyDiskImage },
|
|
{ L".SDK", kProgIdKeyDiskImage },
|
|
{ L".SEA", kProgIdKeyNuFX },
|
|
{ L".SHK", kProgIdKeyNuFX },
|
|
// { L".DC", kProgIdKeyDiskImage },
|
|
// { L".DC6", kProgIdKeyDiskImage },
|
|
// { L".GZ", kProgIdKeyDiskImage },
|
|
// { L".NB2", kProgIdKeyDiskImage },
|
|
// { L".RAW", kProgIdKeyDiskImage },
|
|
// { L".ZIP", kProgIdKeyDiskImage },
|
|
};
|
|
|
|
#if 0
|
|
static const struct {
|
|
const char* user;
|
|
const char* reg;
|
|
} gBadKeys[] = {
|
|
{ "Nimrod Bonehead", "CP1-68C069-62CC9444" },
|
|
{ "Connie Tan", "CP1-877B2C-A428FFD6" },
|
|
};
|
|
#endif
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* One-time install/uninstall
|
|
* ==========================================================================
|
|
*/
|
|
|
|
void MyRegistry::OneTimeInstall(void) const
|
|
{
|
|
/* start by stomping on our ProgIDs */
|
|
LOGI(" Removing ProgIDs");
|
|
RegDeleteKeyHKCU(kProgIdKeyNuFX);
|
|
RegDeleteKeyHKCU(kProgIdKeyDiskImage);
|
|
RegDeleteKeyHKCU(kProgIdKeyBinaryII);
|
|
|
|
/* configure the ProgIDs */
|
|
FixBasicSettings();
|
|
|
|
HKEY hClassesKey = NULL;
|
|
if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
/* configure extensions */
|
|
for (int i = 0; i < NELEM(kFileTypeAssoc); i++) {
|
|
HKEY hExtKey;
|
|
LSTATUS res = RegOpenKeyEx(hClassesKey, kFileTypeAssoc[i].ext, 0,
|
|
KEY_READ, &hExtKey);
|
|
if (res == ERROR_SUCCESS) {
|
|
LOGI(" Found existing HKCU\\%ls\\'%ls', leaving alone",
|
|
kFileAssocBase, kFileTypeAssoc[i].ext);
|
|
RegCloseKey(hExtKey);
|
|
} else if (res == ERROR_FILE_NOT_FOUND) {
|
|
OwnExtension(kFileTypeAssoc[i].ext, kFileTypeAssoc[i].progId);
|
|
} else {
|
|
LOGW(" Got error %lu opening HKCU\\%ls\\'%ls', leaving alone",
|
|
res, kFileAssocBase, kFileTypeAssoc[i].ext);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hClassesKey);
|
|
}
|
|
|
|
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 ProgIDs */
|
|
LOGI(" Removing ProgIDs");
|
|
RegDeleteKeyHKCU(kProgIdKeyNuFX);
|
|
RegDeleteKeyHKCU(kProgIdKeyDiskImage);
|
|
RegDeleteKeyHKCU(kProgIdKeyBinaryII);
|
|
}
|
|
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* Shareware registration logic
|
|
* ==========================================================================
|
|
*/
|
|
|
|
/* [removed] */
|
|
|
|
/*
|
|
* ==========================================================================
|
|
* Windows shell game
|
|
* ==========================================================================
|
|
*/
|
|
|
|
const WCHAR* MyRegistry::GetAppRegistryKey(void) const
|
|
{
|
|
return kCompanyName;
|
|
}
|
|
|
|
bool MyRegistry::IsOurProgId(const WCHAR* progIdKeyName) const
|
|
{
|
|
return (wcsicmp(progIdKeyName, kProgIdKeyNuFX) == 0 ||
|
|
wcsicmp(progIdKeyName, kProgIdKeyDiskImage) == 0 ||
|
|
wcsicmp(progIdKeyName, kProgIdKeyBinaryII) == 0);
|
|
}
|
|
|
|
void MyRegistry::FixBasicSettings(void) const
|
|
{
|
|
/*
|
|
* Fix the basic registry settings, e.g. our ProgID 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 ProgID entries in registry");
|
|
|
|
ConfigureProgId(kProgIdKeyNuFX, L"NuFX Archive (CiderPress)", exeName, 1);
|
|
ConfigureProgId(kProgIdKeyBinaryII, L"Binary II (CiderPress)", exeName, 2);
|
|
ConfigureProgId(kProgIdKeyDiskImage, L"Disk Image (CiderPress)", exeName, 3);
|
|
}
|
|
|
|
void MyRegistry::ConfigureProgId(const WCHAR* progIdKeyName, const WCHAR* descr,
|
|
const WCHAR* exeName, int iconIdx) const
|
|
{
|
|
LOGI(" ConfigureProgId '%ls' for '%ls'", progIdKeyName, exeName);
|
|
|
|
HKEY hClassesKey = NULL;
|
|
HKEY hAppKey = NULL;
|
|
|
|
if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) {
|
|
LOGW(" ConfigureProgId failed to open HKCU");
|
|
return;
|
|
}
|
|
|
|
DWORD disp;
|
|
LONG result;
|
|
if ((result = RegCreateKeyEx(hClassesKey, progIdKeyName, 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hAppKey, &disp)) == ERROR_SUCCESS)
|
|
{
|
|
if (disp == REG_CREATED_NEW_KEY) {
|
|
LOGD(" Created new key for %ls", progIdKeyName);
|
|
} else if (disp == REG_OPENED_EXISTING_KEY) {
|
|
LOGD(" Opened existing key for %ls", progIdKeyName);
|
|
} else {
|
|
LOGD(" Odd RegCreateKeyEx result 0x%lx", disp);
|
|
}
|
|
|
|
ConfigureProgIdCommand(hAppKey, descr, exeName);
|
|
|
|
// Configure default entry and "friendly" type name. The friendly
|
|
// name takes precedence (as tested on Win7), but the default entry
|
|
// is set for backward compatibility.
|
|
if (RegSetValueEx(hAppKey, L"", 0, REG_SZ, (const BYTE*) descr,
|
|
(wcslen(descr)+1) * sizeof(WCHAR)) != ERROR_SUCCESS)
|
|
{
|
|
LOGW(" WARNING: unable to set ProgID default to '%ls'", descr);
|
|
}
|
|
if (RegSetValueEx(hAppKey, kFriendlyTypeName, 0, REG_SZ,
|
|
(const BYTE*) descr,
|
|
(wcslen(descr)+1) * sizeof(WCHAR)) != ERROR_SUCCESS)
|
|
{
|
|
LOGW(" WARNING: unable to set ProgID friendly name to '%ls'", descr);
|
|
}
|
|
//if (RegSetValueEx(hAppKey, kInfoTip, 0, REG_SZ,
|
|
// (const BYTE*) infoTip,
|
|
// (wcslen(infoTip)+1) * sizeof(WCHAR)) != ERROR_SUCCESS)
|
|
//{
|
|
// LOGW(" WARNING: unable to set ProgID info tip to '%ls'", infoTip);
|
|
//}
|
|
|
|
// Configure the default icon field. This is a DefaultIcon entry
|
|
// with a (Default) name.
|
|
HKEY hIconKey = NULL;
|
|
if (RegCreateKeyEx(hAppKey, kDefaultIcon, 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hIconKey, NULL) == 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",
|
|
progIdKeyName);
|
|
} 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'", progIdKeyName,
|
|
(LPCWSTR) iconStr);
|
|
} else {
|
|
LOGW(" WARNING: unable to set DefaultIcon for '%ls' to '%ls'",
|
|
progIdKeyName, (LPCWSTR) iconStr);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hIconKey);
|
|
} else {
|
|
LOGW(" WARNING: couldn't set up DefaultIcon for '%ls'", progIdKeyName);
|
|
}
|
|
} else {
|
|
LOGW(" WARNING: couldn't create ProgId='%ls' (err=%ld)",
|
|
progIdKeyName, result);
|
|
}
|
|
|
|
RegCloseKey(hAppKey);
|
|
RegCloseKey(hClassesKey);
|
|
}
|
|
|
|
void MyRegistry::ConfigureProgIdCommand(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;
|
|
|
|
// TODO: I believe we can do this with a single call -- it should create
|
|
// the intermediate path elements for us.
|
|
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
|
|
* ProgID keys. We don't bother to check whether the ProgID keys
|
|
* 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));
|
|
|
|
*pExt = kFileTypeAssoc[idx].ext;
|
|
*pHandler = L"";
|
|
*pOurs = false;
|
|
|
|
HKEY hClassesKey = NULL;
|
|
if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) {
|
|
LOGW("GetFileAssoc failed to open HKCU");
|
|
return;
|
|
}
|
|
|
|
CString progIdKeyName;
|
|
HKEY hExtKey = NULL;
|
|
|
|
long res = RegOpenKeyEx(hClassesKey, *pExt, 0, KEY_READ, &hExtKey);
|
|
if (res == ERROR_SUCCESS) {
|
|
WCHAR buf[MAX_PATH]; // somewhat arbitrary
|
|
DWORD size = sizeof(buf); // size in bytes
|
|
DWORD type;
|
|
|
|
res = RegQueryValueEx(hExtKey, L"", NULL, &type, (LPBYTE)buf, &size);
|
|
if (res == ERROR_SUCCESS) {
|
|
LOGD(" GetFileAssoc %d got '%ls'", idx, buf);
|
|
progIdKeyName = buf;
|
|
|
|
if (GetAssocAppName(progIdKeyName, pHandler) != 0)
|
|
*pHandler = progIdKeyName;
|
|
} else {
|
|
LOGW("RegQueryValueEx failed on '%ls'", (LPCWSTR) *pExt);
|
|
}
|
|
} else {
|
|
LOGW(" RegOpenKeyEx failed on '%ls'", (LPCWSTR) *pExt);
|
|
}
|
|
|
|
if (!pHandler->IsEmpty()) {
|
|
*pOurs = IsOurProgId(progIdKeyName);
|
|
}
|
|
|
|
RegCloseKey(hExtKey);
|
|
RegCloseKey(hClassesKey);
|
|
}
|
|
|
|
int MyRegistry::GetAssocAppName(const CString& progIdKeyName, CString* pCmd) const
|
|
{
|
|
HKEY hAppKey = NULL;
|
|
int result = -1;
|
|
|
|
HKEY hClassesKey = NULL;
|
|
if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) {
|
|
LOGW("GetAssocAppName failed to open HKCU");
|
|
return result;
|
|
}
|
|
|
|
CString keyName = progIdKeyName + kShellOpenCommand;
|
|
|
|
long res = RegOpenKeyEx(hClassesKey, keyName, 0, KEY_READ, &hAppKey);
|
|
if (res == ERROR_SUCCESS) {
|
|
WCHAR buf[260];
|
|
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 %ls for '%ls'", (LPCWSTR) kShellOpenCommand,
|
|
(LPCWSTR) progIdKeyName);
|
|
}
|
|
} else {
|
|
CString errBuf;
|
|
GetWin32ErrorString(res, &errBuf);
|
|
|
|
LOGW("Unable to open ProgId key '%ls' (%ls)",
|
|
(LPCWSTR) keyName, (LPCWSTR) errBuf);
|
|
}
|
|
|
|
RegCloseKey(hAppKey);
|
|
RegCloseKey(hClassesKey);
|
|
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].progId);
|
|
} 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;
|
|
|
|
HKEY hClassesKey = NULL;
|
|
if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) {
|
|
LOGW("GetAssocState failed to open HKCU");
|
|
return result;
|
|
}
|
|
|
|
res = RegOpenKeyEx(hClassesKey, 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 ProgID values */
|
|
LOGD(" Found '%ls', testing '%ls'", ext, buf);
|
|
if (IsOurProgId((WCHAR*)buf))
|
|
result = true;
|
|
}
|
|
RegCloseKey(hExtKey);
|
|
}
|
|
RegCloseKey(hClassesKey);
|
|
|
|
return result;
|
|
}
|
|
|
|
int MyRegistry::DisownExtension(const WCHAR* ext) const
|
|
{
|
|
ASSERT(ext != NULL);
|
|
ASSERT(ext[0] == '.');
|
|
if (ext == NULL || wcslen(ext) < 2)
|
|
return -1;
|
|
|
|
if (RegDeleteKeyHKCU(ext) == ERROR_SUCCESS) {
|
|
LOGI(" HKCU\\%ls\\%ls subtree deleted", kFileAssocBase, ext);
|
|
} else {
|
|
LOGW(" Failed deleting HKCU\\%ls\\'%ls'", kFileAssocBase, ext);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int MyRegistry::OwnExtension(const WCHAR* ext, const WCHAR* progIdKeyName) const
|
|
{
|
|
ASSERT(ext != NULL);
|
|
ASSERT(ext[0] == '.');
|
|
if (ext == NULL || wcslen(ext) < 2)
|
|
return -1;
|
|
|
|
HKEY hClassesKey = NULL;
|
|
HKEY hExtKey = NULL;
|
|
int result = -1;
|
|
|
|
if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) {
|
|
goto bail;
|
|
}
|
|
|
|
// delete the old key (which might be a hierarchy)
|
|
DWORD res = RegDeleteKeyNT(hClassesKey, ext);
|
|
if (res == ERROR_SUCCESS) {
|
|
LOGI(" HKCU\\%ls\\%ls subtree deleted", kFileAssocBase, ext);
|
|
} else if (res == ERROR_FILE_NOT_FOUND) {
|
|
LOGI(" No HKCU\\%ls\\%ls subtree to delete", kFileAssocBase, ext);
|
|
} else {
|
|
LOGW(" Failed deleting HKCU\\%ls\\'%ls'", kFileAssocBase, ext);
|
|
goto bail;
|
|
}
|
|
|
|
// set the new key
|
|
DWORD dw;
|
|
if (RegCreateKeyEx(hClassesKey, ext, 0, REG_NONE,
|
|
REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL,
|
|
&hExtKey, &dw) == ERROR_SUCCESS)
|
|
{
|
|
// default entry gets the ProgID key name
|
|
res = RegSetValueEx(hExtKey, L"", 0, REG_SZ, (LPBYTE) progIdKeyName,
|
|
(wcslen(progIdKeyName)+1) * sizeof(WCHAR));
|
|
if (res == ERROR_SUCCESS) {
|
|
LOGI(" Set '%ls' to '%ls'", ext, progIdKeyName);
|
|
result = 0;
|
|
} else {
|
|
LOGW("Failed setting '%ls' to '%ls' (res=%d)",
|
|
ext, progIdKeyName, res);
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
bail:
|
|
RegCloseKey(hExtKey);
|
|
RegCloseKey(hClassesKey);
|
|
return result;
|
|
}
|
|
|
|
DWORD MyRegistry::OpenHKCUSoftwareClasses(HKEY* phKey) const
|
|
{
|
|
DWORD result = RegOpenKeyEx(HKEY_CURRENT_USER, kFileAssocBase, 0,
|
|
KEY_READ, phKey);
|
|
if (result != ERROR_SUCCESS) {
|
|
LOGW("Unable to open HKEY_CURRENT_USER \\ '%ls' for reading",
|
|
(LPCWSTR) kFileAssocBase);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DWORD MyRegistry::RegDeleteKeyHKCU(const WCHAR* partialKeyName) const
|
|
{
|
|
HKEY hClassesKey;
|
|
DWORD result;
|
|
|
|
result = OpenHKCUSoftwareClasses(&hClassesKey);
|
|
if (result != ERROR_SUCCESS) { return result; }
|
|
|
|
result = RegDeleteKeyNT(hClassesKey, partialKeyName);
|
|
RegCloseKey(hClassesKey);
|
|
if (result != ERROR_SUCCESS) {
|
|
LOGW("RegDeleteKeyNT failed (err=%lu)", result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// (This comes from the MSDN sample sources.)
|
|
//
|
|
// Recursively delete a key and any sub-keys.
|
|
//
|
|
// 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;
|
|
|
|
LOGD("RegDeleteKeyNT %p '%ls'", hStartKey, (LPCWSTR) pKeyName);
|
|
|
|
// 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
|