summaryrefslogtreecommitdiffstats
path: root/private/nw/install/setupdll/setvalue.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/install/setupdll/setvalue.c')
-rw-r--r--private/nw/install/setupdll/setvalue.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/private/nw/install/setupdll/setvalue.c b/private/nw/install/setupdll/setvalue.c
new file mode 100644
index 000000000..bfaf073cf
--- /dev/null
+++ b/private/nw/install/setupdll/setvalue.c
@@ -0,0 +1,696 @@
+/**********************************************************************/
+/** Microsoft Windows NT **/
+/** Copyright(c) Microsoft Corp., 1991 **/
+/**********************************************************************/
+
+/*
+
+ setvalue.c
+ Code to enable SetValue for everyone.
+
+ history:
+ terryk 09/30/93 Created
+*/
+
+
+#if defined(DEBUG)
+static const char szFileName[] = __FILE__;
+#define _FILENAME_DEFINED_ONCE szFileName
+#endif
+
+#include <string.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nwlsa.h>
+#include <nwapi.h>
+#include <nwcfg.h>
+#include <nwcfg.hxx>
+
+extern char achBuff[];
+
+// exported functions
+
+BOOL FAR PASCAL SetFileSysChangeValue( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL SetEverybodyPermission( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL SetupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL SetupRegistryWorker( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL DeleteGatewayPassword( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL CleanupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+
+//
+// structure for registry munging
+//
+
+typedef struct REG_ENTRY_ {
+ DWORD Operation ;
+ LONG Level ;
+ LPWSTR s1 ;
+ LPWSTR s2 ;
+} REG_ENTRY ;
+
+//
+// local routines
+//
+
+DWORD SetupShellExtensions(REG_ENTRY RegEntries[], DWORD dwNumEntries) ;
+
+typedef DWORD (*LPNWCLEANUPGATEWAYSHARES)(VOID) ;
+
+// Values & Tables that define registry data
+
+#define MAX_REG_LEVEL 10
+
+#define CREATE_ABS 1 // create/open a key with absolute path
+#define CREATE_REL 2 // create/open a key with relative path
+#define VALUE_STR 3 // write a string value
+#define DELETE_ABS 4 // delete key with absolute path
+#define DELETE_REL 5 // delete key with relative path
+#define DELETE_VAL 6 // delete a value
+#define DROP_STACK 7 // drop stack by one
+
+REG_ENTRY RegCreateEntries[] =
+{
+ {CREATE_ABS,0,L"SOFTWARE\\Classes\\NetWare_or_Compatible_Network", NULL},
+ {DELETE_REL,0,L"shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
+ {DELETE_REL,0,L"shellex\\ContextMenuHandlers", NULL},
+ {DELETE_REL,0,L"shellex\\PropertySheetHandlers\\NetWarePage", NULL},
+ {DELETE_REL,0,L"shellex\\PropertySheetHandlers", NULL},
+ {DELETE_REL,0,L"shellex", NULL},
+ {DROP_STACK,0,NULL,NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\NetWare_or_Compatible_Network", NULL},
+
+ {CREATE_ABS, 0,L"SOFTWARE\\Classes\\Network\\Type", NULL},
+ {CREATE_REL,+1, L"3", NULL},
+ {CREATE_REL,+1, L"shellex", NULL},
+ {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
+ {CREATE_REL,+1, L"NetWareMenus", NULL},
+ {VALUE_STR,0, L"", L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}"},
+ {CREATE_REL,-1, L"PropertySheetHandlers", NULL},
+ {CREATE_REL,+1, L"NetWarePage", NULL},
+ {VALUE_STR,0, L"", L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}"},
+ {CREATE_ABS, 0,L"SOFTWARE\\Classes\\CLSID", NULL},
+ {CREATE_REL,+1, L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
+ {VALUE_STR,0, L"", L"NetWare Objects"},
+ {CREATE_REL,+1, L"InProcServer32", NULL},
+ {VALUE_STR,0, L"", L"nwprovau.dll"},
+ {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
+ {CREATE_REL,-1, L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
+ {VALUE_STR,0, L"", L"NetWare UNC Folder Menu"},
+ {CREATE_REL,+1, L"InProcServer32", NULL},
+ {VALUE_STR,0, L"", L"nwprovau.dll"},
+ {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
+ {CREATE_REL,-1, L"{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL},
+ {VALUE_STR,0, L"", L"NetWare Hood Verbs"},
+ {CREATE_REL,+1, L"InProcServer32", NULL},
+ {VALUE_STR,0, L"", L"nwprovau.dll"},
+ {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
+ {CREATE_REL,-1, L"{208D2C60-3AEA-1069-A2D7-08002B30309D}", NULL},
+ {CREATE_REL,+1, L"shellex", NULL},
+ {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
+ {CREATE_REL,+1, L"NetWareMenus", NULL},
+ {VALUE_STR,0, L"", L"{52c68510-09a0-11cf-8daa-00aa004a5691}"},
+ {CREATE_ABS, 0,L"SOFTWARE\\Classes\\Folder", NULL},
+ {CREATE_REL,+1, L"shellex", NULL},
+ {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
+ {CREATE_REL,+1, L"NetWareUNCMenu", NULL},
+ {VALUE_STR,0, L"", L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}"},
+ {CREATE_ABS, 0,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", NULL},
+ {CREATE_REL,+1, L"Shell Extensions", NULL},
+ {CREATE_REL,+1, L"Approved", NULL},
+ {VALUE_STR,0, L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", L"Shell extensions for NetWare"},
+ {VALUE_STR,0, L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", L"Shell extensions for NetWare"},
+ {VALUE_STR,0, L"{52c68510-09a0-11cf-8daa-00aa004a5691}", L"Shell extensions for NetWare"}
+} ;
+
+REG_ENTRY RegDeleteEntries[] =
+{
+ {CREATE_ABS,0,L"SOFTWARE\\Classes\\Network\\Type\\3", NULL},
+ {DELETE_REL,0,L"shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
+ {DELETE_REL,0,L"shellex\\ContextMenuHandlers", NULL},
+ {DELETE_REL,0,L"shellex\\PropertySheetHandlers\\NetWarePage", NULL},
+ {DELETE_REL,0,L"shellex\\PropertySheetHandlers", NULL},
+ {DELETE_REL,0,L"shellex", NULL},
+ {DROP_STACK,0,NULL,NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\Network\\Type\\3", NULL},
+
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{8e9d6600-f84a-11ce-8daa-00aa004a5691}\\InProcServer32", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{e3f2bac0-099f-11cf-8daa-00aa004a5691}\\InProcServer32", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{52c68510-09a0-11cf-8daa-00aa004a5691}\\InProcServer32", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{208D2C60-3AEA-1069-A2D7-08002B30309D}\\shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
+
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\Folder\\shellex\\ContextMenuHandlers\\NetWareUNCMenu", NULL},
+ {CREATE_ABS,0,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", NULL},
+ {DELETE_VAL,0,L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
+ {DELETE_VAL,0,L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
+ {DELETE_VAL,0,L"{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL}
+} ;
+
+
+/*******************************************************************
+
+ NAME: SetEverybodyPermission
+
+ SYNOPSIS: Set the registry key to everybody "Set Value" (or whatever
+ the caller want.) This is called from the inf file
+
+ ENTRY: Registry key as the first parameter
+ Permisstion type as the second parameter
+
+ RETURN: BOOL - TRUE for success.
+
+ HISTORY:
+ terryk 07-May-1993 Created
+
+********************************************************************/
+
+typedef DWORD (*T_SetPermission)(HKEY hKey, DWORD dwPermission);
+
+BOOL FAR PASCAL SetEverybodyPermission( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ HKEY hKey = (HKEY)atol( &(apszArgs[0][1]) ); // registry key
+ DWORD dwPermission = atol( apszArgs[1] ); // permission value
+ DWORD err = ERROR_SUCCESS;
+
+ do {
+ HINSTANCE hDll = LoadLibraryA( "nwapi32.dll" );
+ FARPROC pSetPermission = NULL;
+
+ if ( hDll == NULL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ pSetPermission = GetProcAddress( hDll, "NwLibSetEverybodyPermission" );
+
+ if ( pSetPermission == NULL )
+ {
+ err = GetLastError();
+ break;
+ }
+ err = (*(T_SetPermission)pSetPermission)( hKey, dwPermission );
+ } while ( FALSE );
+
+ wsprintfA( achBuff, "{\"%d\"}", err );
+ *ppszResult = achBuff;
+
+ return( err == ERROR_SUCCESS );
+}
+
+/*******************************************************************
+
+ NAME: SetFileSysChangeValue
+
+ SYNOPSIS: calls common setup routine. this old entry point is
+ is left here to handle any DLL/INF mismatch.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL SetFileSysChangeValue( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ return SetupRegistryWorker( nArgs, apszArgs, ppszResult );
+}
+
+/*******************************************************************
+
+ NAME: SetupRegistryForNWCS
+
+ SYNOPSIS: calls common worker routine to setup registry.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL SetupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ return SetupRegistryWorker( nArgs, apszArgs, ppszResult );
+}
+
+/*******************************************************************
+
+ NAME: SetupRegistryWorker
+
+ SYNOPSIS: set the FileSysChangeValue to please NETWARE.DRV.
+ also set win.ini parameter so wfwnet.drv knows we are there.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL SetupRegistryWorker( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ DWORD err = 0, err1 = 0 ;
+
+ (void) nArgs ; // quiet the compiler
+ (void) apszArgs ; // quiet the compiler
+
+ if (!WriteProfileStringA("NWCS",
+ "NwcsInstalled",
+ "1"))
+ {
+ err = GetLastError() ;
+ }
+
+ if (!WritePrivateProfileStringA("386Enh",
+ "FileSysChange",
+ "off",
+ "system.ini"))
+ {
+ err1 = GetLastError() ;
+ }
+
+ if (err1 == NO_ERROR)
+ {
+ err1 = SetupShellExtensions(
+ RegCreateEntries,
+ sizeof(RegCreateEntries)/sizeof(RegCreateEntries[0])) ;
+ }
+
+ wsprintfA( achBuff, "{\"%d\"}", err ? err : err1 );
+ *ppszResult = achBuff;
+
+ return TRUE;
+}
+
+#define NWCLEANUPGATEWAYSHARES_NAME "NwCleanupGatewayShares"
+#define NWPROVAU_DLL_NAME L"NWPROVAU"
+
+/*******************************************************************
+
+ NAME: DeleteGatewayPassword
+
+ SYNOPSIS: delete the LSA secret used for gateway password.
+ also clears the NWCS installed bit. INF will be
+ changed to call CleanupRegistryForNWCS, but this entry
+ point is left here to handle DLL/INF mismatch.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL
+DeleteGatewayPassword(
+ DWORD nArgs,
+ LPSTR apszArgs[],
+ LPSTR * ppszResult
+ )
+{
+ return TRUE ; // work is done in cleanup below which does everything.
+}
+
+/*******************************************************************
+
+ NAME: CleanupRegistryForNWCS
+
+ SYNOPSIS: delete the LSA secret used for gateway password.
+ also set flag that NWCS has been removed. this flag
+ is used by wfwnet.drv.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL
+CleanupRegistryForNWCS(
+ DWORD nArgs,
+ LPSTR apszArgs[],
+ LPSTR * ppszResult
+ )
+{
+ HANDLE hDll ;
+ DWORD err = 0, err1 = 0 ;
+ LPNWCLEANUPGATEWAYSHARES lpfnNwCleanupGatewayShares = NULL ;
+
+ (void) nArgs ; // quiet the compiler
+ (void) apszArgs ; // quiet the compiler
+
+ if (!WriteProfileStringA("NWCS",
+ "NwcsInstalled",
+ "0"))
+ {
+ err = GetLastError() ;
+ }
+
+ err1 = NwDeletePassword(GATEWAY_USER) ;
+
+ if (!err)
+ err = err1 ;
+
+ if ((hDll = LoadLibraryW(NWPROVAU_DLL_NAME)) &&
+ (lpfnNwCleanupGatewayShares = (LPNWCLEANUPGATEWAYSHARES)
+ GetProcAddress(hDll, NWCLEANUPGATEWAYSHARES_NAME)))
+ {
+ err1 = (*lpfnNwCleanupGatewayShares)() ;
+ (void) FreeLibrary(hDll) ;
+ }
+
+ //
+ // ignore errors for this.
+ //
+ (void) SetupShellExtensions(
+ RegDeleteEntries,
+ sizeof(RegDeleteEntries)/sizeof(RegDeleteEntries[0])) ;
+
+ if (!err)
+ err = err1 ;
+
+ wsprintfA( achBuff, "{\"%d\"}", err );
+ *ppszResult = achBuff;
+
+ return TRUE;
+}
+
+/*******************************************************************
+
+ NAME: SetupShellExtensions
+
+ SYNOPSIS: setup the registry for shell extensions. function is driven
+ by a table of entries (RegEntries). for each entry there is a
+ Operation code that tells us what we are doing. key entries can
+ be created absolute or relative to previous positions, so we
+ maintain a stack of registry handles for the latter case. every
+ key that is created is initially put on the stack. values
+ are always written based on the 'current stack' position.
+
+ ENTRY: NONE
+
+ RETURN: Win32 error code
+
+ HISTORY:
+ chuckc 29-Nov-1995 Created
+
+********************************************************************/
+DWORD SetupShellExtensions(REG_ENTRY RegEntries[], DWORD dwNumEntries)
+{
+ DWORD err, errClose, dwDisposition, i ;
+ HKEY hKey, RegHandleStack[MAX_REG_LEVEL] ;
+ LONG StackIndex = -1 ;
+
+ //
+ // Loop thru and for each entry. Then switch & do the appropriate
+ // operation in the registry.
+ //
+
+ for (i = 0; i < dwNumEntries; i++)
+ {
+ err = NO_ERROR ;
+
+ switch (RegEntries[i].Operation)
+ {
+ case CREATE_ABS:
+
+ //
+ // create/open a reg key with an absolute path. since this
+ // is absolute, we drop everything on the stack, and start
+ // all over again.
+ //
+
+ while (StackIndex >= 0)
+ {
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+ ASSERT(errClose == NO_ERROR) ;
+ }
+
+ err = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
+ RegEntries[i].s1, // subkey
+ 0, // reserved
+ NULL, // class
+ REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS,
+ NULL, // default security
+ &hKey,
+ &dwDisposition) ; // not used
+ if (err != NO_ERROR)
+ {
+ break ;
+ }
+
+ //
+ // by default we advance the stack. no need check for overflow
+ // as the stack is empty.
+ //
+
+ RegHandleStack[++StackIndex] = hKey ;
+ break ;
+
+ case CREATE_REL:
+
+ //
+ // create/open a reg key relative to current stack. make sure
+ // there is something on the stack (check StackIndex >= 0).
+ // then see if we are advancing (+1), staying the same (0) or
+ // dropping back (-ve).
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ if (RegEntries[i].Level == +1)
+ {
+ //
+ // opening next level down. continue as is and use
+ // most recently opened key as the starting point.
+ //
+ }
+ else if (RegEntries[i].Level == 0)
+ {
+ //
+ // opening at same level as last time. so we are done
+ // with the last key. what we want to do is close it
+ // and use the parent.
+ //
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+
+ ASSERT(errClose == NO_ERROR) ;
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+ }
+ else if (RegEntries[i].Level < 0)
+ {
+ //
+ // dropping back & opening at a higher level. cleanup
+ // handle for each level we pop.
+ //
+
+ LONG Count = RegEntries[i].Level ;
+
+ while (Count++ < 1)
+ {
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+
+ ASSERT(errClose == NO_ERROR) ;
+
+ if (StackIndex < -1)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+ }
+ }
+ else
+ {
+ //
+ // only -ve numbers, 0 and 1 are valid
+ //
+
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ //
+ // create key relative to current point
+ //
+ err = RegCreateKeyExW(RegHandleStack[StackIndex], // current key
+ RegEntries[i].s1, // subkey
+ 0, // reserved
+ NULL, // class
+ REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS,
+ NULL, // default security
+ &hKey,
+ &dwDisposition) ; // not used
+ if (err != NO_ERROR)
+ {
+ break ;
+ }
+
+ //
+ // by default we advance the stack
+ //
+
+ RegHandleStack[++StackIndex] = hKey ;
+
+ if (StackIndex >= MAX_REG_LEVEL)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ break ;
+
+ case VALUE_STR:
+
+ //
+ // create a REG_SZ value at current point. check we have
+ // handle on stack.
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ err = RegSetValueExW(
+ RegHandleStack[StackIndex], // current key
+ RegEntries[i].s1, // value name
+ 0, // reserved
+ REG_SZ,
+ (BYTE *)RegEntries[i].s2, // value data
+ (wcslen(RegEntries[i].s2)+1)*sizeof(WCHAR)) ;
+ break ;
+
+ case DELETE_ABS:
+
+ //
+ // delete a key (absolute). no change to stack.
+ //
+
+ err = RegDeleteKeyW(HKEY_LOCAL_MACHINE,
+ RegEntries[i].s1) ; // subkey
+
+ if ( err == ERROR_FILE_NOT_FOUND )
+ err = NO_ERROR;
+
+ break ;
+
+ case DELETE_REL:
+
+ //
+ // delete a key (relative). no change to stack.
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ err = RegDeleteKeyW(RegHandleStack[StackIndex], // current key
+ RegEntries[i].s1) ; // subkey
+
+ if ( err == ERROR_FILE_NOT_FOUND )
+ err = NO_ERROR;
+
+ break ;
+
+ case DELETE_VAL:
+
+ //
+ // delete value at current point. check we have handle on stack.
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ err = RegDeleteValueW(RegHandleStack[StackIndex], // current key
+ RegEntries[i].s1) ; // value name
+ break ;
+
+ case DROP_STACK:
+
+ //
+ // drop current stack by one (closing the handle).
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+
+ ASSERT(errClose == NO_ERROR) ;
+
+ break ;
+
+ default:
+
+ //
+ // error out if unknown operation
+ //
+
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ if (err != NO_ERROR)
+ {
+ break ;
+ }
+ }
+
+ //
+ // cleanup open handles on the stack
+ //
+
+ while (StackIndex >= 0)
+ {
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+ ASSERT(errClose == NO_ERROR) ;
+ }
+
+ return err ;
+}