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.

719 lines
22 KiB

  1. /*===================================================================
  2. Microsoft IIS Active Server Pages
  3. Microsoft Confidential.
  4. Copyright 1997 Microsoft Corporation. All Rights Reserved.
  5. Component: Registry stuff
  6. File: reg.cpp
  7. Owner: AndrewS/LeiJin
  8. ===================================================================*/
  9. #include "denpre.h"
  10. #pragma hdrstop
  11. #if _IIS_5_1
  12. #include <iadm.h>
  13. #elif _IIS_6_0
  14. #include <iadmw.h>
  15. #else
  16. #error "Neither _IIS_6_0 nor _IIS_5_1 is defined"
  17. #endif
  18. #include "comadmin.h"
  19. #include "memchk.h"
  20. #include "Accctrl.h"
  21. #include "aclapi.h"
  22. #include "iiscnfg.h"
  23. //External functions, defined in glob.cpp
  24. extern HRESULT MDRegisterProperties(void);
  25. extern HRESULT MDUnRegisterProperties(void);
  26. // Globals
  27. const REGSAM samDesired = KEY_READ | KEY_WRITE;
  28. HRESULT RegisterASPProperties(BOOL fReg = TRUE);
  29. /*
  30. * Info about our intrinsics used by Register & UnRegister
  31. */
  32. const char *szClassDesc[] = { "ASP Response Object",
  33. "ASP Request Object",
  34. "ASP Request Dictionary",
  35. "ASP Server Object",
  36. "ASP Application Object",
  37. "ASP Session Object",
  38. "ASP String List Object",
  39. "ASP Read Cookie",
  40. "ASP Write Cookie",
  41. "ASP Scripting Context Object",
  42. "ASP Certificate Object",
  43. };
  44. const char *szCLSIDEntry[] = { "CLSID\\{D97A6DA0-A864-11cf-83BE-00A0C90C2BD8}", // IResponse
  45. "CLSID\\{D97A6DA0-A861-11cf-93AE-00A0C90C2BD8}", // IRequest
  46. "CLSID\\{D97A6DA0-A85F-11df-83AE-00A0C90C2BD8}", // IRequestDictionary
  47. "CLSID\\{D97A6DA0-A867-11cf-83AE-01A0C90C2BD8}", // IServer
  48. "CLSID\\{D97A6DA0-A866-11cf-83AE-10A0C90C2BD8}", // IApplicationObject
  49. "CLSID\\{D97A6DA0-A865-11cf-83AF-00A0C90C2BD8}", // ISessionObject
  50. "CLSID\\{D97A6DA0-A85D-11cf-83AE-00A0C90C2BD8}", // IStringList
  51. "CLSID\\{71EAF260-0CE0-11d0-A53E-00A0C90C2091}", // IReadCookie
  52. "CLSID\\{D97A6DA0-A862-11cf-84AE-00A0C90C2BD8}", // IWriteCookie
  53. "CLSID\\{D97A6DA0-A868-11cf-83AE-00B0C90C2BD8}", // IScriptingContext
  54. "CLSID\\{b3192190-1176-11d0-8ce8-00aa006c400c}", // ICertificate
  55. };
  56. const cClassesMax = sizeof(szCLSIDEntry) / sizeof(char *);
  57. /*===================================================================
  58. RegisterIntrinsics
  59. Register info about our intrinsics in the registry.
  60. Returns:
  61. HRESULT - S_OK on success
  62. Side effects:
  63. Registers denali objects in the registry
  64. ===================================================================*/
  65. HRESULT RegisterIntrinsics(void)
  66. {
  67. static const char szDenaliDLL[] = "asp.DLL";
  68. static const char szThreadingModel[] = "ThreadingModel";
  69. static const char szInprocServer32[] = "InprocServer32";
  70. static const char szFreeThreaded[] = "Both";
  71. static const char szProgIdKey[] = "ProgId";
  72. static const char szCLSIDKey[] = "CLSID";
  73. HRESULT hr = S_OK;
  74. char szPath[MAX_PATH];
  75. char *pch;
  76. HKEY hkeyCLSID = NULL;
  77. HKEY hkeyT = NULL;
  78. HKEY hkey2;
  79. DWORD iClass;
  80. // Get the path and name of Denali
  81. if (!GetModuleFileNameA(g_hinstDLL, szPath, sizeof(szPath)/sizeof(char)))
  82. return E_FAIL;
  83. // bug fix 102010 DBCS fixes
  84. //
  85. //for (pch = szPath + lstrlen(szPath); pch > szPath && *pch != TEXT('\\'); pch--)
  86. // ;
  87. //if (pch == szPath)
  88. pch = (char*) _mbsrchr((const unsigned char*)szPath, '\\');
  89. if (pch == NULL)
  90. {
  91. Assert(FALSE);
  92. goto LErrExit;
  93. }
  94. strcpy(pch + 1, szDenaliDLL);
  95. for (iClass = 0; iClass < cClassesMax; iClass++)
  96. {
  97. // install the CLSID key
  98. // Setting the value of the description creates the key for the clsid
  99. if ((RegSetValueA(HKEY_CLASSES_ROOT, szCLSIDEntry[iClass], REG_SZ, szClassDesc[iClass],
  100. strlen(szClassDesc[iClass])) != ERROR_SUCCESS))
  101. goto LErrExit;
  102. // Open the CLSID key so we can set values on it
  103. if (RegOpenKeyExA(HKEY_CLASSES_ROOT, szCLSIDEntry[iClass], 0, samDesired, &hkeyCLSID) != ERROR_SUCCESS)
  104. goto LErrExit;
  105. // install the InprocServer32 key and open the sub-key to set the named value
  106. if ((RegSetValueA(hkeyCLSID, szInprocServer32, REG_SZ, szPath, strlen(szPath)) != ERROR_SUCCESS))
  107. goto LErrExit;
  108. if ((RegOpenKeyExA(hkeyCLSID, szInprocServer32, 0, samDesired, &hkeyT) != ERROR_SUCCESS))
  109. goto LErrExit;
  110. // install the ThreadingModel named value
  111. if (RegSetValueExA(hkeyT, szThreadingModel, 0, REG_SZ, (const BYTE *)szFreeThreaded,
  112. (strlen(szFreeThreaded)+1) * sizeof(char)) != ERROR_SUCCESS)
  113. goto LErrExit;
  114. if (RegCloseKey(hkeyT) != ERROR_SUCCESS)
  115. goto LErrExit;
  116. hkeyT = NULL;
  117. RegCloseKey(hkeyCLSID);
  118. hkeyCLSID = NULL;
  119. }
  120. return hr;
  121. LErrExit:
  122. RegCloseKey(hkeyT);
  123. RegCloseKey(hkeyCLSID);
  124. return E_FAIL;
  125. }
  126. /*===================================================================
  127. UnRegisterKey
  128. Given a string which is the name of a key under HKEY_CLASSES_ROOT,
  129. delete everything under that key and the key itself from the registry
  130. (why the heck isnt there an API that does this!?!?)
  131. Returns:
  132. HRESULT - S_OK on success
  133. Side effects:
  134. Removes a key & all subkeys from the registry
  135. ===================================================================*/
  136. HRESULT UnRegisterKey(CHAR *szKey)
  137. {
  138. HKEY hkey = NULL;
  139. CHAR szKeyName[255];
  140. DWORD cbKeyName;
  141. LONG errT;
  142. // Open the HKEY_CLASSES_ROOT\CLSID\{...} key so we can delete its subkeys
  143. if (RegOpenKeyExA(HKEY_CLASSES_ROOT, szKey, 0, samDesired, &hkey) != ERROR_SUCCESS)
  144. goto LErrExit;
  145. // Enumerate all its subkeys, and delete them
  146. while (TRUE)
  147. {
  148. cbKeyName = sizeof(szKeyName);
  149. if ((errT = RegEnumKeyExA(hkey, 0, szKeyName, &cbKeyName, 0, NULL, 0, NULL)) != ERROR_SUCCESS)
  150. break;
  151. if ((errT = RegDeleteKeyA(hkey, szKeyName)) != ERROR_SUCCESS)
  152. goto LErrExit;
  153. }
  154. // Close the key, and then delete it
  155. if ((errT = RegCloseKey(hkey)) != ERROR_SUCCESS)
  156. return(E_FAIL);
  157. if ((errT = RegDeleteKeyA(HKEY_CLASSES_ROOT, szKey)) != ERROR_SUCCESS)
  158. {
  159. DBGPRINTF((DBG_CONTEXT, "Deleting key %s returned %d\n",
  160. szKey, GetLastError()));
  161. return(E_FAIL);
  162. }
  163. return S_OK;
  164. LErrExit:
  165. RegCloseKey(hkey);
  166. return E_FAIL;
  167. }
  168. /*===================================================================
  169. UnRegisterIntrinsics
  170. UnRegister the info about our intrinsics from the registry.
  171. Returns:
  172. HRESULT - S_OK on success
  173. Side effects:
  174. Removes denali objects from the registry
  175. ===================================================================*/
  176. HRESULT UnRegisterIntrinsics(void)
  177. {
  178. HRESULT hr = S_OK, hrT;
  179. DWORD iClass;
  180. // Now delete the keys for the objects
  181. for (iClass = 0; iClass < cClassesMax; iClass++)
  182. {
  183. // Open the HKEY_CLASSES_ROOT\CLSID\{...} key so we can delete its subkeys
  184. if (FAILED(hrT = UnRegisterKey((CHAR *)szCLSIDEntry[iClass])))
  185. hr = hrT; // Hold onto the error, but keep going
  186. }
  187. return hr;
  188. }
  189. /*===================================================================
  190. RegisterTypeLib
  191. Register denali typelib in the registry.
  192. Returns:
  193. HRESULT - S_OK on success
  194. Side effects:
  195. register denali typelib in the registry
  196. ===================================================================*/
  197. HRESULT RegisterTypeLib(void)
  198. {
  199. HRESULT hr;
  200. ITypeLib *pITypeLib = NULL;
  201. char szFile[MAX_PATH+4];
  202. BSTR bstrFile;
  203. // Get the path and name of Denali
  204. if (!GetModuleFileNameA(g_hinstDLL, szFile, MAX_PATH))
  205. return E_FAIL;
  206. // There are two type libraries: First the standard ASP typelib
  207. // then the typelib for the Transacted Script Context object.
  208. // Load them both.
  209. // First type lib, from default (first ITypeLib entry) location
  210. hr = SysAllocStringFromSz(szFile, 0, &bstrFile);
  211. if (FAILED(hr))
  212. return hr;
  213. hr = LoadTypeLibEx(bstrFile, REGKIND_REGISTER, &pITypeLib);
  214. if (pITypeLib) {
  215. pITypeLib->Release();
  216. pITypeLib = NULL;
  217. }
  218. SysFreeString(bstrFile);
  219. if (FAILED(hr))
  220. return hr;
  221. // now register the Transacted Script Context Object
  222. strcat(szFile, "\\2");
  223. hr = SysAllocStringFromSz(szFile, 0, &bstrFile);
  224. if (FAILED(hr))
  225. return hr;
  226. hr = LoadTypeLibEx(bstrFile, REGKIND_REGISTER, &pITypeLib);
  227. if (pITypeLib) {
  228. pITypeLib->Release();
  229. pITypeLib = NULL;
  230. }
  231. SysFreeString(bstrFile);
  232. return hr;
  233. }
  234. /*===================================================================
  235. UnRegisterTypeLib
  236. UnRegister denali typelib in the registry. Note: Only the current version used by asp.dll is removed.
  237. Returns:
  238. HRESULT - S_OK on success
  239. Side effects:
  240. unregister denali typelib in the registry
  241. ===================================================================*/
  242. HRESULT UnRegisterTypeLib(void)
  243. {
  244. HRESULT hr;
  245. ITypeLib *pITypeLib = NULL;
  246. TLIBATTR *pTLibAttr = NULL;
  247. char szFile[MAX_PATH + 4];
  248. BSTR bstrFile;
  249. // Get the path and name of Denali
  250. if (!GetModuleFileNameA(g_hinstDLL, szFile, MAX_PATH))
  251. return E_FAIL;
  252. hr = SysAllocStringFromSz(szFile, 0, &bstrFile);
  253. if (FAILED(hr))
  254. return hr;
  255. hr = LoadTypeLibEx(bstrFile, REGKIND_REGISTER, &pITypeLib);
  256. if(SUCCEEDED(hr) && pITypeLib)
  257. {
  258. hr = pITypeLib->GetLibAttr(&pTLibAttr);
  259. if(SUCCEEDED(hr) && pTLibAttr)
  260. {
  261. hr = UnRegisterTypeLib( pTLibAttr->guid,
  262. pTLibAttr->wMajorVerNum,
  263. pTLibAttr->wMinorVerNum,
  264. pTLibAttr->lcid,
  265. pTLibAttr->syskind);
  266. pITypeLib->ReleaseTLibAttr(pTLibAttr);
  267. pTLibAttr = NULL;
  268. }
  269. pITypeLib->Release();
  270. pITypeLib = NULL;
  271. }
  272. SysFreeString(bstrFile);
  273. // unregister the Txn typelib
  274. strcat(szFile, "\\2");
  275. hr = SysAllocStringFromSz(szFile, 0, &bstrFile);
  276. if (FAILED(hr))
  277. return hr;
  278. hr = LoadTypeLibEx(bstrFile, REGKIND_REGISTER, &pITypeLib);
  279. if(SUCCEEDED(hr) && pITypeLib)
  280. {
  281. hr = pITypeLib->GetLibAttr(&pTLibAttr);
  282. if(SUCCEEDED(hr) && pTLibAttr)
  283. {
  284. hr = UnRegisterTypeLib( pTLibAttr->guid,
  285. pTLibAttr->wMajorVerNum,
  286. pTLibAttr->wMinorVerNum,
  287. pTLibAttr->lcid,
  288. pTLibAttr->syskind);
  289. pITypeLib->ReleaseTLibAttr(pTLibAttr);
  290. pTLibAttr = NULL;
  291. }
  292. pITypeLib->Release();
  293. pITypeLib = NULL;
  294. }
  295. SysFreeString(bstrFile);
  296. return hr;
  297. }
  298. HRESULT GetIWAMAccountName(LPSTR pszIdentity,
  299. DWORD cbIdentity)
  300. {
  301. HRESULT hr = S_OK;
  302. IMSAdminBase *pMetabase = NULL;
  303. METADATA_HANDLE hMetabase = NULL;
  304. METADATA_RECORD recMetaData;
  305. DWORD dwRequiredLen;
  306. CWCharToMBCS convStr;
  307. MD_SET_DATA_RECORD( &recMetaData,
  308. MD_WAM_USER_NAME,
  309. METADATA_NO_ATTRIBUTES,
  310. IIS_MD_UT_WAM,
  311. STRING_METADATA,
  312. cbIdentity,
  313. pszIdentity);
  314. if (FAILED(hr = CoCreateInstance(CLSID_MSAdminBase,
  315. NULL,
  316. CLSCTX_SERVER,
  317. IID_IMSAdminBase,
  318. (void **)&pMetabase)));
  319. // Open key to the Web service, and get a handle of \LM\w3svc
  320. else if (FAILED(hr = pMetabase->OpenKey(METADATA_MASTER_ROOT_HANDLE,
  321. (LPWSTR)(L"\\LM\\W3SVC"),
  322. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE,
  323. 30000,
  324. &hMetabase)));
  325. // get the data. This will return the IWAM username in the caller
  326. // supplied buffer. The name will be UNICODE.
  327. else if (FAILED(hr = pMetabase->GetData(hMetabase,
  328. NULL,
  329. &recMetaData,
  330. &dwRequiredLen)));
  331. // convert the string to MBCS using the CP_ACP
  332. else if (FAILED(hr = convStr.Init((LPWSTR)pszIdentity)));
  333. // overwrite the caller's memory with the converted string
  334. else strcpy(pszIdentity, convStr.GetString());
  335. // cleanup
  336. if (hMetabase && pMetabase)
  337. pMetabase->CloseKey(hMetabase);
  338. if (pMetabase)
  339. pMetabase->Release();
  340. return(hr);
  341. }
  342. HRESULT CreateCompiledTemplatesTempDir()
  343. {
  344. HRESULT hr = S_OK;
  345. BYTE szRegString[MAX_PATH];
  346. BYTE pszExpanded[MAX_PATH];
  347. int result = 0;
  348. EXPLICIT_ACCESSA ea[3];
  349. PACL pNewDACL = NULL;
  350. char szWamIdentity[1024];
  351. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  352. // read the temp dir name for the persistant templ
  353. // cache.
  354. CchLoadStringOfId(IDS_DEFAULTPERSISTDIR, (LPSTR)szRegString, MAX_PATH);
  355. result = ExpandEnvironmentStringsA((LPCSTR)szRegString,
  356. (LPSTR)pszExpanded,
  357. MAX_PATH);
  358. if ((result <= MAX_PATH) && (result > 0)) {
  359. CreateDirectoryA((LPCSTR)pszExpanded,NULL);
  360. }
  361. // this next section of code will place the SYSTEM and IWAM_<ComputerName>
  362. // ACEs on the directorie's ACL
  363. ZeroMemory(ea, sizeof(EXPLICIT_ACCESSA) * 3);
  364. ea[0].grfAccessPermissions = SYNCHRONIZE | GENERIC_ALL;
  365. ea[0].grfAccessMode = GRANT_ACCESS;
  366. ea[0].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  367. ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  368. ea[1].grfAccessPermissions = SYNCHRONIZE | GENERIC_ALL;
  369. ea[1].grfAccessMode = GRANT_ACCESS;
  370. ea[1].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  371. ea[1].Trustee.TrusteeForm = TRUSTEE_IS_NAME;
  372. ea[1].Trustee.ptstrName = szWamIdentity;
  373. ea[2].grfAccessPermissions = SYNCHRONIZE | GENERIC_ALL;
  374. ea[2].grfAccessMode = GRANT_ACCESS;
  375. ea[2].grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  376. ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  377. // go get the IWAM account name from the metabase
  378. if (FAILED(hr = GetIWAMAccountName(szWamIdentity, sizeof(szWamIdentity))));
  379. // build the new DACL with just these ACEs
  380. else if (!AllocateAndInitializeSid(&NtAuthority,
  381. 1,
  382. SECURITY_LOCAL_SYSTEM_RID,
  383. 0,0,0,0,0,0,0,
  384. (PSID *)(&ea[0].Trustee.ptstrName)))
  385. hr = HRESULT_FROM_WIN32(GetLastError());
  386. else if (!AllocateAndInitializeSid(&NtAuthority,
  387. 2, // 2 sub-authorities
  388. SECURITY_BUILTIN_DOMAIN_RID,
  389. DOMAIN_ALIAS_RID_ADMINS,
  390. 0,0,0,0,0,0,
  391. (PSID *)(&ea[2].Trustee.ptstrName)))
  392. hr = HRESULT_FROM_WIN32(GetLastError());
  393. else if ((hr = SetEntriesInAclA(3,
  394. ea,
  395. NULL,
  396. &pNewDACL)) != ERROR_SUCCESS);
  397. // set the ACL on the directory
  398. else hr = SetNamedSecurityInfoA((LPSTR)pszExpanded,
  399. SE_FILE_OBJECT,
  400. DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
  401. NULL,
  402. NULL,
  403. pNewDACL,
  404. NULL);
  405. if (pNewDACL)
  406. LocalFree(pNewDACL);
  407. if (ea[0].Trustee.ptstrName)
  408. FreeSid(ea[0].Trustee.ptstrName);
  409. if (ea[2].Trustee.ptstrName)
  410. FreeSid(ea[2].Trustee.ptstrName);
  411. return(hr);
  412. }
  413. /*===================================================================
  414. DllRegisterServer
  415. Entry point used by RegSvr32.exe to register the DLL.
  416. Returns:
  417. HRESULT - S_OK on success
  418. Side effects:
  419. Registers denali objects in the registry
  420. ===================================================================*/
  421. STDAPI DllRegisterServer(void)
  422. {
  423. HRESULT hr;
  424. HRESULT hrCoInit;
  425. hrCoInit = CoInitialize(NULL);
  426. // First try to unregister some stuff
  427. // This is important when we are registering on top of
  428. // an old IIS 3.0 Denali registration
  429. // Don't care if fails
  430. UnRegisterEventLog();
  431. UnRegisterIntrinsics();
  432. UnRegisterTypeLib();
  433. // Now do the registration
  434. if (FAILED(hr = MDRegisterProperties()))
  435. goto LErr;
  436. // Register NT event log
  437. if(FAILED(hr = RegisterEventLog()))
  438. goto LErr;
  439. if (FAILED(hr = RegisterTypeLib()))
  440. goto LErr;
  441. // Register our intrinsics
  442. if (FAILED(hr = RegisterIntrinsics()))
  443. goto LErr;
  444. if(FAILED(hr = RegisterASPProperties(TRUE)))
  445. goto LErr;
  446. if (FAILED(hr = CreateCompiledTemplatesTempDir()))
  447. goto LErr;
  448. LErr:
  449. if (SUCCEEDED(hrCoInit))
  450. CoUninitialize();
  451. return(hr);
  452. }
  453. /*===================================================================
  454. DllUnregisterServer
  455. Entry point used by RegSvr32.exe to unregister the DLL.
  456. Returns:
  457. HRESULT - S_OK on success
  458. Side effects:
  459. Removes denali registrations from the registry
  460. ===================================================================*/
  461. STDAPI DllUnregisterServer(void)
  462. {
  463. HRESULT hr = S_OK, hrT;
  464. HRESULT hrCoInit;
  465. hrCoInit = CoInitialize(NULL);
  466. hrT = UnRegisterEventLog();
  467. if (FAILED(hrT))
  468. hr = hrT;
  469. hrT = MDUnRegisterProperties();
  470. if (FAILED(hrT))
  471. hr = hrT;
  472. hrT = UnRegisterIntrinsics();
  473. if (FAILED(hrT))
  474. hr = hrT;
  475. hrT = UnRegisterTypeLib();
  476. if (FAILED(hrT))
  477. hr = hrT;
  478. hrT = RegisterASPProperties(FALSE);
  479. if (FAILED(hrT))
  480. hr = hrT;
  481. // UNDONE BUG 80063: Ignore errors from this call
  482. #ifdef UNDONE
  483. if (FAILED(hrT))
  484. hr = hrT;
  485. #endif
  486. if (SUCCEEDED(hrCoInit))
  487. CoUninitialize();
  488. return(hr);
  489. }
  490. /*===================================================================
  491. RegisterASPProperties
  492. Entry point used by RegSvr32.exe to register and unregister
  493. ASP setting stored in the SYSTEM registry.
  494. Parameters
  495. fReg = TRUE - to register
  496. FALSE - to unregister
  497. Returns:
  498. HRESULT - S_OK on success
  499. Side effects:
  500. Removes denali registrations from the registry
  501. ===================================================================*/
  502. HRESULT RegisterASPProperties(BOOL fReg)
  503. {
  504. HKEY hkey1 = NULL, hkey2 = NULL;
  505. DWORD iValue;
  506. LONG lT;
  507. static const char szW3SVC[] = "System\\CurrentControlSet\\Services\\W3SVC";
  508. static const char szW3SVCASP[] = "System\\CurrentControlSet\\Services\\W3SVC\\ASP\\Parameters";
  509. static const char szASPParm[] = "ASP\\Parameters";
  510. BYTE szDefailtString[1024];
  511. // Open the key for W3SVCASP so we can add denali under it if it does not exist
  512. //
  513. if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, szW3SVCASP, 0, samDesired, &hkey2) != ERROR_SUCCESS)
  514. {
  515. // if the key does not exits and you call with FALSE the exit with error
  516. //
  517. if (!fReg)
  518. goto LErrExit;
  519. // Open the key for W3SVC so we can add denali under it
  520. //
  521. if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, szW3SVC, 0, samDesired, &hkey1) != ERROR_SUCCESS)
  522. {
  523. MSG_Error(IDS_IIS_NOTINSTALLED);
  524. return(E_FAIL);
  525. }
  526. // Add the key for Denali\Parameters
  527. //
  528. if (RegCreateKeyA(hkey1, szASPParm, &hkey2) != ERROR_SUCCESS)
  529. return(E_FAIL);
  530. }
  531. // release the extra key
  532. //
  533. if (hkey1)
  534. RegCloseKey(hkey1);
  535. hkey1 = hkey2;
  536. // set whatever is needed under hkey1
  537. // (currently nothing)
  538. // done clean up and exit
  539. if (RegCloseKey(hkey1) != ERROR_SUCCESS)
  540. return(E_FAIL);
  541. return S_OK;
  542. LErrExit:
  543. RegCloseKey(hkey1);
  544. return E_FAIL;
  545. }