Source code of Windows XP (NT5)
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.

696 lines
23 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1991 **/
  4. /**********************************************************************/
  5. /*
  6. setvalue.c
  7. Code to enable SetValue for everyone.
  8. history:
  9. terryk 09/30/93 Created
  10. */
  11. #if defined(DEBUG)
  12. static const char szFileName[] = __FILE__;
  13. #define _FILENAME_DEFINED_ONCE szFileName
  14. #endif
  15. #include <string.h>
  16. #include <nt.h>
  17. #include <ntrtl.h>
  18. #include <nturtl.h>
  19. #include <windows.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <nwlsa.h>
  23. #include <nwapi.h>
  24. #include <nwcfg.h>
  25. #include <nwcfg.hxx>
  26. extern char achBuff[];
  27. // exported functions
  28. BOOL FAR PASCAL SetFileSysChangeValue( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
  29. BOOL FAR PASCAL SetEverybodyPermission( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
  30. BOOL FAR PASCAL SetupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
  31. BOOL FAR PASCAL SetupRegistryWorker( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
  32. BOOL FAR PASCAL DeleteGatewayPassword( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
  33. BOOL FAR PASCAL CleanupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
  34. //
  35. // structure for registry munging
  36. //
  37. typedef struct REG_ENTRY_ {
  38. DWORD Operation ;
  39. LONG Level ;
  40. LPWSTR s1 ;
  41. LPWSTR s2 ;
  42. } REG_ENTRY ;
  43. //
  44. // local routines
  45. //
  46. DWORD SetupShellExtensions(REG_ENTRY RegEntries[], DWORD dwNumEntries) ;
  47. typedef DWORD (*LPNWCLEANUPGATEWAYSHARES)(VOID) ;
  48. // Values & Tables that define registry data
  49. #define MAX_REG_LEVEL 10
  50. #define CREATE_ABS 1 // create/open a key with absolute path
  51. #define CREATE_REL 2 // create/open a key with relative path
  52. #define VALUE_STR 3 // write a string value
  53. #define DELETE_ABS 4 // delete key with absolute path
  54. #define DELETE_REL 5 // delete key with relative path
  55. #define DELETE_VAL 6 // delete a value
  56. #define DROP_STACK 7 // drop stack by one
  57. REG_ENTRY RegCreateEntries[] =
  58. {
  59. {CREATE_ABS,0,L"SOFTWARE\\Classes\\NetWare_or_Compatible_Network", NULL},
  60. {DELETE_REL,0,L"shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
  61. {DELETE_REL,0,L"shellex\\ContextMenuHandlers", NULL},
  62. {DELETE_REL,0,L"shellex\\PropertySheetHandlers\\NetWarePage", NULL},
  63. {DELETE_REL,0,L"shellex\\PropertySheetHandlers", NULL},
  64. {DELETE_REL,0,L"shellex", NULL},
  65. {DROP_STACK,0,NULL,NULL},
  66. {DELETE_ABS,0,L"SOFTWARE\\Classes\\NetWare_or_Compatible_Network", NULL},
  67. {CREATE_ABS, 0,L"SOFTWARE\\Classes\\Network\\Type", NULL},
  68. {CREATE_REL,+1, L"3", NULL},
  69. {CREATE_REL,+1, L"shellex", NULL},
  70. {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
  71. {CREATE_REL,+1, L"NetWareMenus", NULL},
  72. {VALUE_STR,0, L"", L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}"},
  73. {CREATE_REL,-1, L"PropertySheetHandlers", NULL},
  74. {CREATE_REL,+1, L"NetWarePage", NULL},
  75. {VALUE_STR,0, L"", L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}"},
  76. {CREATE_ABS, 0,L"SOFTWARE\\Classes\\CLSID", NULL},
  77. {CREATE_REL,+1, L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
  78. {VALUE_STR,0, L"", L"NetWare Objects"},
  79. {CREATE_REL,+1, L"InProcServer32", NULL},
  80. {VALUE_STR,0, L"", L"nwprovau.dll"},
  81. {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
  82. {CREATE_REL,-1, L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
  83. {VALUE_STR,0, L"", L"NetWare UNC Folder Menu"},
  84. {CREATE_REL,+1, L"InProcServer32", NULL},
  85. {VALUE_STR,0, L"", L"nwprovau.dll"},
  86. {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
  87. {CREATE_REL,-1, L"{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL},
  88. {VALUE_STR,0, L"", L"NetWare Hood Verbs"},
  89. {CREATE_REL,+1, L"InProcServer32", NULL},
  90. {VALUE_STR,0, L"", L"nwprovau.dll"},
  91. {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
  92. {CREATE_REL,-1, L"{208D2C60-3AEA-1069-A2D7-08002B30309D}", NULL},
  93. {CREATE_REL,+1, L"shellex", NULL},
  94. {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
  95. {CREATE_REL,+1, L"NetWareMenus", NULL},
  96. {VALUE_STR,0, L"", L"{52c68510-09a0-11cf-8daa-00aa004a5691}"},
  97. {CREATE_ABS, 0,L"SOFTWARE\\Classes\\Folder", NULL},
  98. {CREATE_REL,+1, L"shellex", NULL},
  99. {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
  100. {CREATE_REL,+1, L"NetWareUNCMenu", NULL},
  101. {VALUE_STR,0, L"", L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}"},
  102. {CREATE_ABS, 0,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", NULL},
  103. {CREATE_REL,+1, L"Shell Extensions", NULL},
  104. {CREATE_REL,+1, L"Approved", NULL},
  105. {VALUE_STR,0, L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", L"Shell extensions for NetWare"},
  106. {VALUE_STR,0, L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", L"Shell extensions for NetWare"},
  107. {VALUE_STR,0, L"{52c68510-09a0-11cf-8daa-00aa004a5691}", L"Shell extensions for NetWare"}
  108. } ;
  109. REG_ENTRY RegDeleteEntries[] =
  110. {
  111. {CREATE_ABS,0,L"SOFTWARE\\Classes\\Network\\Type\\3", NULL},
  112. {DELETE_REL,0,L"shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
  113. {DELETE_REL,0,L"shellex\\ContextMenuHandlers", NULL},
  114. {DELETE_REL,0,L"shellex\\PropertySheetHandlers\\NetWarePage", NULL},
  115. {DELETE_REL,0,L"shellex\\PropertySheetHandlers", NULL},
  116. {DELETE_REL,0,L"shellex", NULL},
  117. {DROP_STACK,0,NULL,NULL},
  118. {DELETE_ABS,0,L"SOFTWARE\\Classes\\Network\\Type\\3", NULL},
  119. {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{8e9d6600-f84a-11ce-8daa-00aa004a5691}\\InProcServer32", NULL},
  120. {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
  121. {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{e3f2bac0-099f-11cf-8daa-00aa004a5691}\\InProcServer32", NULL},
  122. {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
  123. {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{52c68510-09a0-11cf-8daa-00aa004a5691}\\InProcServer32", NULL},
  124. {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL},
  125. {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{208D2C60-3AEA-1069-A2D7-08002B30309D}\\shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
  126. {DELETE_ABS,0,L"SOFTWARE\\Classes\\Folder\\shellex\\ContextMenuHandlers\\NetWareUNCMenu", NULL},
  127. {CREATE_ABS,0,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", NULL},
  128. {DELETE_VAL,0,L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
  129. {DELETE_VAL,0,L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
  130. {DELETE_VAL,0,L"{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL}
  131. } ;
  132. /*******************************************************************
  133. NAME: SetEverybodyPermission
  134. SYNOPSIS: Set the registry key to everybody "Set Value" (or whatever
  135. the caller want.) This is called from the inf file
  136. ENTRY: Registry key as the first parameter
  137. Permisstion type as the second parameter
  138. RETURN: BOOL - TRUE for success.
  139. HISTORY:
  140. terryk 07-May-1993 Created
  141. ********************************************************************/
  142. typedef DWORD (*T_SetPermission)(HKEY hKey, DWORD dwPermission);
  143. BOOL FAR PASCAL SetEverybodyPermission( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
  144. {
  145. HKEY hKey = (HKEY)atol( &(apszArgs[0][1]) ); // registry key
  146. DWORD dwPermission = atol( apszArgs[1] ); // permission value
  147. DWORD err = ERROR_SUCCESS;
  148. do {
  149. HINSTANCE hDll = LoadLibraryA( "nwapi32.dll" );
  150. FARPROC pSetPermission = NULL;
  151. if ( hDll == NULL )
  152. {
  153. err = GetLastError();
  154. break;
  155. }
  156. pSetPermission = GetProcAddress( hDll, "NwLibSetEverybodyPermission" );
  157. if ( pSetPermission == NULL )
  158. {
  159. err = GetLastError();
  160. break;
  161. }
  162. err = (*(T_SetPermission)pSetPermission)( hKey, dwPermission );
  163. } while ( FALSE );
  164. wsprintfA( achBuff, "{\"%d\"}", err );
  165. *ppszResult = achBuff;
  166. return( err == ERROR_SUCCESS );
  167. }
  168. /*******************************************************************
  169. NAME: SetFileSysChangeValue
  170. SYNOPSIS: calls common setup routine. this old entry point is
  171. is left here to handle any DLL/INF mismatch.
  172. ENTRY: NONE from inf file.
  173. RETURN: BOOL - TRUE for success.
  174. (always return TRUE)
  175. HISTORY:
  176. chuckc 29-Oct-1993 Created
  177. ********************************************************************/
  178. BOOL FAR PASCAL SetFileSysChangeValue( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
  179. {
  180. return SetupRegistryWorker( nArgs, apszArgs, ppszResult );
  181. }
  182. /*******************************************************************
  183. NAME: SetupRegistryForNWCS
  184. SYNOPSIS: calls common worker routine to setup registry.
  185. ENTRY: NONE from inf file.
  186. RETURN: BOOL - TRUE for success.
  187. (always return TRUE)
  188. HISTORY:
  189. chuckc 29-Oct-1993 Created
  190. ********************************************************************/
  191. BOOL FAR PASCAL SetupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
  192. {
  193. return SetupRegistryWorker( nArgs, apszArgs, ppszResult );
  194. }
  195. /*******************************************************************
  196. NAME: SetupRegistryWorker
  197. SYNOPSIS: set the FileSysChangeValue to please NETWARE.DRV.
  198. also set win.ini parameter so wfwnet.drv knows we are there.
  199. ENTRY: NONE from inf file.
  200. RETURN: BOOL - TRUE for success.
  201. (always return TRUE)
  202. HISTORY:
  203. chuckc 29-Oct-1993 Created
  204. ********************************************************************/
  205. BOOL FAR PASCAL SetupRegistryWorker( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
  206. {
  207. DWORD err = 0, err1 = 0 ;
  208. (void) nArgs ; // quiet the compiler
  209. (void) apszArgs ; // quiet the compiler
  210. if (!WriteProfileStringA("NWCS",
  211. "NwcsInstalled",
  212. "1"))
  213. {
  214. err = GetLastError() ;
  215. }
  216. if (!WritePrivateProfileStringA("386Enh",
  217. "FileSysChange",
  218. "off",
  219. "system.ini"))
  220. {
  221. err1 = GetLastError() ;
  222. }
  223. if (err1 == NO_ERROR)
  224. {
  225. err1 = SetupShellExtensions(
  226. RegCreateEntries,
  227. sizeof(RegCreateEntries)/sizeof(RegCreateEntries[0])) ;
  228. }
  229. wsprintfA( achBuff, "{\"%d\"}", err ? err : err1 );
  230. *ppszResult = achBuff;
  231. return TRUE;
  232. }
  233. #define NWCLEANUPGATEWAYSHARES_NAME "NwCleanupGatewayShares"
  234. #define NWPROVAU_DLL_NAME L"NWPROVAU"
  235. /*******************************************************************
  236. NAME: DeleteGatewayPassword
  237. SYNOPSIS: delete the LSA secret used for gateway password.
  238. also clears the NWCS installed bit. INF will be
  239. changed to call CleanupRegistryForNWCS, but this entry
  240. point is left here to handle DLL/INF mismatch.
  241. ENTRY: NONE from inf file.
  242. RETURN: BOOL - TRUE for success.
  243. (always return TRUE)
  244. HISTORY:
  245. chuckc 29-Oct-1993 Created
  246. ********************************************************************/
  247. BOOL FAR PASCAL
  248. DeleteGatewayPassword(
  249. DWORD nArgs,
  250. LPSTR apszArgs[],
  251. LPSTR * ppszResult
  252. )
  253. {
  254. return TRUE ; // work is done in cleanup below which does everything.
  255. }
  256. /*******************************************************************
  257. NAME: CleanupRegistryForNWCS
  258. SYNOPSIS: delete the LSA secret used for gateway password.
  259. also set flag that NWCS has been removed. this flag
  260. is used by wfwnet.drv.
  261. ENTRY: NONE from inf file.
  262. RETURN: BOOL - TRUE for success.
  263. (always return TRUE)
  264. HISTORY:
  265. chuckc 29-Oct-1993 Created
  266. ********************************************************************/
  267. BOOL FAR PASCAL
  268. CleanupRegistryForNWCS(
  269. DWORD nArgs,
  270. LPSTR apszArgs[],
  271. LPSTR * ppszResult
  272. )
  273. {
  274. HANDLE hDll ;
  275. DWORD err = 0, err1 = 0 ;
  276. LPNWCLEANUPGATEWAYSHARES lpfnNwCleanupGatewayShares = NULL ;
  277. (void) nArgs ; // quiet the compiler
  278. (void) apszArgs ; // quiet the compiler
  279. if (!WriteProfileStringA("NWCS",
  280. "NwcsInstalled",
  281. "0"))
  282. {
  283. err = GetLastError() ;
  284. }
  285. err1 = NwDeletePassword(GATEWAY_USER) ;
  286. if (!err)
  287. err = err1 ;
  288. if ((hDll = LoadLibraryW(NWPROVAU_DLL_NAME)) &&
  289. (lpfnNwCleanupGatewayShares = (LPNWCLEANUPGATEWAYSHARES)
  290. GetProcAddress(hDll, NWCLEANUPGATEWAYSHARES_NAME)))
  291. {
  292. err1 = (*lpfnNwCleanupGatewayShares)() ;
  293. (void) FreeLibrary(hDll) ;
  294. }
  295. //
  296. // ignore errors for this.
  297. //
  298. (void) SetupShellExtensions(
  299. RegDeleteEntries,
  300. sizeof(RegDeleteEntries)/sizeof(RegDeleteEntries[0])) ;
  301. if (!err)
  302. err = err1 ;
  303. wsprintfA( achBuff, "{\"%d\"}", err );
  304. *ppszResult = achBuff;
  305. return TRUE;
  306. }
  307. /*******************************************************************
  308. NAME: SetupShellExtensions
  309. SYNOPSIS: setup the registry for shell extensions. function is driven
  310. by a table of entries (RegEntries). for each entry there is a
  311. Operation code that tells us what we are doing. key entries can
  312. be created absolute or relative to previous positions, so we
  313. maintain a stack of registry handles for the latter case. every
  314. key that is created is initially put on the stack. values
  315. are always written based on the 'current stack' position.
  316. ENTRY: NONE
  317. RETURN: Win32 error code
  318. HISTORY:
  319. chuckc 29-Nov-1995 Created
  320. ********************************************************************/
  321. DWORD SetupShellExtensions(REG_ENTRY RegEntries[], DWORD dwNumEntries)
  322. {
  323. DWORD err, errClose, dwDisposition, i ;
  324. HKEY hKey, RegHandleStack[MAX_REG_LEVEL] ;
  325. LONG StackIndex = -1 ;
  326. //
  327. // Loop thru and for each entry. Then switch & do the appropriate
  328. // operation in the registry.
  329. //
  330. for (i = 0; i < dwNumEntries; i++)
  331. {
  332. err = NO_ERROR ;
  333. switch (RegEntries[i].Operation)
  334. {
  335. case CREATE_ABS:
  336. //
  337. // create/open a reg key with an absolute path. since this
  338. // is absolute, we drop everything on the stack, and start
  339. // all over again.
  340. //
  341. while (StackIndex >= 0)
  342. {
  343. errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
  344. ASSERT(errClose == NO_ERROR) ;
  345. }
  346. err = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
  347. RegEntries[i].s1, // subkey
  348. 0, // reserved
  349. NULL, // class
  350. REG_OPTION_NON_VOLATILE,
  351. KEY_ALL_ACCESS,
  352. NULL, // default security
  353. &hKey,
  354. &dwDisposition) ; // not used
  355. if (err != NO_ERROR)
  356. {
  357. break ;
  358. }
  359. //
  360. // by default we advance the stack. no need check for overflow
  361. // as the stack is empty.
  362. //
  363. RegHandleStack[++StackIndex] = hKey ;
  364. break ;
  365. case CREATE_REL:
  366. //
  367. // create/open a reg key relative to current stack. make sure
  368. // there is something on the stack (check StackIndex >= 0).
  369. // then see if we are advancing (+1), staying the same (0) or
  370. // dropping back (-ve).
  371. //
  372. if (StackIndex < 0)
  373. {
  374. err = ERROR_INVALID_FUNCTION ;
  375. break ;
  376. }
  377. if (RegEntries[i].Level == +1)
  378. {
  379. //
  380. // opening next level down. continue as is and use
  381. // most recently opened key as the starting point.
  382. //
  383. }
  384. else if (RegEntries[i].Level == 0)
  385. {
  386. //
  387. // opening at same level as last time. so we are done
  388. // with the last key. what we want to do is close it
  389. // and use the parent.
  390. //
  391. errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
  392. ASSERT(errClose == NO_ERROR) ;
  393. if (StackIndex < 0)
  394. {
  395. err = ERROR_INVALID_FUNCTION ;
  396. break ;
  397. }
  398. }
  399. else if (RegEntries[i].Level < 0)
  400. {
  401. //
  402. // dropping back & opening at a higher level. cleanup
  403. // handle for each level we pop.
  404. //
  405. LONG Count = RegEntries[i].Level ;
  406. while (Count++ < 1)
  407. {
  408. errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
  409. ASSERT(errClose == NO_ERROR) ;
  410. if (StackIndex < -1)
  411. {
  412. err = ERROR_INVALID_FUNCTION ;
  413. break ;
  414. }
  415. }
  416. }
  417. else
  418. {
  419. //
  420. // only -ve numbers, 0 and 1 are valid
  421. //
  422. err = ERROR_INVALID_FUNCTION ;
  423. break ;
  424. }
  425. //
  426. // create key relative to current point
  427. //
  428. err = RegCreateKeyExW(RegHandleStack[StackIndex], // current key
  429. RegEntries[i].s1, // subkey
  430. 0, // reserved
  431. NULL, // class
  432. REG_OPTION_NON_VOLATILE,
  433. KEY_ALL_ACCESS,
  434. NULL, // default security
  435. &hKey,
  436. &dwDisposition) ; // not used
  437. if (err != NO_ERROR)
  438. {
  439. break ;
  440. }
  441. //
  442. // by default we advance the stack
  443. //
  444. RegHandleStack[++StackIndex] = hKey ;
  445. if (StackIndex >= MAX_REG_LEVEL)
  446. {
  447. err = ERROR_INVALID_FUNCTION ;
  448. break ;
  449. }
  450. break ;
  451. case VALUE_STR:
  452. //
  453. // create a REG_SZ value at current point. check we have
  454. // handle on stack.
  455. //
  456. if (StackIndex < 0)
  457. {
  458. err = ERROR_INVALID_FUNCTION ;
  459. break ;
  460. }
  461. err = RegSetValueExW(
  462. RegHandleStack[StackIndex], // current key
  463. RegEntries[i].s1, // value name
  464. 0, // reserved
  465. REG_SZ,
  466. (BYTE *)RegEntries[i].s2, // value data
  467. (wcslen(RegEntries[i].s2)+1)*sizeof(WCHAR)) ;
  468. break ;
  469. case DELETE_ABS:
  470. //
  471. // delete a key (absolute). no change to stack.
  472. //
  473. err = RegDeleteKeyW(HKEY_LOCAL_MACHINE,
  474. RegEntries[i].s1) ; // subkey
  475. if ( err == ERROR_FILE_NOT_FOUND )
  476. err = NO_ERROR;
  477. break ;
  478. case DELETE_REL:
  479. //
  480. // delete a key (relative). no change to stack.
  481. //
  482. if (StackIndex < 0)
  483. {
  484. err = ERROR_INVALID_FUNCTION ;
  485. break ;
  486. }
  487. err = RegDeleteKeyW(RegHandleStack[StackIndex], // current key
  488. RegEntries[i].s1) ; // subkey
  489. if ( err == ERROR_FILE_NOT_FOUND )
  490. err = NO_ERROR;
  491. break ;
  492. case DELETE_VAL:
  493. //
  494. // delete value at current point. check we have handle on stack.
  495. //
  496. if (StackIndex < 0)
  497. {
  498. err = ERROR_INVALID_FUNCTION ;
  499. break ;
  500. }
  501. err = RegDeleteValueW(RegHandleStack[StackIndex], // current key
  502. RegEntries[i].s1) ; // value name
  503. break ;
  504. case DROP_STACK:
  505. //
  506. // drop current stack by one (closing the handle).
  507. //
  508. if (StackIndex < 0)
  509. {
  510. err = ERROR_INVALID_FUNCTION ;
  511. break ;
  512. }
  513. errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
  514. ASSERT(errClose == NO_ERROR) ;
  515. break ;
  516. default:
  517. //
  518. // error out if unknown operation
  519. //
  520. err = ERROR_INVALID_FUNCTION ;
  521. break ;
  522. }
  523. if (err != NO_ERROR)
  524. {
  525. break ;
  526. }
  527. }
  528. //
  529. // cleanup open handles on the stack
  530. //
  531. while (StackIndex >= 0)
  532. {
  533. errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
  534. ASSERT(errClose == NO_ERROR) ;
  535. }
  536. return err ;
  537. }