/*++ Module Name: registry.cxx Abstract: Functions to read/write registry parameters Author: Venkatraman Kudallur (venkatk) Revision History: 3-10-2000 venkatk Created --*/ #ifdef ENABLE_DEBUG #include #include "registry.h" // // manifests // #define INTERNET_CLIENT_KEY "Internet Settings" #define SYSTEM_INI_FILE_NAME "SYSTEM.INI" #define NETWORK_SECTION_NAME "Network" #define COMPUTER_NAME_VALUE "ComputerName" #define PROFILE_INT_BUFFER_LENGTH 128 #define MIME_TO_FILE_EXTENSION_KEY "MIME\\Database\\Content Type\\" #define EXTENSION_VALUE "Extension" // // macros // #define INTERNET_SETTINGS_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" #define INTERNET_CACHE_SETTINGS_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\5.0\\Cache" //from macros.h #define PRIVATE #define PUBLIC #define ARRAY_ELEMENTS(array) \ (sizeof(array)/sizeof(array[0])) //from defaults.h #define DEFAULT_EMAIL_NAME "user@domain" //from wininet.w #define ERROR_INTERNET_BAD_REGISTRY_PARAMETER 12022 PRIVATE BOOL IsPlatformWinNT() { OSVERSIONINFO osVersionInfo; BOOL fRet = FALSE; osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (GetVersionEx(&osVersionInfo)) fRet = (VER_PLATFORM_WIN32_NT == osVersionInfo.dwPlatformId); return fRet; } // // private prototypes // PRIVATE DWORD InternetReadRegistryDwordKey( IN HKEY ParameterKey, IN LPCSTR ParameterName, OUT LPDWORD ParameterValue ); PRIVATE DWORD InternetReadRegistryStringKey( IN HKEY ParameterKey, IN LPCSTR ParameterName, OUT LPSTR ParameterValue, IN OUT LPDWORD ParameterLength ); PRIVATE DWORD ReadRegistryOemString( IN HKEY Key, IN LPCSTR ParameterName, OUT LPSTR String, IN OUT LPDWORD Length ); PRIVATE DWORD WriteRegistryDword( IN HKEY Key, IN LPCSTR ParameterName, IN DWORD ParameterValue ); // // private data // PRIVATE HKEY hKeyInternetSettings = NULL; // // functions // DWORD OpenInternetSettingsKey( VOID ) /*++ Routine Description: Opens registry key for Internet Settings branch Arguments: None. Return Value: DWORD Success - ERROR_SUCCESS Failure - --*/ { if (hKeyInternetSettings == NULL) { DWORD dwDisposition; REGCREATEKEYEX(HKEY_CURRENT_USER, INTERNET_SETTINGS_KEY, 0, // reserved NULL, // class 0, // options KEY_READ | KEY_WRITE, NULL, // security attributes &hKeyInternetSettings, &dwDisposition ); } return ERROR_SUCCESS; } DWORD CloseInternetSettingsKey( VOID ) /*++ Routine Description: Closes Internet Settings registry key Arguments: None. Return Value: DWORD Success - ERROR_SUCCESS Failure - --*/ { DWORD error = ERROR_SUCCESS; if (hKeyInternetSettings != NULL) { error = REGCLOSEKEY(hKeyInternetSettings); hKeyInternetSettings = NULL; } return error; } DWORD GetMyEmailName( OUT LPSTR EmailName, IN OUT LPDWORD Length ) /*++ Routine Description: Retrieve the user's email name from the appropriate place in the registry Arguments: EmailName - place to store email name Length - IN: length of EmailName OUT: returned length of EmailName (in characters, minus trailing NUL) Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_FILE_NOT_FOUND ERROR_PATH_NOT_FOUND --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "GetMyEmailName", "%#x, %#x [%d]", EmailName, Length, *Length )); DWORD error; // // for the EmailName, we first try HKEY_CURRENT_USER. If that fails then we // try the same branch of the HKEY_LOCAL_MACHINE tree. If that fails, // invent something // static HKEY KeysToTry[2] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE}; int i; // // in the event we cannot find EmailName in both HKEY_CURRENT_USER and // HKEY_LOCAL_MACHINE trees, then we return this default // static char DefaultEmailName[] = DEFAULT_EMAIL_NAME; for (i = 0; i < ARRAY_ELEMENTS(KeysToTry); ++i) { error = InternetReadRegistryStringKey(KeysToTry[i], "EmailName", EmailName, Length ); if (error == ERROR_SUCCESS) { break; } } if (error != ERROR_SUCCESS) { if (IsPlatformWinNT()) { // // only NT supports GetUserName() // if (GetUserName(EmailName, Length)) { // // we return the length as if the result from strlen/wcslen // *Length -= sizeof(char); DEBUG_PRINT(REGISTRY, INFO, ("GetUserName() returns %q\n", EmailName )); error = ERROR_SUCCESS; } else { // // BUGBUG - what's the required length? // error = GetLastError(); } } else { // // Win95 & Win32s: have to do something different // } // // if we still don't have an email name, we use an internal default // if (error != ERROR_SUCCESS) { DEBUG_PRINT(REGISTRY, ERROR, ("Cannot find EmailName: using default (%s)\n", DefaultEmailName )); if (*Length >= sizeof(DEFAULT_EMAIL_NAME)) { memcpy(EmailName, DefaultEmailName, sizeof(DEFAULT_EMAIL_NAME)); // // success - returned length as if from strlen() // *Length = sizeof(DEFAULT_EMAIL_NAME) - 1; error = ERROR_SUCCESS; } else { // // failure - returned length is the required buffer size // *Length = sizeof(DEFAULT_EMAIL_NAME); error = ERROR_INSUFFICIENT_BUFFER; } } } DEBUG_LEAVE(error); return error; } PUBLIC DWORD InternetDeleteRegistryValue( IN LPSTR ParameterName ) /*++ Routine Description: Delets an entry from a the Internet Client registry key if the platform is NT/Win95. Arguments: ParameterName - name of the parameter to retrieve (e.g. AccessType) Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND --*/ { DWORD error; DEBUG_ENTER((DBG_REGISTRY, Dword, "InternetDeleteRegistryValue", "%q", ParameterName )); HKEY clientKey; // // open the registry key containing the Internet client values (this is // in the same place on NT and Win95) // error = REGOPENKEYEX(HKEY_CURRENT_USER, INTERNET_SETTINGS_KEY, 0, // reserved KEY_ALL_ACCESS, &clientKey ); if (error == ERROR_SUCCESS) { error = RegDeleteValue(clientKey, ParameterName ); REGCLOSEKEY(clientKey); } DEBUG_LEAVE(error); return error; } DWORD InternetReadRegistryDword( IN LPCSTR ParameterName, OUT LPDWORD ParameterValue ) /*++ Routine Description: Reads a single DWORD from a the Internet Client registry key if the platform is NT/Win95, else reads the value from SYSTEM.INI if we are running on Win32s Arguments: ParameterName - name of the parameter to retrieve (e.g. AccessType) ParameterValue - pointer to place to store retrieved value Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "InternetReadRegistryDword", "%q, %x", ParameterName, ParameterValue )); DWORD error = InternetReadRegistryDwordKey(HKEY_CURRENT_USER, ParameterName, ParameterValue ); DEBUG_LEAVE(error); return error; } DWORD InternetCacheReadRegistryDword( IN LPCSTR ParameterName, OUT LPDWORD ParameterValue ) /*++ Routine Description: Reads a single DWORD from a the Internet Client registry key if the platform is NT/Win95, else reads the value from SYSTEM.INI if we are running on Win32s Arguments: ParameterName - name of the parameter to retrieve (e.g. AccessType) ParameterValue - pointer to place to store retrieved value Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "InternetCacheReadRegistryDword", "%q, %x", ParameterName, ParameterValue )); DWORD error = ERROR_SUCCESS; HKEY clientKey; error = REGOPENKEYEX(HKEY_CURRENT_USER, INTERNET_CACHE_SETTINGS_KEY, 0, // reserved KEY_QUERY_VALUE, &clientKey ); if (error == ERROR_SUCCESS) { error = ReadRegistryDword(clientKey, ParameterName, ParameterValue ); REGCLOSEKEY(clientKey); } DEBUG_LEAVE(error); return error; } DWORD InternetWriteRegistryDword( IN LPCSTR ParameterName, IN DWORD ParameterValue ) /*++ Routine Description: Writes a single DWORD from to the Internet Client registry key if the platform is NT/Win95, otherwise it fails. Arguments: ParameterName - name of the parameter to retrieve (e.g. AccessType) ParameterValue - value to store in registry. Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "InternetWriteRegistryDword", "%q, %x", ParameterName, ParameterValue )); DWORD error; if (hKeyInternetSettings != NULL) { error = WriteRegistryDword(hKeyInternetSettings, ParameterName, ParameterValue ); } else { error = ERROR_SUCCESS; } DEBUG_PRINT(REGISTRY, INFO, ("InternetWriteRegistryDword(%q): value = %d (%#x)\n", ParameterName, ParameterValue, ParameterValue )); DEBUG_LEAVE(error); return error; } DWORD InternetReadRegistryString( IN LPCSTR ParameterName, OUT LPSTR ParameterValue, IN OUT LPDWORD ParameterLength ) /*++ Routine Description: Reads a string from the Internet Client registry key on NT/Win95, or reads the corresponding value from SYSTEM.INI on a Win32s platform Arguments: ParameterName - name of value parameter within key (e.g. EmailName) ParameterValue - pointer to string buffer for returned string ParameterLength - IN: number of bytes in ParameterValue OUT: number of bytes in string (excluding trailing '\0') Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "InternetReadRegistryString", "%q, %x, %x [%d]", ParameterName, ParameterValue, ParameterLength, *ParameterLength )); DWORD error = InternetReadRegistryStringKey(HKEY_CURRENT_USER, ParameterName, ParameterValue, ParameterLength ); DEBUG_LEAVE(error); return error; } PUBLIC DWORD InternetReadRegistryDwordKey( IN HKEY ParameterKey, IN LPCSTR ParameterName, OUT LPDWORD ParameterValue ) /*++ Routine Description: Reads a single DWORD from a the Internet Client registry key if the platform is NT/Win95, else reads the value from SYSTEM.INI if we are running on Win32s. Does not modify the *ParameterValue if the registry variable cannot be read Arguments: ParameterKey - root registry key (e.g. HKEY_CURRENT_USER) ParameterName - name of the parameter to retrieve (e.g. AccessType) ParameterValue - pointer to place to store retrieved value Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "InternetReadRegistryDwordKey", "%s, %q, %x", (ParameterKey == HKEY_LOCAL_MACHINE) ? "HKEY_LOCAL_MACHINE" : (ParameterKey == HKEY_CURRENT_USER) ? "HKEY_CURRENT_USER" : "???", ParameterName, ParameterValue )); DWORD error = ERROR_SUCCESS; HKEY clientKey = hKeyInternetSettings; if (ParameterKey != HKEY_CURRENT_USER) { error = REGOPENKEYEX(ParameterKey, INTERNET_SETTINGS_KEY, 0, // reserved KEY_QUERY_VALUE, &clientKey ); } else if (clientKey == NULL) { error = ERROR_PATH_NOT_FOUND; } if (error == ERROR_SUCCESS) { error = ReadRegistryDword(clientKey, ParameterName, ParameterValue ); if (clientKey != hKeyInternetSettings) { REGCLOSEKEY(clientKey); } } DEBUG_PRINT(REGISTRY, INFO, ("InternetReadRegistryDwordKey(%q): value = %d (%#x)\n", ParameterName, *ParameterValue, *ParameterValue )); DEBUG_LEAVE(error); return error; } PRIVATE DWORD InternetReadRegistryStringKey( IN HKEY ParameterKey, IN LPCSTR ParameterName, OUT LPSTR ParameterValue, IN OUT LPDWORD ParameterLength ) /*++ Routine Description: Reads a string from the Internet Client registry key on NT/Win95, or reads the corresponding value from SYSTEM.INI on a Win32s platform Arguments: ParameterKey - root registry key (e.g. HKEY_LOCAL_MACHINE) ParameterName - name of value parameter within key (e.g. EmailName) ParameterValue - pointer to string buffer for returned string ParameterLength - IN: number of bytes in ParameterValue OUT: number of bytes in string (excluding trailing '\0') Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "InternetReadRegistryStringKey", "%s (%x), %q, %x, %x [%d]", (ParameterKey == HKEY_LOCAL_MACHINE) ? "HKEY_LOCAL_MACHINE" : (ParameterKey == HKEY_CURRENT_USER) ? "HKEY_CURRENT_USER" : "???", ParameterKey, ParameterName, ParameterValue, ParameterLength, *ParameterLength )); // // zero-terminate the string // if (*ParameterLength > 0) { *ParameterValue = '\0'; } DWORD error = ERROR_SUCCESS; HKEY clientKey = hKeyInternetSettings; if (ParameterKey != HKEY_CURRENT_USER) { error = REGOPENKEYEX(ParameterKey, INTERNET_SETTINGS_KEY, 0, // reserved KEY_QUERY_VALUE, &clientKey ); } else if (clientKey == NULL) { error = ERROR_PATH_NOT_FOUND; } if (error == ERROR_SUCCESS) { error = ReadRegistryOemString(clientKey, ParameterName, ParameterValue, ParameterLength ); if (clientKey != hKeyInternetSettings) { REGCLOSEKEY(clientKey); } } DEBUG_PRINT(REGISTRY, INFO, ("InternetReadRegistryStringKey(%q): value = %q\n", ParameterName, ParameterValue )); DEBUG_LEAVE(error); return error; } PRIVATE DWORD ReadRegistryOemString( IN HKEY Key, IN LPCSTR ParameterName, OUT LPSTR String, IN OUT LPDWORD Length ) /*++ Routine Description: Reads a string out of the registry as an OEM string Arguments: Key - open registry key where to read value from ParameterName - name of registry value to read String - place to put it Length - IN: length of String buffer in characters OUT: length of String in characters, as if returned from strlen() Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_FILE_NOT_FOUND Couldn't find the parameter ERROR_PATH_NOT_FOUND Couldn't find the parameter ERROR_INTERNET_BAD_REGISTRY_PARAMETER Inconsistent registry contents --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "ReadRegistryOemString", "%#x, %q, %#x, %#x [%d]", Key, ParameterName, String, Length, *Length )); LONG error; DWORD valueType; LPSTR str; DWORD valueLength; // // first, get the length of the string // valueLength = *Length; error = RegQueryValueEx(Key, ParameterName, NULL, // reserved &valueType, (LPBYTE)String, &valueLength ); if (error != ERROR_SUCCESS) { goto quit; } // // we only support REG_SZ (single string) values in this function // if (valueType != REG_SZ) { error = ERROR_INTERNET_BAD_REGISTRY_PARAMETER; goto quit; } // // if 1 or 0 chars returned then the string is empty // if (valueLength <= sizeof(char)) { error = ERROR_PATH_NOT_FOUND; goto quit; } // // convert the ANSI string to OEM character set in place. According to Win // help, this always succeeds // CharToOem(String, String); // // return the length as if returned from strlen() (i.e. drop the '\0') // *Length = valueLength - sizeof(char); DEBUG_PRINT(REGISTRY, INFO, ("ReadRegistryOemString(%q) returning %q (%d chars)\n", ParameterName, String, *Length )); quit: DEBUG_LEAVE(error); return error; } DWORD ReadRegistryDword( IN HKEY Key, IN LPCSTR ParameterName, OUT LPDWORD ParameterValue ) /*++ Routine Description: Reads a DWORD parameter from the registry Won't modify *ParameterValue unless a valid value is read from the registry Arguments: Key - handle of open registry key where parameter resides ParameterName - name of DWORD parameter to read ParameterValue - returned DWORD parameter read from registry Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND One of the following occurred: - the parameter is not in the specified registry key - the parameter is the wrong type - the parameter is the wrong size --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "ReadRegistryDword", "%x, %q, %x", Key, ParameterName, ParameterValue )); DWORD error; DWORD valueLength; DWORD valueType; DWORD value; valueLength = sizeof(*ParameterValue); error = (DWORD)RegQueryValueEx(Key, ParameterName, NULL, // reserved &valueType, (LPBYTE)&value, &valueLength ); // // if the size or type aren't correct then return an error, else only if // success was returned do we modify *ParameterValue // if (error == ERROR_SUCCESS) { if (((valueType != REG_DWORD) && (valueType != REG_BINARY)) || (valueLength != sizeof(DWORD))) { DEBUG_PRINT(REGISTRY, ERROR, ("valueType = %d, valueLength = %d\n", valueType, valueLength )); error = ERROR_PATH_NOT_FOUND; } else { *ParameterValue = value; } } DEBUG_LEAVE(error); return error; } PRIVATE DWORD WriteRegistryDword( IN HKEY Key, IN LPCSTR ParameterName, IN DWORD ParameterValue ) /*++ Routine Description: Writes a DWORD parameter from the registry Will write ParameterValue to the key. Arguments: Key - handle of open registry key where parameter resides ParameterName - name of DWORD parameter to write ParameterValue - DWORD parameter to write from registry Return Value: DWORD Success - ERROR_SUCCESS Failure - ERROR_PATH_NOT_FOUND One of the following occurred: - the parameter is not in the specified registry key - the parameter is the wrong type - the parameter is the wrong size --*/ { DEBUG_ENTER((DBG_REGISTRY, Dword, "WriteRegistryDword", "%x, %q, %x", Key, ParameterName, ParameterValue )); DWORD error; DWORD valueLength; DWORD valueType; DWORD value; valueLength = sizeof(ParameterValue); valueType = REG_DWORD; value = ParameterValue; error = (DWORD)RegSetValueEx(Key, ParameterName, NULL, // reserved valueType, (LPBYTE)&value, valueLength ); DEBUG_PRINT(REGISTRY, INFO, ("added: valueType = %d, valueLength = %d\n", valueType, valueLength )); DEBUG_LEAVE(error); return error; } #if INET_DEBUG typedef struct { LIST_ENTRY entry; HKEY hkey; char * file; int line; char name[1]; } DBGREGKEYINFO; #if 0 SERIALIZED_LIST DbgRegKeyList; #endif VOID DbgRegKey_Init(VOID) { #if 0 InitializeSerializedList(&DbgRegKeyList); #endif } VOID DbgRegKey_Terminate(VOID) { #if 0 TerminateSerializedList(&DbgRegKeyList); #endif } void regkey_add(const char * name, HKEY hkey, char * file, int line) { #if 0 if (!name) { name = ""; } int len = lstrlen(name); DBGREGKEYINFO * p = (DBGREGKEYINFO *)ALLOCATE_FIXED_MEMORY(sizeof(DBGREGKEYINFO) + len); if (p) { memcpy(p->name, name, len + 1); p->line = line; p->file = file; p->hkey = hkey; InsertAtHeadOfSerializedList(&DbgRegKeyList, &p->entry); } #endif } void regkey_remove(HKEY hkey) { #if 0 LockSerializedList(&DbgRegKeyList); DBGREGKEYINFO * p = (DBGREGKEYINFO *)HeadOfSerializedList(&DbgRegKeyList); while (p != (DBGREGKEYINFO *)SlSelf(&DbgRegKeyList)) { if (p->hkey == hkey) { RemoveFromSerializedList(&DbgRegKeyList, (PLIST_ENTRY)p); FREE_MEMORY(p); break; } p = (DBGREGKEYINFO *)p->entry.Flink; } UnlockSerializedList(&DbgRegKeyList); #endif } #undef NEW_STRING #define NEW_STRING(str) (str) char * regkey_name(HKEY hkey, const char * subname) { switch ((INT_PTR)hkey) { case (INT_PTR)HKEY_CLASSES_ROOT: return NEW_STRING("HKEY_CLASSES_ROOT"); case (INT_PTR)HKEY_CURRENT_USER: return NEW_STRING("HKEY_CURRENT_USER"); case (INT_PTR)HKEY_LOCAL_MACHINE: return NEW_STRING("HKEY_LOCAL_MACHINE"); case (INT_PTR)HKEY_USERS: return NEW_STRING("HKEY_USERS"); case (INT_PTR)HKEY_PERFORMANCE_DATA: return NEW_STRING("HKEY_PERFORMANCE_DATA"); case (INT_PTR)HKEY_CURRENT_CONFIG: return NEW_STRING("HKEY_CURRENT_CONFIG"); case (INT_PTR)HKEY_DYN_DATA: return NEW_STRING("HKEY_DYN_DATA"); } char * name = NULL; #if 0 LockSerializedList(&DbgRegKeyList); DBGREGKEYINFO * p = (DBGREGKEYINFO *)HeadOfSerializedList(&DbgRegKeyList); while (p != (DBGREGKEYINFO *)SlSelf(&DbgRegKeyList)) { if (p->hkey == hkey) { int len = lstrlen(p->name); int slen = lstrlen(subname); name = (char *)ALLOCATE_FIXED_MEMORY(len + 1 + slen + 1); if (name) { memcpy(name, p->name, len); name[len] = '\\'; memcpy(name + len + 1, subname, slen + 1); } break; } p = (DBGREGKEYINFO *)p->entry.Flink; } UnlockSerializedList(&DbgRegKeyList); #endif return name; } void regkey_freename(char * name) { #if 0 if (name) { FREE_MEMORY(name); } #endif } LONG DbgRegOpenKey( IN HKEY hKey, IN LPCTSTR lpszSubKey, OUT PHKEY phkResult, char * file, int line ) { char * keyname = regkey_name(hKey, lpszSubKey); LONG rc = RegOpenKey(hKey, lpszSubKey, phkResult); if (rc == 0) { regkey_add(keyname, *phkResult, file, line); } regkey_freename(keyname); return rc; } LONG DbgRegOpenKeyEx( IN HKEY hKey, IN LPCSTR lpSubKey, IN DWORD ulOptions, IN REGSAM samDesired, OUT PHKEY phkResult, char * file, int line ) { char * keyname = regkey_name(hKey, lpSubKey); LONG rc = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, phkResult); if (rc == 0) { regkey_add(keyname, *phkResult, file, line); } regkey_freename(keyname); return rc; } LONG DbgRegCreateKeyEx( IN HKEY hKey, IN LPCSTR lpSubKey, IN DWORD Reserved, IN LPSTR lpClass, IN DWORD dwOptions, IN REGSAM samDesired, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes, OUT PHKEY phkResult, OUT LPDWORD lpdwDisposition, char * file, int line ) { char * keyname = regkey_name(hKey, lpSubKey); LONG rc = RegCreateKeyEx(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes, phkResult, lpdwDisposition); if (rc == 0) { regkey_add(keyname, *phkResult, file, line); } regkey_freename(keyname); return rc; } LONG DbgRegCloseKey( IN HKEY hKey ) { LONG rc = RegCloseKey(hKey); if (rc == 0) { regkey_remove(hKey); } return rc; } #endif // INET_DEBUG #endif // ENABLE_DEBUG