Leaked source code of windows server 2003
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.

1191 lines
37 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation
  3. Module Name:
  4. fusionreg.h
  5. Abstract:
  6. ported from vsee\lib\reg\ckey.cpp
  7. CRegKey2
  8. Author:
  9. Jay Krell (JayKrell) August 2001
  10. Revision History:
  11. --*/
  12. #include "stdinc.h"
  13. #include <wchar.h>
  14. #include "vseeport.h"
  15. #include "fusionregkey2.h"
  16. #include "fusionarray.h"
  17. /*-----------------------------------------------------------------------------
  18. Name: CRegKey2::~CRegKey2
  19. @mfunc
  20. destructor; call RegCloseKey if the CRegKey2 is valid
  21. @owner
  22. -----------------------------------------------------------------------------*/
  23. F::CRegKey2::~CRegKey2
  24. (
  25. )
  26. {
  27. if (m_fValid)
  28. {
  29. LONG lResult = RegCloseKey(m_hKey);
  30. ASSERT_NTC(lResult == ERROR_SUCCESS);
  31. }
  32. }
  33. /*-----------------------------------------------------------------------------
  34. Name: CRegKey2::CRegKey2
  35. @mfunc
  36. default constructor
  37. @owner
  38. -----------------------------------------------------------------------------*/
  39. F::CRegKey2::CRegKey2
  40. (
  41. )
  42. :
  43. m_fValid(false),
  44. m_fMaxValueLengthValid(false),
  45. // just in case
  46. m_hKey(reinterpret_cast<HKEY>(INVALID_HANDLE_VALUE)),
  47. m_cbMaxValueLength(0),
  48. m_fKnownSam(false),
  49. m_samDesired(0)
  50. {
  51. VSEE_NO_THROW();
  52. }
  53. /*-----------------------------------------------------------------------------
  54. Name: CRegKey2::CRegKey2
  55. @mfunc
  56. construct a CRegKey2 from an HKEY
  57. @owner
  58. -----------------------------------------------------------------------------*/
  59. F::CRegKey2::CRegKey2
  60. (
  61. HKEY hKey
  62. )
  63. :
  64. m_hKey(hKey),
  65. m_fValid(true),
  66. m_fKnownSam(false),
  67. m_samDesired(0)
  68. {
  69. VSEE_NO_THROW();
  70. }
  71. /*-----------------------------------------------------------------------------
  72. Name: CRegKey2::operator=
  73. @mfunc
  74. assign an HKEY to a CRegKey2; throws if the CRegKey2 already has a valid HKEY
  75. @owner
  76. -----------------------------------------------------------------------------*/
  77. VOID
  78. F::CRegKey2::operator=
  79. (
  80. HKEY hKey
  81. ) throw(CErr)
  82. {
  83. VSEE_ASSERT_CAN_THROW();
  84. VsVerifyThrow(!m_fValid, "only 'single assignment' please.");
  85. m_hKey = hKey;
  86. m_fValid = true;
  87. m_fKnownSam = false;
  88. m_samDesired = 0;
  89. }
  90. /*-----------------------------------------------------------------------------
  91. Name: CRegKey2::ThrAttach
  92. @mfunc
  93. same as operator=
  94. @owner
  95. -----------------------------------------------------------------------------*/
  96. VOID
  97. F::CRegKey2::ThrAttach
  98. (
  99. HKEY hKey
  100. ) throw(CErr)
  101. {
  102. VSEE_ASSERT_CAN_THROW();
  103. (*this) = hKey;
  104. }
  105. /*-----------------------------------------------------------------------------
  106. Name: CRegKey2::Detach
  107. @mfunc
  108. @owner
  109. -----------------------------------------------------------------------------*/
  110. HKEY
  111. F::CRegKey2::Detach
  112. (
  113. ) throw(CErr)
  114. {
  115. HKEY key = NULL;
  116. if (m_fValid)
  117. {
  118. key = operator HKEY();
  119. m_fValid = FALSE;
  120. }
  121. return key;
  122. }
  123. /*-----------------------------------------------------------------------------
  124. Name: CRegKey2::operator HKEY
  125. @mfunc
  126. return the held HKEY, or throws if not valid
  127. the const version has protected access, because it doesn't
  128. enforce logical const
  129. @owner
  130. -----------------------------------------------------------------------------*/
  131. F::CRegKey2::operator HKEY
  132. (
  133. ) const throw(CErr)
  134. {
  135. return *const_cast<CRegKey2*>(this);
  136. }
  137. /*-----------------------------------------------------------------------------
  138. Name: CRegKey2::operator HKEY
  139. @mfunc
  140. return the held HKEY, or throws if not valid
  141. @owner
  142. -----------------------------------------------------------------------------*/
  143. F::CRegKey2::operator HKEY
  144. (
  145. ) throw(CErr)
  146. {
  147. VSEE_ASSERT_CAN_THROW();
  148. VsVerifyThrow(m_fValid, __FUNCTION__);
  149. return m_hKey;
  150. }
  151. /*-----------------------------------------------------------------------------
  152. Name: CRegKey2::HrOpen
  153. @mfunc
  154. call RegOpenKeyEx
  155. @owner
  156. -----------------------------------------------------------------------------*/
  157. HRESULT
  158. F::CRegKey2::HrOpen
  159. (
  160. HKEY hKeyParent, // @parm same as ::RegOpenKeyEx
  161. LPCWSTR pszKeyName, // @parm same as ::RegOpenKeyEx
  162. REGSAM samDesired // = KEY_READ // @parm same as ::RegOpenKeyEx
  163. )
  164. {
  165. VSEE_NO_THROW();
  166. // REVIEW or should we call Close like ATL?
  167. if (m_fValid)
  168. {
  169. return E_UNEXPECTED;
  170. }
  171. HKEY hKey = NULL;
  172. LONG lRes = ::RegOpenKeyExW(hKeyParent, pszKeyName, 0/*ULONG reserved*/, samDesired, &hKey);
  173. if (lRes != ERROR_SUCCESS)
  174. {
  175. return HRESULT_FROM_WIN32(lRes);
  176. }
  177. // ATL calls Close here.
  178. m_hKey = hKey;
  179. m_fValid = true;
  180. m_fKnownSam = true;
  181. m_samDesired = samDesired;
  182. return S_OK;
  183. }
  184. /*-----------------------------------------------------------------------------
  185. Name: CRegKey2::ThrOpen
  186. @mfunc
  187. call RegOpenKeyEx
  188. @owner
  189. -----------------------------------------------------------------------------*/
  190. VOID
  191. F::CRegKey2::ThrOpen
  192. (
  193. HKEY hKeyParent, // @parm same as ::RegOpenKeyEx
  194. LPCWSTR pszKeyName, // @parm same as ::RegOpenKeyEx
  195. REGSAM samDesired // = KEY_READ // @parm same as ::RegOpenKeyEx
  196. ) throw(CErr)
  197. {
  198. VSEE_ASSERT_CAN_THROW();
  199. NVseeLibError_VCheck(HrOpen(hKeyParent, pszKeyName, samDesired));
  200. }
  201. /*-----------------------------------------------------------------------------
  202. Name: CRegKey2::Create
  203. @mfunc
  204. call RegCreateKeyEx
  205. @rvalue dwDisposition | from RegCreateKeyEx
  206. @rvalue REG_CREATED_NEW_KEY | The key did not exist and was created.
  207. @rvalue REG_OPENED_EXISTING_KEY | The key existed and was simply opened
  208. without being changed.
  209. @owner
  210. -----------------------------------------------------------------------------*/
  211. DWORD
  212. F::CRegKey2::Create
  213. (
  214. HKEY hKeyParent, // @parm same as ::RegCreateKeyEx
  215. PCWSTR pszKeyName, // @parm same as ::RegCreateKeyEx
  216. REGSAM samDesired // = KEY_ALL_ACCESS// @parm same as ::RegCreateKeyEx
  217. ) throw(CErr)
  218. {
  219. VSEE_ASSERT_CAN_THROW();
  220. // REVIEW or should we call Close like ATL?
  221. VsVerifyThrow(!m_fValid, "only 'single assignment' please");
  222. DWORD dwDisposition = 0;
  223. HKEY hKey = NULL;
  224. LONG lRes =
  225. ::RegCreateKeyExW
  226. (
  227. hKeyParent,
  228. pszKeyName,
  229. 0, // DWORD reserved
  230. NULL, // LPCWSTR class
  231. REG_OPTION_NON_VOLATILE, // DWORD options
  232. samDesired,
  233. NULL, // security
  234. &hKey,
  235. &dwDisposition
  236. );
  237. if (lRes != ERROR_SUCCESS)
  238. {
  239. NVseeLibError_VThrowWin32(lRes);
  240. }
  241. // ATL calls Close here.
  242. m_hKey = hKey;
  243. m_fValid = true;
  244. m_fKnownSam = true;
  245. m_samDesired = samDesired;
  246. return dwDisposition;
  247. }
  248. /*-----------------------------------------------------------------------------
  249. Name: CRegKey2::ThrSetValue
  250. @mfunc
  251. call RegSetValueEx
  252. @owner AlinC
  253. -----------------------------------------------------------------------------*/
  254. VOID
  255. F::CRegKey2::ThrSetValue
  256. (
  257. PCWSTR pszValueName, // @parm [in] same as RegSetValueEx
  258. const DWORD& dwValue
  259. ) throw(CErr)
  260. {
  261. VSEE_ASSERT_CAN_THROW();
  262. VSASSERT(!m_fKnownSam || (m_samDesired & KEY_SET_VALUE), "Attempt to set value when key not opened with KEY_SET_VALUE");
  263. VsVerifyThrow(m_fValid, __FUNCTION__);
  264. LONG lRes =
  265. ::RegSetValueExW
  266. (
  267. *this,
  268. pszValueName,
  269. 0, // DWORD reserved
  270. REG_DWORD,
  271. reinterpret_cast<const BYTE*>(&dwValue),
  272. sizeof(DWORD)
  273. );
  274. if (lRes != ERROR_SUCCESS)
  275. {
  276. NVseeLibError_VThrowWin32(lRes);
  277. }
  278. }
  279. /*-----------------------------------------------------------------------------
  280. Name: CRegKey2::ThrSetValue
  281. @mfunc
  282. call RegSetValueEx
  283. @owner
  284. -----------------------------------------------------------------------------*/
  285. VOID
  286. F::CRegKey2::ThrSetValue
  287. (
  288. PCWSTR pszValueName, // @parm [in] same as RegSetValueEx
  289. const F::CBaseStringBuffer& strValue
  290. ) throw(CErr)
  291. {
  292. VSEE_ASSERT_CAN_THROW();
  293. VsVerifyThrow(m_fValid, __FUNCTION__);
  294. VSASSERT(!m_fKnownSam || (m_samDesired & KEY_SET_VALUE), "Attempt to set value when key not opened with KEY_SET_VALUE");
  295. DWORD cbSize = static_cast<DWORD>((strValue.Cch()+1) * sizeof(WCHAR));
  296. LPCWSTR szData = (LPCWSTR)strValue;
  297. LONG lRes =
  298. ::RegSetValueExW
  299. (
  300. *this,
  301. pszValueName,
  302. 0, // DWORD reserved
  303. REG_SZ,
  304. reinterpret_cast<const BYTE*>(szData),
  305. cbSize
  306. );
  307. if (lRes != ERROR_SUCCESS)
  308. {
  309. NVseeLibError_VThrowWin32(lRes);
  310. }
  311. }
  312. /*-----------------------------------------------------------------------------
  313. Name: CRegKey2::ThrQueryValue
  314. @mfunc
  315. call RegQueryValueEx
  316. @owner
  317. -----------------------------------------------------------------------------*/
  318. VOID
  319. F::CRegKey2::ThrQueryValue
  320. (
  321. PCWSTR pszValueName, // @parm [in] same as RegQueryValueEx
  322. DWORD* pdwType, // @parm [out] same as RegQueryValueEx
  323. BYTE* pbData, // @parm [out] same as RegQueryValueEx
  324. DWORD* pcbData // @parm [out] same as RegQueryValueEx
  325. ) const throw(CErr)
  326. {
  327. VSEE_ASSERT_CAN_THROW();
  328. VsVerifyThrow(m_fValid, __FUNCTION__);
  329. DWORD cbActualBufferSize = 0;
  330. if (pcbData != NULL)
  331. {
  332. cbActualBufferSize = *pcbData;
  333. }
  334. if (pbData != NULL && cbActualBufferSize != 0)
  335. {
  336. if (cbActualBufferSize > 0)
  337. pbData[0] = 0;
  338. if (cbActualBufferSize > 1)
  339. {
  340. pbData[1] = 0;
  341. pbData[cbActualBufferSize - 1] = 0;
  342. }
  343. if (cbActualBufferSize > 2)
  344. {
  345. pbData[cbActualBufferSize - 2] = 0;
  346. }
  347. ZeroMemory(pbData, cbActualBufferSize); // temporary aggressiveness
  348. }
  349. LONG lRes =
  350. ::RegQueryValueExW
  351. (
  352. m_hKey,
  353. pszValueName,
  354. NULL, // DWORD* reserved
  355. pdwType,
  356. pbData,
  357. pcbData
  358. );
  359. if (pdwType != NULL)
  360. {
  361. FixBadRegistryStringValue(m_hKey, pszValueName, cbActualBufferSize, lRes, *pdwType, pbData, pcbData);
  362. }
  363. if (lRes != ERROR_SUCCESS)
  364. {
  365. NVseeLibError_VThrowWin32(lRes);
  366. }
  367. }
  368. /*-----------------------------------------------------------------------------
  369. Name: CRegKey2::ThrQueryValue
  370. @mfunc
  371. call RegQueryValueEx
  372. @owner
  373. -----------------------------------------------------------------------------*/
  374. VOID
  375. F::CRegKey2::ThrQueryValue
  376. (
  377. PCWSTR pszValueName, // @parm [in] same as RegQueryValueEx
  378. F::CBaseStringBuffer* pstrData
  379. ) const throw(CErr)
  380. {
  381. VSEE_ASSERT_CAN_THROW();
  382. VsVerifyThrow(m_fValid, __FUNCTION__);
  383. DWORD cbActualBufferSize = 0;
  384. DWORD dwType;
  385. LONG lRes =
  386. ::RegQueryValueExW
  387. (
  388. m_hKey,
  389. pszValueName,
  390. NULL, // DWORD* reserved
  391. &dwType,
  392. NULL,
  393. &cbActualBufferSize
  394. );
  395. if (REG_SZ != dwType)
  396. VsOriginateError(E_FAIL);
  397. cbActualBufferSize += 2 * sizeof(WCHAR); // fudge
  398. CFusionArray<WCHAR> szTempValue;
  399. if (!szTempValue.Win32SetSize(cbActualBufferSize /* more fudge */ + sizeof(WCHAR)))
  400. FusionpOutOfMemory();
  401. lRes =
  402. ::RegQueryValueExW
  403. (
  404. m_hKey,
  405. pszValueName,
  406. NULL, // DWORD* reserved
  407. &dwType,
  408. reinterpret_cast<BYTE*>(static_cast<PWSTR>(szTempValue.GetArrayPtr())),
  409. &cbActualBufferSize
  410. );
  411. if (lRes != ERROR_SUCCESS)
  412. {
  413. NVseeLibError_VThrowWin32(lRes);
  414. }
  415. // missing FixBadRegistryStringValue here
  416. pstrData->ThrAssign(szTempValue.GetArrayPtr(), StringLength(szTempValue.GetArrayPtr()));
  417. }
  418. // for diagnostic purposes only
  419. void
  420. F::CRegKey2::GetKeyNameForDiagnosticPurposes(
  421. HKEY Key,
  422. CUnicodeBaseStringBuffer & buff
  423. )
  424. {
  425. struct
  426. {
  427. KEY_NAME_INFORMATION KeyNameInfo;
  428. WCHAR NameBuffer[MAX_PATH];
  429. } s;
  430. NTSTATUS Status = 0;
  431. ULONG LengthOut = 0;
  432. buff.Clear();
  433. if (!NT_SUCCESS(Status = NtQueryKey(Key, KeyNameInformation, &s, sizeof(s), &LengthOut)))
  434. return;
  435. buff.Win32Assign(s.KeyNameInfo.Name, s.KeyNameInfo.NameLength / sizeof(WCHAR));
  436. }
  437. /*-----------------------------------------------------------------------------
  438. Name: FixBadRegistryStringValue
  439. @mfunc
  440. I'm seeing strings come back with an odd number of bytes, and without terminal
  441. nuls. Apply some fixup here. We are dependent on the fact that our caller
  442. overallocates its buffer to fit the terminal nul, but we do check this
  443. at runtime via cbActualBufferSize.
  444. @owner
  445. -----------------------------------------------------------------------------*/
  446. VOID
  447. F::CRegKey2::FixBadRegistryStringValue
  448. (
  449. HKEY Key, // @parm [in] for diagnostic purposes
  450. PCWSTR ValueName, // @parm [in] for diagnostic purposes
  451. DWORD cbActualBufferSize, // @parm [in] the size of the buffer pbData points to
  452. // this value might be larger than the value
  453. // passed to RegQueryValuEx, like if we want to reserve
  454. // space to append a nul beyond what RegQueryValuEx
  455. // gives us
  456. LONG lRes, // @parm [in] the result of a RegQueryValueEx
  457. // or RegEnumValue call
  458. DWORD dwType, // @parm [in] the type returned from a RegQueryValueEx
  459. // or RegEnumValue call
  460. BYTE* pbData, // @parm [in out] the data returned from a RegQueryValueEx
  461. // or RegEnumValue call, we possibly append a Unicode nul to it
  462. DWORD* pcbData // @parm [in out] the size returned from RegQueryValueEx
  463. // or RegEnumValue; we possibly round it to even
  464. // or grow it for a terminal nul
  465. )
  466. {
  467. VSEE_NO_THROW();
  468. C_ASSERT(sizeof(WCHAR) == 2);
  469. CTinyUnicodeStringBuffer KeyNameForDiagnosticPurposes;
  470. if
  471. (
  472. lRes == ERROR_SUCCESS
  473. && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)
  474. && pcbData != NULL
  475. )
  476. {
  477. DWORD& rcbData = *pcbData;
  478. WCHAR* pchData = reinterpret_cast<WCHAR*>(pbData);
  479. UNICODE_STRING NtUnicodeString;
  480. NtUnicodeString.Length = static_cast<USHORT>(rcbData);
  481. NtUnicodeString.Buffer = reinterpret_cast<PWSTR>(pbData);
  482. // can assert but not fixup in this case
  483. if (pbData == NULL || cbActualBufferSize < sizeof(WCHAR))
  484. {
  485. VSASSERT((rcbData % sizeof(WCHAR)) == 0, "bad registry data: odd number of bytes in a string");
  486. return;
  487. }
  488. // no chars at all? just put in a terminal nul
  489. if (rcbData < sizeof(WCHAR))
  490. {
  491. GetKeyNameForDiagnosticPurposes(Key, KeyNameForDiagnosticPurposes);
  492. FusionpDbgPrint(
  493. "fusion_regkey2: bad registry data: string with 0 or 1 byte, Key=%ls ValueName=%ls, ValueDataLength=0x%lx, PossiblyExpectedValueDataLength=0x%Ix, ValueData=%wZ\n",
  494. static_cast<PCWSTR>(KeyNameForDiagnosticPurposes),
  495. ValueName,
  496. rcbData,
  497. sizeof(WCHAR),
  498. &NtUnicodeString
  499. );
  500. // just put one terminal nul in
  501. pchData[0] = 0;
  502. rcbData = sizeof(WCHAR);
  503. }
  504. else
  505. {
  506. // first round down odd rcbData if already nul terminated,
  507. // because these cases otherwise look non nul terminated, since
  508. // the extra byte isn't nul.
  509. // I see this a lot, ThreadingModel = Apartment, rcbData = 21
  510. if (rcbData > sizeof(WCHAR) && (rcbData % sizeof(WCHAR)) != 0)
  511. {
  512. // usually the terminal nul is at
  513. // pbData[rcbData - 1] and pbData[rcbData - 2], but we look back one byte.
  514. if (pbData[rcbData - 2] == 0 && pbData[rcbData - 3] == 0)
  515. {
  516. // BUG elsewhere in the product
  517. //VSASSERT(false, "bad registry data: odd number of bytes in a string");
  518. GetKeyNameForDiagnosticPurposes(Key, KeyNameForDiagnosticPurposes);
  519. FusionpDbgPrint(
  520. "fusion_regkey2: bad registry data: odd number of bytes in a string, Key=%ls ValueName=%ls, ValueDataLength=0x%lx PossiblyExpectedValueDataLength=0x%lx, ValueData=%wZ\n",
  521. static_cast<PCWSTR>(KeyNameForDiagnosticPurposes),
  522. ValueName,
  523. rcbData,
  524. rcbData - 1,
  525. &NtUnicodeString
  526. );
  527. rcbData -= 1;
  528. }
  529. }
  530. // check for embedded / terminal nul
  531. DWORD cchData = rcbData / sizeof(WCHAR);
  532. WCHAR* pchNul = wmemchr(pchData, L'\0', cchData);
  533. WCHAR* pchNul2 = wmemchr(pchData, L'\0', cbActualBufferSize / sizeof(WCHAR));
  534. if (pchNul != (pchData + cchData - 1))
  535. {
  536. if (pchNul == NULL)
  537. {
  538. GetKeyNameForDiagnosticPurposes(Key, KeyNameForDiagnosticPurposes);
  539. if (pchNul2 == NULL)
  540. {
  541. FusionpDbgPrint(
  542. "fusion_regkey2: bad registry data: string contains no nuls, Key=%ls ValueName=%ls, ValueDataLength=0x%lx, ValueData=%wZ\n",
  543. static_cast<PCWSTR>(KeyNameForDiagnosticPurposes),
  544. ValueName,
  545. rcbData,
  546. &NtUnicodeString
  547. );
  548. }
  549. else
  550. {
  551. FusionpDbgPrint(
  552. "fusion_regkey2: bad registry data: string contains no nuls, Key=%ls ValueName=%ls, ValueDataLength=0x%lx, PossiblyExpectedValueDataLength=0x%lx, ValueData=%wZ\n",
  553. static_cast<PCWSTR>(KeyNameForDiagnosticPurposes),
  554. ValueName,
  555. rcbData,
  556. (pchNul2 - pchData + 1) * sizeof(WCHAR),
  557. &NtUnicodeString
  558. );
  559. }
  560. }
  561. else
  562. {
  563. // BUG elsewhere in the product
  564. //VSASSERT(false, "bad registry data: string contains embedded nul");
  565. GetKeyNameForDiagnosticPurposes(Key, KeyNameForDiagnosticPurposes);
  566. SIZE_T sizetcbData = (::wcslen(reinterpret_cast<PCWSTR>(pbData)) + 1) * sizeof(WCHAR);
  567. FusionpDbgPrint(
  568. "fusion_regkey2: bad registry data: string contains embedded nul%s, Key=%ls ValueName=%ls, ValueDataLength=0x%lx PossiblyExpectedValueDataLength=0x%Ix, ValueData=%wZ\n",
  569. (pbData[rcbData - 1] == 0 && pbData[rcbData - 2] == 0) ? "" : " and no terminal nul at claimed length",
  570. static_cast<PCWSTR>(KeyNameForDiagnosticPurposes),
  571. ValueName,
  572. rcbData,
  573. sizetcbData,
  574. &NtUnicodeString
  575. );
  576. // just reset the length down..
  577. if (sizetcbData > MAXULONG)
  578. {
  579. VsOriginateError(ERROR_INSUFFICIENT_BUFFER);
  580. }
  581. rcbData = static_cast<ULONG>(sizetcbData);
  582. // REVIEW should we set
  583. // rcbData approx = (pchNul - pbData)..
  584. // here and skip the next block?
  585. }
  586. // put in a terminal nul either way, in case a caller expects it, if there is room
  587. if (cbActualBufferSize >= sizeof(WCHAR))
  588. {
  589. pchData[(cbActualBufferSize / sizeof(WCHAR)) - 1] = 0;
  590. }
  591. }
  592. }
  593. }
  594. }
  595. /*-----------------------------------------------------------------------------
  596. Name: CRegKey2::HrQueryValue
  597. @mfunc
  598. call RegQueryValueEx, expecting [REG_SZ, REG_EXPAND_SZ]
  599. throws E_FAIL on type mismatch or returned FILE_NOT_FOUND is value doesn't exist
  600. @owner AllenD
  601. -----------------------------------------------------------------------------*/
  602. HRESULT
  603. F::CRegKey2::HrQueryValue
  604. (
  605. PCWSTR pszValueName, // @parm [in] same as RegQueryValueEx
  606. F::CBaseStringBuffer* pstrValue // @parm [out]
  607. ) const throw(CErr)
  608. {
  609. VSEE_ASSERT_CAN_THROW();
  610. VsVerifyThrow(m_fValid, __FUNCTION__);
  611. if (!m_fMaxValueLengthValid)
  612. {
  613. ThrQueryValuesInfo
  614. (
  615. NULL, // pcValues,
  616. NULL, // pcchMaxValueNameLength,
  617. &m_cbMaxValueLength
  618. );
  619. m_fMaxValueLengthValid = true;
  620. }
  621. // first try with possibly invalid m_cbMaxValueLength
  622. { // scope to destroy bufferValue
  623. CStringW_CFixedSizeBuffer bufferValue(pstrValue, m_cbMaxValueLength / sizeof(WCHAR));
  624. DWORD dwType = REG_DWORD; // initialize to other than a type we want
  625. DWORD cbActualBufferSize = m_cbMaxValueLength;
  626. // Adjust down by one WCHAR so we have room to add a nul;
  627. // our own QueryValue already increased it by two WCHARs, so this is safe.
  628. DWORD cbData = cbActualBufferSize - sizeof(WCHAR);
  629. BYTE* pbData = reinterpret_cast<BYTE*>(static_cast<PWSTR>(bufferValue));
  630. ZeroMemory(pbData, cbActualBufferSize); // temporary aggressiveness
  631. bufferValue[0] = 0; // preset it to an empty string in case of bogosity
  632. bufferValue[cbData / sizeof(WCHAR)] = 0;
  633. LONG lRes = // and make the actual call
  634. ::RegQueryValueExW
  635. (
  636. m_hKey,
  637. pszValueName,
  638. NULL, // DWORD* reserved
  639. &dwType,
  640. pbData,
  641. &cbData
  642. );
  643. FixBadRegistryStringValue(m_hKey, pszValueName, cbActualBufferSize, lRes, dwType, pbData, &cbData);
  644. if (lRes != ERROR_SUCCESS && lRes != ERROR_MORE_DATA)
  645. {
  646. return HRESULT_FROM_WIN32(lRes);
  647. }
  648. // type check
  649. VsVerifyThrow
  650. (
  651. dwType == REG_SZ || dwType == REG_EXPAND_SZ,
  652. "registry type mismatch in VQueryValue(F::CBaseStringBuffer*)"
  653. );
  654. if (lRes == ERROR_SUCCESS)
  655. {
  656. return S_OK;
  657. }
  658. // lRes == ERROR_MORE_DATA
  659. // try once with larger buffer
  660. m_cbMaxValueLength = NVseeLibAlgorithm_RkMaximum(m_cbMaxValueLength, cbData);
  661. m_fMaxValueLengthValid = true;
  662. }
  663. // try again, copy/paste/edit above code
  664. // edit: we don't check for ERROR_MORE_DATA again
  665. // Race condition: if registry is being modified, growing, while we read it,
  666. // we fail to grow our buffer more than once.
  667. CStringW_CFixedSizeBuffer bufferValue(pstrValue, m_cbMaxValueLength / sizeof(WCHAR));
  668. DWORD dwType = REG_DWORD; // initialize to other than a type we want
  669. DWORD cbActualBufferSize = m_cbMaxValueLength;
  670. DWORD cbData = cbActualBufferSize - sizeof(WCHAR);
  671. BYTE* pbData = reinterpret_cast<BYTE*>(static_cast<PWSTR>(bufferValue));
  672. ZeroMemory(pbData, cbActualBufferSize); // temporary aggressiveness
  673. bufferValue[0] = 0; // preset it to an empty string in case of bogosity
  674. bufferValue[cbData / sizeof(WCHAR)] = 0;
  675. LONG lRes = // and make the actual call
  676. ::RegQueryValueExW
  677. (
  678. m_hKey,
  679. pszValueName,
  680. NULL, // DWORD* reserved
  681. &dwType,
  682. pbData,
  683. &cbData
  684. );
  685. FixBadRegistryStringValue(m_hKey, pszValueName, cbActualBufferSize, lRes, dwType, pbData, &cbData);
  686. // any error other than more data, throw without a type check
  687. if (lRes != ERROR_SUCCESS)
  688. {
  689. return HRESULT_FROM_WIN32(lRes);
  690. }
  691. // type check
  692. VsVerifyThrow
  693. (
  694. dwType == REG_SZ || dwType == REG_EXPAND_SZ,
  695. "registry type mismatch in VQueryValue(F::CBaseStringBuffer*)"
  696. );
  697. return S_OK;
  698. }
  699. /*-----------------------------------------------------------------------------
  700. Name: CRegKey2::HrQueryValue
  701. @mfunc
  702. call RegQueryValueEx, expecting [REG_DWORD]
  703. throws E_FAIL on type mismatch or returned FILE_NOT_FOUND is value doesn't exist
  704. @owner a-peteco
  705. -----------------------------------------------------------------------------*/
  706. HRESULT
  707. F::CRegKey2::HrQueryValue
  708. (
  709. PCWSTR pszValueName, // @parm [in] same as RegQueryValueEx
  710. DWORD* pdwValue // @parm [out] same as RegQueryValueEx for dwtype==REG_DWORD
  711. ) const throw(CErr)
  712. {
  713. FN_PROLOG_HR;
  714. VsVerifyThrow(m_fValid, __FUNCTION__);
  715. VsVerifyThrow(pdwValue, __FUNCTION__);
  716. DWORD dwType = REG_SZ; // initialize to other than a type we want
  717. DWORD cbData = sizeof(DWORD);
  718. BYTE* pbData = reinterpret_cast<BYTE*>(pdwValue);
  719. IFREGFAILED_ORIGINATE_AND_EXIT(::RegQueryValueExW
  720. (
  721. m_hKey,
  722. pszValueName,
  723. NULL, // DWORD* reserved
  724. &dwType,
  725. pbData,
  726. &cbData
  727. ));
  728. // type check
  729. ASSERT2(dwType == REG_DWORD, "registry type mismatch in VQueryValue(F::CBaseStringBuffer*)");
  730. FN_EPILOG;
  731. }
  732. /*-----------------------------------------------------------------------------
  733. Name: CRegKey2::ThrQueryInfo
  734. @mfunc
  735. this is a wrapper of ::RegQueryInfoExW, with its entire confusing
  736. huge parameter list, including class, reserved, security, filetime..
  737. it adds
  738. - throw
  739. - Win95 bug fix
  740. - pessimism over the possibility of REG_SZ missing terminal nul
  741. @owner
  742. -----------------------------------------------------------------------------*/
  743. /*static*/ VOID
  744. F::CRegKey2::ThrQueryInfo
  745. (
  746. HKEY hKey,
  747. WCHAR* pClass,
  748. DWORD* pcbClass,
  749. DWORD* pReserved,
  750. DWORD* pcSubKeys,
  751. DWORD* pcchMaxSubKeyLength,
  752. DWORD* pcchMaxClassLength,
  753. DWORD* pcValues,
  754. DWORD* pcchMaxValueNameLength,
  755. DWORD* pcbMaxValueDataLength,
  756. DWORD* pcbSecurityDescriptorLength,
  757. FILETIME* pftLastWriteTime
  758. ) throw(CErr)
  759. {
  760. VSEE_ASSERT_CAN_THROW();
  761. // all parameters can be NULL
  762. LONG lRes =
  763. ::RegQueryInfoKeyW
  764. (
  765. hKey,
  766. pClass,
  767. pcbClass,
  768. pReserved,
  769. pcSubKeys,
  770. pcchMaxSubKeyLength,
  771. pcchMaxClassLength,
  772. pcValues,
  773. pcchMaxValueNameLength,
  774. pcbMaxValueDataLength,
  775. pcbSecurityDescriptorLength,
  776. pftLastWriteTime
  777. );
  778. if (lRes != ERROR_SUCCESS)
  779. {
  780. NVseeLibError_VThrowWin32(lRes);
  781. }
  782. /*
  783. This is supposedly required if the call is made on a remote Windows95 machine.
  784. It was done in \vs\src\vsee\pkgs\va\vsa\WizCompD.cpp, and the NT4sp3 code looks like:
  785. //
  786. // Check for a downlevel Win95 server, which requires
  787. // us to work around their BaseRegQueryInfoKey bugs.
  788. // They do not account for Unicode correctly.
  789. //
  790. if (IsWin95Server(DereferenceRemoteHandle(hKey),dwVersion)) {
  791. //
  792. // This is a Win95 server.
  793. // Double the maximum value name length and
  794. // maximum value data length to account for
  795. // the Unicode translation that Win95 forgot
  796. // to account for.
  797. //
  798. cbMaxValueNameLen *= sizeof(WCHAR);
  799. cbMaxValueLen *= sizeof(WCHAR);
  800. }
  801. notice they don't touch cbMaxSubKeyLen
  802. */
  803. if (pcchMaxSubKeyLength != NULL)
  804. {
  805. *pcchMaxSubKeyLength *= sizeof(WCHAR);
  806. // fudge some more
  807. *pcchMaxSubKeyLength += 3 * sizeof(WCHAR);
  808. }
  809. // Some REG_SZ values are missing their terminal nul, so add room for
  810. // some here so anyone that allocates a buffer based on this has room to add
  811. // the terminal nul. Add a second out of paranoia.
  812. if (pcbMaxValueDataLength != NULL)
  813. {
  814. *pcbMaxValueDataLength += 3 * sizeof(WCHAR);
  815. }
  816. }
  817. /*-----------------------------------------------------------------------------
  818. Name: CRegKey2::ThrQueryValuesInfo
  819. @mfunc
  820. a subset of RegQueryInfoKey, that only returns information about values;
  821. this is useful for users of RegEnumValue / CEnumValues
  822. @owner
  823. -----------------------------------------------------------------------------*/
  824. /*static*/ VOID
  825. F::CRegKey2::ThrQueryValuesInfo
  826. (
  827. HKEY hKey, // @parm [in] the HKEY to query value info rom
  828. DWORD* pcValues, // @parm [out] can be NULL, parameter to RegQueryInfoKey
  829. DWORD* pcchMaxValueNameLength, // @parm [out] can be NULL, parameter to RegQueryInfoKey
  830. DWORD* pcbMaxValueDataLength // @parm [out] can be NULL, parameter to RegQueryInfoKey
  831. ) throw(CErr)
  832. {
  833. VSEE_ASSERT_CAN_THROW();
  834. ThrQueryInfo
  835. (
  836. hKey,
  837. NULL, // pClass,
  838. NULL, // pcbClass,
  839. NULL, // pReserved,
  840. NULL, // pcSubKeys,
  841. NULL, // pcchMaxSubKeyLength,
  842. NULL, // pcchMaxClassLength,
  843. pcValues,
  844. pcchMaxValueNameLength,
  845. pcbMaxValueDataLength,
  846. NULL, // pcbSecurityDescriptorLength,
  847. NULL // pftLastWriteTime
  848. );
  849. // fudge them up a bit
  850. if (pcchMaxValueNameLength != NULL)
  851. {
  852. *pcchMaxValueNameLength += 3 * sizeof(WCHAR);
  853. }
  854. if (pcbMaxValueDataLength != NULL)
  855. {
  856. *pcbMaxValueDataLength += 3 * sizeof(WCHAR);
  857. }
  858. }
  859. /*-----------------------------------------------------------------------------
  860. Name: CRegKey2::ThrQuerySubKeysInfo
  861. @mfunc
  862. a subset of RegQueryInfoKey, that only returns information about subkeys;
  863. this is useful for users of RegEnumKeyEx / CEnumKeys
  864. @owner
  865. -----------------------------------------------------------------------------*/
  866. /*static*/ VOID
  867. F::CRegKey2::ThrQuerySubKeysInfo
  868. (
  869. HKEY hKey, // @parm [in] the HKEY to query value info rom
  870. DWORD* pcSubKeys, // @parm [out] can be NULL, parameter to RegQueryInfoKey
  871. DWORD* pcchMaxSubKeyLength // @parm [out] can be NULL, parameter to RegQueryInfoKey
  872. ) throw(CErr)
  873. {
  874. VSEE_ASSERT_CAN_THROW();
  875. ThrQueryInfo
  876. (
  877. hKey,
  878. NULL, // pClass,
  879. NULL, // pcbClass,
  880. NULL, // pReserved,
  881. pcSubKeys,
  882. pcchMaxSubKeyLength,
  883. NULL, // pcchMaxClassLength,
  884. NULL, // pcValues,
  885. NULL, // pcchMaxValueNameLength,
  886. NULL, // pcbMaxValueDataLength,
  887. NULL, // pcbSecurityDescriptorLength,
  888. NULL // pftLastWriteTime
  889. );
  890. // fudge them up a bit
  891. if (pcchMaxSubKeyLength != NULL)
  892. {
  893. *pcchMaxSubKeyLength += 3 * sizeof(WCHAR);
  894. }
  895. }
  896. /*-----------------------------------------------------------------------------
  897. Name: CRegKey2::ThrQueryValuesInfo
  898. @mfunc
  899. a subset of RegQueryInfoKey, that only returns information about values;
  900. this is useful for users of RegEnumValue / CEnumValues
  901. @owner
  902. -----------------------------------------------------------------------------*/
  903. VOID
  904. F::CRegKey2::ThrQueryValuesInfo
  905. (
  906. DWORD* pcValues, // @parm [out] can be NULL, parameter to RegQueryInfoKey
  907. DWORD* pcchMaxValueNameLength, // @parm [out] can be NULL, parameter to RegQueryInfoKey
  908. DWORD* pcbMaxValueLength // @parm [out] can be NULL, parameter to RegQueryInfoKey
  909. ) const throw(CErr)
  910. {
  911. VSEE_ASSERT_CAN_THROW();
  912. ThrQueryValuesInfo(*this, pcValues, pcchMaxValueNameLength, pcbMaxValueLength);
  913. }
  914. /*-----------------------------------------------------------------------------
  915. Name: CRegKey2::ThrEnumValue
  916. @mfunc
  917. call RegEnumValue; consumed by CEnumValues
  918. @owner
  919. -----------------------------------------------------------------------------*/
  920. /*static*/ VOID
  921. F::CRegKey2::ThrEnumValue
  922. (
  923. HKEY hKey, // @parm [in] parameter to RegEnumValue
  924. DWORD dwIndex, // @parm [in] parameter to RegEnumValue
  925. PWSTR pszValueName, // @parm [in] can be NULL, parameter to RegEnumValue
  926. DWORD* pcchValueNameLength, // @parm [out] can be NULL, parameter to RegEnumValue
  927. DWORD* pdwType, // @parm [out] can be NULL, parameter to RegEnumValue
  928. BYTE* pbData, // @parm [out] can be NULL, parameter to RegEnumValue
  929. DWORD* pcbData // @parm [in out] can be NULL, parameter to RegEnumValue
  930. ) throw(CErr)
  931. {
  932. VSEE_ASSERT_CAN_THROW();
  933. DWORD cbActualBufferSize = 0;
  934. if (pcbData != NULL)
  935. {
  936. cbActualBufferSize = *pcbData;
  937. }
  938. LONG lRes =
  939. ::RegEnumValueW
  940. (
  941. hKey,
  942. dwIndex,
  943. pszValueName,
  944. pcchValueNameLength,
  945. NULL, // DWORD* reserved,
  946. pdwType,
  947. pbData,
  948. pcbData
  949. );
  950. if (pdwType != NULL && pcbData != NULL)
  951. {
  952. FixBadRegistryStringValue(hKey, pszValueName, cbActualBufferSize, lRes, *pdwType, pbData, pcbData);
  953. }
  954. if (lRes != ERROR_SUCCESS)
  955. {
  956. NVseeLibError_VThrowWin32(lRes);
  957. }
  958. }
  959. /*-----------------------------------------------------------------------------
  960. Name: CRegKey2::RegEnumKey
  961. @mfunc
  962. call RegEnumKeyEx; consumed by CEnumKeys
  963. @owner
  964. -----------------------------------------------------------------------------*/
  965. /*static*/ LONG
  966. F::CRegKey2::RegEnumKey
  967. (
  968. HKEY hKey, // @parm [in] parameter to RegEnumKeyEx
  969. DWORD dwIndex, // @parm [in] parameter to RegEnumKeyEx
  970. PWSTR pszSubKeyName, // @parm [out] parameter to RegEnumKeyEx
  971. DWORD* pcchSubKeyNameLength // @parm [out] parameter to RegEnumKeyEx
  972. ) throw(CErr)
  973. {
  974. VSEE_ASSERT_CAN_THROW();
  975. FILETIME ftIgnoreLastWriteTime = { 0, 0 };
  976. LONG lRes =
  977. ::RegEnumKeyExW
  978. (
  979. hKey,
  980. dwIndex,
  981. pszSubKeyName,
  982. pcchSubKeyNameLength,
  983. NULL, // reserved
  984. NULL, // class
  985. NULL, // cbClass
  986. &ftIgnoreLastWriteTime
  987. );
  988. return lRes;
  989. }
  990. /*-----------------------------------------------------------------------------
  991. Name: CRegKey2::ThrEnumKey
  992. @mfunc
  993. call RegEnumKeyEx; consumed by CEnumKeys
  994. @owner
  995. -----------------------------------------------------------------------------*/
  996. /*static*/ VOID
  997. F::CRegKey2::ThrEnumKey
  998. (
  999. HKEY hKey, // @parm [in] parameter to RegEnumKeyEx
  1000. DWORD dwIndex, // @parm [in] parameter to RegEnumKeyEx
  1001. PWSTR pszSubKeyName, // @parm [out] parameter to RegEnumKeyEx
  1002. DWORD* pcchSubKeyNameLength // @parm [out] parameter to RegEnumKeyEx
  1003. ) throw(CErr)
  1004. {
  1005. LONG lRes = RegEnumKey(hKey, dwIndex, pszSubKeyName, pcchSubKeyNameLength);
  1006. if (lRes != ERROR_SUCCESS)
  1007. {
  1008. NVseeLibError_VThrowWin32(lRes);
  1009. }
  1010. }
  1011. /*-----------------------------------------------------------------------------
  1012. Name: CRegKey2::ThrRecurseDeleteKey
  1013. @mfunc
  1014. Recursively deletes a subkey.
  1015. @owner
  1016. -----------------------------------------------------------------------------*/
  1017. VOID
  1018. F::CRegKey2::ThrRecurseDeleteKey(LPCWSTR lpszKey)
  1019. {
  1020. VSEE_ASSERT_CAN_THROW();
  1021. VsVerifyThrowHr(lpszKey != NULL, "lpszKey is NULL", E_UNEXPECTED);
  1022. VSASSERT(!m_fKnownSam || (m_samDesired & KEY_WRITE), "Attempt to delete key contents when key not opened with KEY_WRITE");
  1023. F::CRegKey2 key;
  1024. key.ThrOpen(m_hKey, lpszKey, KEY_READ | KEY_WRITE);
  1025. FILETIME time;
  1026. DWORD dwSize = 256;
  1027. WCHAR szBuffer[256];
  1028. while (::RegEnumKeyExW(key.m_hKey, 0, szBuffer, &dwSize, NULL, NULL, NULL,
  1029. &time)==ERROR_SUCCESS)
  1030. {
  1031. key.ThrRecurseDeleteKey(szBuffer);
  1032. dwSize = 256;
  1033. }
  1034. DeleteSubKey(lpszKey);
  1035. }
  1036. /*-----------------------------------------------------------------------------
  1037. Name: CRegKey2::DeleteSubKey
  1038. @mfunc
  1039. Deletes a subkey underneath the current key. Basically a wrapper for RegDeleteKey.
  1040. Used by ThrRecurseDeleteKey
  1041. @owner
  1042. -----------------------------------------------------------------------------*/
  1043. VOID
  1044. F::CRegKey2::DeleteSubKey(LPCWSTR lpszSubKey)
  1045. {
  1046. VSEE_ASSERT_CAN_THROW();
  1047. VsVerifyThrowHr(lpszSubKey != NULL, "lpszSubKey is NULL", E_UNEXPECTED);
  1048. VsVerifyThrowHr(m_hKey != NULL, "m_hKey is NULL", E_UNEXPECTED);
  1049. VSASSERT(!m_fKnownSam || (m_samDesired & KEY_SET_VALUE), "Attempt to set value when key not opened with KEY_SET_VALUE");
  1050. ::RegDeleteKeyW(m_hKey, lpszSubKey);
  1051. }