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.
654 lines
19 KiB
654 lines
19 KiB
/*****************************************************************************
|
|
*
|
|
* scratch.c
|
|
*
|
|
* Scratch application.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <shlobj.h>
|
|
#include <shlobjp.h>
|
|
#include <shlwapi.h>
|
|
#include <shlwapip.h>
|
|
#include <shellapi.h>
|
|
#include <ole2.h>
|
|
|
|
#include "umrdpdr.h"
|
|
#include "drdevlst.h"
|
|
#include "umrdpdrv.h"
|
|
#include "drdbg.h"
|
|
|
|
#include <wlnotify.h>
|
|
|
|
#define ALLOCMEM(size) HeapAlloc(RtlProcessHeap(), 0, size)
|
|
#define FREEMEM(pointer) HeapFree(RtlProcessHeap(), 0, \
|
|
pointer)
|
|
|
|
// Global debug flag.
|
|
extern DWORD GLOBAL_DEBUG_FLAGS;
|
|
extern HINSTANCE g_hInstance;
|
|
|
|
/******************************************************************************
|
|
*
|
|
* This is the private version of createsession key
|
|
*
|
|
******************************************************************************/
|
|
|
|
#define REGSTR_PATH_EXPLORER TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer")
|
|
|
|
HKEY _SHGetExplorerHkey()
|
|
{
|
|
HKEY hUser;
|
|
HKEY hkeyExplorer = NULL;
|
|
|
|
if (RegOpenCurrentUser(KEY_WRITE, &hUser) == ERROR_SUCCESS) {
|
|
RegCreateKey(hUser, REGSTR_PATH_EXPLORER, &hkeyExplorer);
|
|
RegCloseKey(hUser);
|
|
}
|
|
|
|
return hkeyExplorer;
|
|
}
|
|
|
|
//
|
|
// The "Session key" is a volatile registry key unique to this session.
|
|
// A session is a single continuous logon. If Explorer crashes and is
|
|
// auto-restarted, the two Explorers share the same session. But if you
|
|
// log off and back on, that new Explorer is a new session.
|
|
//
|
|
// Note that Win9x doesn't support volatile registry keys, so
|
|
// we just fake it.
|
|
//
|
|
|
|
//
|
|
// The s_SessionKeyName is the name of the session key relative to
|
|
// REGSTR_PATH_EXPLORER\SessionInfo. On NT, this is normally the
|
|
// Authentication ID, but we pre-initialize it to something safe so
|
|
// we don't fault if for some reason we can't get to it. Since
|
|
// Win95 supports only one session at a time, it just stays at the
|
|
// default value.
|
|
//
|
|
// Sometimes we want to talk about the full path (SessionInfo\BlahBlah)
|
|
// and sometimes just the partial path (BlahBlah) so we wrap it inside
|
|
// this goofy structure.
|
|
//
|
|
|
|
union SESSIONKEYNAME {
|
|
TCHAR szPath[12+16+1];
|
|
struct {
|
|
TCHAR szSessionInfo[12]; // strlen("SepssionInfo\\")
|
|
TCHAR szName[16+1]; // 16 = two DWORDs converted to hex
|
|
};
|
|
} s_SessionKeyName = {
|
|
{ TEXT("SessionInfo\\.Default") }
|
|
};
|
|
#ifdef WINNT
|
|
BOOL g_fHaveSessionKeyName = FALSE;
|
|
#endif
|
|
|
|
//
|
|
// samDesired = a registry security access mask, or the special value
|
|
// 0xFFFFFFFF to delete the session key.
|
|
// phk = receives the session key on success
|
|
//
|
|
// NOTE! Only Explorer should delete the session key (when the user
|
|
// logs off).
|
|
//
|
|
STDAPI _SHCreateSessionKey(REGSAM samDesired, HKEY *phk)
|
|
{
|
|
LONG lRes;
|
|
HKEY hkExplorer;
|
|
|
|
*phk = NULL;
|
|
|
|
#ifdef WINNT
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: _SHCreateSessionKey\n"));
|
|
|
|
|
|
if (!g_fHaveSessionKeyName)
|
|
{
|
|
HANDLE hToken;
|
|
|
|
//
|
|
// Build the name of the session key. We use the authentication ID
|
|
// which is guaranteed to be unique forever. We can't use the
|
|
// Hydra session ID since that can be recycled.
|
|
//
|
|
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken) ||
|
|
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
{
|
|
TOKEN_STATISTICS stats;
|
|
DWORD cbOut;
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: thread token: %p\n", hToken));
|
|
|
|
if (GetTokenInformation(hToken, TokenStatistics, &stats, sizeof(stats), &cbOut))
|
|
{
|
|
wsprintf(s_SessionKeyName.szName, TEXT("%08x%08x"),
|
|
stats.AuthenticationId.HighPart,
|
|
stats.AuthenticationId.LowPart);
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: Session Key: %S\n", s_SessionKeyName.szName));
|
|
|
|
g_fHaveSessionKeyName = TRUE;
|
|
}
|
|
else {
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: failed to get token info, error: %d\n",
|
|
GetLastError()));
|
|
}
|
|
|
|
CloseHandle(hToken);
|
|
}
|
|
else {
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: failed to open thread token, error: %d\n",
|
|
GetLastError()));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: SessionKey: %S\n", s_SessionKeyName.szName));
|
|
|
|
hkExplorer = _SHGetExplorerHkey();
|
|
|
|
if (hkExplorer)
|
|
{
|
|
if (samDesired != 0xFFFFFFFF)
|
|
{
|
|
DWORD dwDisposition;
|
|
lRes = RegCreateKeyEx(hkExplorer, s_SessionKeyName.szPath, 0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
samDesired,
|
|
NULL,
|
|
phk,
|
|
&dwDisposition );
|
|
}
|
|
else
|
|
{
|
|
lRes = SHDeleteKey(hkExplorer, s_SessionKeyName.szPath);
|
|
}
|
|
|
|
RegCloseKey(hkExplorer);
|
|
}
|
|
else
|
|
{
|
|
lRes = ERROR_ACCESS_DENIED;
|
|
}
|
|
return HRESULT_FROM_WIN32(lRes);
|
|
}
|
|
|
|
|
|
//
|
|
// Get a key to HKEY_CURRENT_USER\Software\Classes\CLSID
|
|
//
|
|
HKEY GetHKCUClassesCLSID()
|
|
{
|
|
HKEY hUser;
|
|
HKEY hkClassesCLSID = NULL;
|
|
|
|
if (RegOpenCurrentUser(KEY_WRITE, &hUser) == ERROR_SUCCESS) {
|
|
if (RegCreateKeyW(hUser,
|
|
L"Software\\Classes\\CLSID",
|
|
&hkClassesCLSID) == ERROR_SUCCESS) {
|
|
RegCloseKey(hUser);
|
|
return hkClassesCLSID;
|
|
} else {
|
|
RegCloseKey(hUser);
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
//
|
|
// Get a key to HKEY_CURRENT_USER\Software\Classes\Wow6432Node\CLSID
|
|
//
|
|
HKEY GetHKCUWow64ClassesCLSID()
|
|
{
|
|
HKEY hUser;
|
|
HKEY hkClassesCLSID = NULL;
|
|
|
|
if (RegOpenCurrentUser(KEY_WRITE, &hUser) == ERROR_SUCCESS) {
|
|
if (RegCreateKeyW(hUser,
|
|
L"Software\\Classes\\Wow6432Node\\CLSID",
|
|
&hkClassesCLSID) == ERROR_SUCCESS) {
|
|
RegCloseKey(hUser);
|
|
return hkClassesCLSID;
|
|
} else {
|
|
RegCloseKey(hUser);
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
return NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Describes a registry key in the form specified in the article
|
|
// http://msdn.microsoft.com/library/techart/shellinstobj.htm
|
|
//
|
|
// {guid}=REG_SZ:"Sample Instance Object"
|
|
// value InfoTip=REG_SZ:"Demonstrate sample shell registry folder"
|
|
// DefaultIcon=REG_EXPAND_SZ:"%SystemRoot%\system32\shell32.dll,9"
|
|
// InProcServer32=REG_EXPAND_SZ:"%SystemRoot%\system32\shdocvw.dll"
|
|
// value ThreadingModel=REG_SZ:"Apartment"
|
|
// ShellFolder
|
|
// value Attributes=REG_DWORD:0x60000000
|
|
// value WantsFORPARSING=REG_SZ:""
|
|
// Instance
|
|
// value CLSID=REG_SZ:"{0AFACED1-E828-11D1-9187-B532F1E9575D}"
|
|
// InitPropertyBag
|
|
// value Target=REG_SZ:"\\raymondc\public"
|
|
|
|
typedef struct _REGKEYENTRY {
|
|
PWCHAR pszSubkey;
|
|
PWCHAR pszValue;
|
|
DWORD dwType;
|
|
LPVOID pvData;
|
|
} REGKEYENTRY;
|
|
|
|
REGKEYENTRY g_RegEntry[] = {
|
|
{
|
|
NULL,
|
|
NULL,
|
|
REG_SZ,
|
|
L"tsclient drive", /* folder display name e.g. \\tsclient\c */
|
|
},
|
|
|
|
{
|
|
NULL,
|
|
L"InfoTip",
|
|
REG_SZ,
|
|
L"Your local machine's disk storage", // info tip comments
|
|
},
|
|
|
|
{
|
|
L"DefaultIcon",
|
|
NULL,
|
|
REG_EXPAND_SZ,
|
|
L"%SystemRoot%\\system32\\shell32.dll,9", // icon resource file
|
|
},
|
|
|
|
{
|
|
L"InProcServer32",
|
|
NULL,
|
|
REG_EXPAND_SZ,
|
|
L"%SystemRoot%\\system32\\shdocvw.dll",
|
|
},
|
|
|
|
{
|
|
L"InProcServer32",
|
|
L"ThreadingModel",
|
|
REG_SZ,
|
|
L"Apartment",
|
|
},
|
|
|
|
{
|
|
L"InProcServer32",
|
|
L"LoadWithoutCOM",
|
|
REG_SZ,
|
|
L"",
|
|
},
|
|
|
|
{
|
|
L"ShellFolder",
|
|
L"Attributes",
|
|
REG_DWORD,
|
|
((VOID *)(ULONG_PTR)0xF0000000),
|
|
},
|
|
|
|
{
|
|
L"ShellFolder",
|
|
L"WantsFORPARSING",
|
|
REG_SZ,
|
|
L"",
|
|
},
|
|
|
|
{
|
|
L"Instance",
|
|
L"CLSID",
|
|
REG_SZ,
|
|
L"{0AFACED1-E828-11D1-9187-B532F1E9575D}",
|
|
},
|
|
|
|
{
|
|
L"Instance",
|
|
L"LoadWithoutCOM",
|
|
REG_SZ,
|
|
L"",
|
|
},
|
|
|
|
{
|
|
L"Instance\\InitPropertyBag",
|
|
L"Target",
|
|
REG_SZ,
|
|
L"\\\\tsclient\\c", /* Target name e.g. \\tsclient\c */
|
|
},
|
|
};
|
|
|
|
#define NUM_REGKEYENTRY (sizeof(g_RegEntry)/sizeof(g_RegEntry[0]))
|
|
#define DISPLAY_INDEX 0
|
|
#define INFOTIP_INDEX 1
|
|
#define TARGET_INDEX (NUM_REGKEYENTRY - 1)
|
|
|
|
|
|
//
|
|
// Create volatile shell folder reg entries
|
|
//
|
|
BOOL CreateVolatilePerUserCLSID(HKEY hkClassesCLSID, PWCHAR pszGuid)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
unsigned i;
|
|
HKEY hk;
|
|
|
|
if (RegCreateKeyEx(hkClassesCLSID, pszGuid, 0, NULL,
|
|
REG_OPTION_VOLATILE, KEY_WRITE, NULL,
|
|
&hk, NULL) == ERROR_SUCCESS) {
|
|
|
|
fSuccess = TRUE;
|
|
|
|
// Okay, now fill the key with the information above
|
|
for (i = 0; i < NUM_REGKEYENTRY && fSuccess; i++) {
|
|
HKEY hkSub;
|
|
HKEY hkClose = NULL;
|
|
LONG lRes;
|
|
|
|
if (g_RegEntry[i].pszSubkey && *g_RegEntry[i].pszSubkey) {
|
|
lRes = RegCreateKeyEx(hk, g_RegEntry[i].pszSubkey, 0, NULL,
|
|
REG_OPTION_VOLATILE, KEY_WRITE, NULL,
|
|
&hkSub, NULL);
|
|
hkClose = hkSub;
|
|
} else {
|
|
hkSub = hk;
|
|
lRes = ERROR_SUCCESS;
|
|
}
|
|
|
|
if (lRes == ERROR_SUCCESS) {
|
|
LPVOID pvData;
|
|
DWORD cbData;
|
|
DWORD dwData;
|
|
|
|
if (g_RegEntry[i].dwType == REG_DWORD) {
|
|
cbData = 4;
|
|
dwData = PtrToUlong(g_RegEntry[i].pvData);
|
|
pvData = (LPVOID)&dwData;
|
|
} else {
|
|
cbData = (lstrlen((LPCTSTR)g_RegEntry[i].pvData) + 1) * sizeof(TCHAR);
|
|
pvData = g_RegEntry[i].pvData;
|
|
}
|
|
|
|
if (RegSetValueEx(hkSub, g_RegEntry[i].pszValue, 0,
|
|
g_RegEntry[i].dwType, (LPBYTE)pvData, cbData) != ERROR_SUCCESS) {
|
|
fSuccess = FALSE;
|
|
}
|
|
|
|
if (hkClose) RegCloseKey(hkClose);
|
|
} else {
|
|
fSuccess = FALSE;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hk);
|
|
|
|
if (!fSuccess) {
|
|
SHDeleteKey(hkClassesCLSID, pszGuid);
|
|
}
|
|
}
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
//
|
|
// Create shell reg folder for redirected client drive connection
|
|
//
|
|
BOOL CreateDriveFolder(WCHAR *RemoteName, WCHAR *ClientDisplayName, PDRDEVLSTENTRY deviceEntry)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
WCHAR *szGuid = NULL;
|
|
WCHAR szBuf[MAX_PATH];
|
|
GUID guid;
|
|
|
|
HRESULT hrInit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: CreateDriveFolder\n"));
|
|
|
|
fSuccess = SUCCEEDED(CoCreateGuid(&guid));
|
|
|
|
if (fSuccess) {
|
|
// Allocate guid string buffer
|
|
szGuid = (WCHAR *) ALLOCMEM(GUIDSTR_MAX * sizeof(WCHAR));
|
|
|
|
if (szGuid != NULL) {
|
|
fSuccess = TRUE;
|
|
}
|
|
else {
|
|
fSuccess = FALSE;
|
|
}
|
|
}
|
|
|
|
if (fSuccess) {
|
|
PVOID pvData;
|
|
WCHAR onString[32];
|
|
WCHAR infoTip[64];
|
|
LPWSTR args[2];
|
|
|
|
SHStringFromGUID(&guid, szGuid, GUIDSTR_MAX);
|
|
|
|
onString[0] = L'\0';
|
|
infoTip[0] = L'\0';
|
|
|
|
LoadString(g_hInstance, IDS_ON, onString, sizeof(onString) / sizeof(WCHAR));
|
|
LoadString(g_hInstance, IDS_DRIVE_INFO_TIP, infoTip, sizeof(infoTip) / sizeof(WCHAR));
|
|
|
|
// Set up shell folder display name
|
|
pvData = ALLOCMEM(MAX_PATH * sizeof(WCHAR));
|
|
if (pvData) {
|
|
args[0] = deviceEntry->clientDeviceName;
|
|
args[1] = ClientDisplayName;
|
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
onString, 0, 0, pvData, MAX_PATH, (va_list*)args);
|
|
|
|
g_RegEntry[DISPLAY_INDEX].pvData = pvData;
|
|
}
|
|
else {
|
|
fSuccess = FALSE;
|
|
}
|
|
|
|
// Setup shell folder target name
|
|
if (fSuccess) {
|
|
pvData = ALLOCMEM((wcslen(RemoteName) + 1) * sizeof(WCHAR));
|
|
if (pvData) {
|
|
wcscpy(pvData, RemoteName);
|
|
g_RegEntry[TARGET_INDEX].pvData = pvData;
|
|
}
|
|
else {
|
|
fSuccess = FALSE;
|
|
FREEMEM(g_RegEntry[DISPLAY_INDEX].pvData);
|
|
g_RegEntry[DISPLAY_INDEX].pvData = NULL;
|
|
}
|
|
|
|
// Create the shell instance object as a volatile per-user objects
|
|
if (fSuccess) {
|
|
pvData = ALLOCMEM((wcslen(infoTip) + 1) * sizeof(WCHAR));
|
|
|
|
if (pvData) {
|
|
wcscpy(pvData, infoTip);
|
|
g_RegEntry[INFOTIP_INDEX].pvData = pvData;
|
|
}
|
|
else {
|
|
fSuccess = FALSE;
|
|
FREEMEM(g_RegEntry[DISPLAY_INDEX].pvData);
|
|
g_RegEntry[DISPLAY_INDEX].pvData = NULL;
|
|
|
|
FREEMEM(g_RegEntry[TARGET_INDEX].pvData);
|
|
g_RegEntry[TARGET_INDEX].pvData = NULL;
|
|
}
|
|
}
|
|
|
|
if (fSuccess) {
|
|
HKEY hk64ClassesCLSID;
|
|
HKEY hkClassesCLSID;
|
|
|
|
hkClassesCLSID = GetHKCUClassesCLSID();
|
|
|
|
if (hkClassesCLSID) {
|
|
fSuccess = CreateVolatilePerUserCLSID(hkClassesCLSID, szGuid);
|
|
RegCloseKey(hkClassesCLSID);
|
|
}
|
|
else {
|
|
fSuccess = FALSE;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
hk64ClassesCLSID = GetHKCUWow64ClassesCLSID();
|
|
|
|
|
|
if (hk64ClassesCLSID) {
|
|
fSuccess = CreateVolatilePerUserCLSID(hk64ClassesCLSID, szGuid);
|
|
RegCloseKey(hk64ClassesCLSID);
|
|
}
|
|
else {
|
|
fSuccess = FALSE;
|
|
}
|
|
#endif
|
|
|
|
FREEMEM(g_RegEntry[DISPLAY_INDEX].pvData);
|
|
g_RegEntry[DISPLAY_INDEX].pvData = NULL;
|
|
|
|
FREEMEM(g_RegEntry[TARGET_INDEX].pvData);
|
|
g_RegEntry[TARGET_INDEX].pvData = NULL;
|
|
|
|
FREEMEM(g_RegEntry[INFOTIP_INDEX].pvData);
|
|
g_RegEntry[INFOTIP_INDEX].pvData = NULL;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
DBGMSG(DBG_ERROR, ("SHLEXT: Failed to create the GUID\n"));
|
|
}
|
|
|
|
// Register this object under the per-session My Computer namespace
|
|
if (fSuccess) {
|
|
HKEY hkSession;
|
|
HKEY hkOut;
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: Created VolatilePerUserCLSID\n"));
|
|
|
|
fSuccess = SUCCEEDED(_SHCreateSessionKey(KEY_WRITE, &hkSession));
|
|
|
|
if (fSuccess) {
|
|
wnsprintf(szBuf, MAX_PATH, L"MyComputer\\Namespace\\%s", szGuid);
|
|
|
|
if (RegCreateKeyEx(hkSession, szBuf, 0, NULL,
|
|
REG_OPTION_VOLATILE, KEY_WRITE, NULL,
|
|
&hkOut, NULL) == ERROR_SUCCESS) {
|
|
|
|
fSuccess = TRUE;
|
|
RegCloseKey(hkOut);
|
|
} else {
|
|
fSuccess = FALSE;
|
|
}
|
|
|
|
RegCloseKey(hkSession);
|
|
}
|
|
}
|
|
|
|
// Now tell the shell that the object was recently created
|
|
if (fSuccess) {
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: Created per session MyComputer namespace\n"));
|
|
|
|
wnsprintf(szBuf, MAX_PATH,
|
|
TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::%s"),
|
|
szGuid);
|
|
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szBuf, NULL);
|
|
}
|
|
else {
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: Failed to create per session MyComputer namespace\n"));
|
|
}
|
|
|
|
if (SUCCEEDED(hrInit))
|
|
CoUninitialize();
|
|
|
|
// Save the guid in device entry so we can delete reg entry later
|
|
deviceEntry->deviceSpecificData = (PVOID)szGuid;
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
//
|
|
// Delete shell reg folder for redirected client drive connection
|
|
//
|
|
BOOL DeleteDriveFolder(IN PDRDEVLSTENTRY deviceEntry)
|
|
{
|
|
WCHAR szBuf[MAX_PATH];
|
|
WCHAR *szGuid;
|
|
HKEY hkSession;
|
|
HKEY hkClassesCLSID;
|
|
HKEY hk64ClassesCLSID;
|
|
|
|
HRESULT hrInit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: DeleteDriveFolder\n"));
|
|
|
|
ASSERT(deviceEntry != NULL);
|
|
szGuid = (WCHAR *)(deviceEntry->deviceSpecificData);
|
|
|
|
if (szGuid != NULL) {
|
|
|
|
// Delete it from the namespace
|
|
if (SUCCEEDED(_SHCreateSessionKey(KEY_WRITE, &hkSession))) {
|
|
wnsprintf(szBuf, MAX_PATH, L"MyComputer\\Namespace\\%s", szGuid);
|
|
RegDeleteKey(hkSession, szBuf);
|
|
RegCloseKey(hkSession);
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: Delete GUID from my computer session namespace\n"));
|
|
}
|
|
|
|
// Delete it from HKCU\...\CLSID
|
|
hkClassesCLSID = GetHKCUClassesCLSID();
|
|
if (hkClassesCLSID) {
|
|
SHDeleteKey(hkClassesCLSID, szGuid);
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: Delete GUID from HKCU Classes\n"));
|
|
|
|
RegCloseKey(hkClassesCLSID);
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
hk64ClassesCLSID = GetHKCUWow64ClassesCLSID();
|
|
if (hk64ClassesCLSID) {
|
|
SHDeleteKey(hk64ClassesCLSID, szGuid);
|
|
|
|
DBGMSG(DBG_TRACE, ("SHLEXT: Delete GUID from HKCU Classes\n"));
|
|
|
|
RegCloseKey(hk64ClassesCLSID);
|
|
}
|
|
#endif
|
|
|
|
// Tell the shell that it's gone
|
|
wnsprintf(szBuf, MAX_PATH,
|
|
TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::%s"),
|
|
szGuid);
|
|
SHChangeNotify(SHCNE_DELETE, SHCNF_PATH, szBuf, NULL);
|
|
|
|
FREEMEM(szGuid);
|
|
deviceEntry->deviceSpecificData = NULL;
|
|
}
|
|
|
|
//
|
|
// Need to reset the session key on disconnect/logoff
|
|
//
|
|
g_fHaveSessionKeyName = FALSE;
|
|
|
|
if (SUCCEEDED(hrInit))
|
|
CoUninitialize();
|
|
|
|
return TRUE;
|
|
}
|
|
|