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.

734 lines
20 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: N C R E G Q . C P P
  7. //
  8. // Contents: HrRegQuery functions.
  9. //
  10. // Notes:
  11. //
  12. // Author: shaunco 5 Jun 1998
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.h>
  16. #pragma hdrstop
  17. #include "ncdebug.h"
  18. #include "ncreg.h"
  19. //+---------------------------------------------------------------------------
  20. //
  21. // Function: ExpandEnvironmentStringsIntoTstring
  22. //
  23. // Purpose: Call ExpandEnvironmentStrings and provide a buffer that
  24. // is a tstring.
  25. //
  26. // Arguments:
  27. // pszSrc [in] The string to expand. Can be empty, but not NULL.
  28. // pstrDst [out] The expanded version.
  29. //
  30. // Returns: nothing
  31. //
  32. // Author: shaunco 6 Jun 1998
  33. //
  34. // Notes:
  35. //
  36. VOID
  37. ExpandEnvironmentStringsIntoTstring (
  38. PCWSTR pszSrc,
  39. tstring* pstrDst)
  40. {
  41. // Initialize the output parameter.
  42. //
  43. pstrDst->erase();
  44. DWORD cch = lstrlenW (pszSrc);
  45. if (cch)
  46. {
  47. // Start with 64 more characters than are in pszSrc.
  48. //
  49. cch += 64;
  50. // assign will reserve cch characters and set them all to 0.
  51. // checking capacity afterwards ensures the allocation made
  52. // internally didn't fail.
  53. //
  54. pstrDst->assign (cch, 0);
  55. if (cch <= pstrDst->capacity ())
  56. {
  57. DWORD cchIncludingNull;
  58. cchIncludingNull = ExpandEnvironmentStringsW (
  59. pszSrc,
  60. (PWSTR)pstrDst->data (),
  61. cch + 1);
  62. Assert (cchIncludingNull);
  63. cch = cchIncludingNull - 1;
  64. // If we didn't have enough room, reserve the required amount.
  65. //
  66. if (cch > pstrDst->capacity ())
  67. {
  68. pstrDst->assign (cch, 0);
  69. if (cch <= pstrDst->capacity ())
  70. {
  71. ExpandEnvironmentStringsW (
  72. pszSrc,
  73. (PWSTR)pstrDst->data (),
  74. cch + 1);
  75. }
  76. }
  77. // Make sure the string's inards are correct.
  78. //
  79. pstrDst->resize (cch);
  80. }
  81. }
  82. }
  83. //+---------------------------------------------------------------------------
  84. //
  85. // Function: HrRegQueryTypeWithAlloc
  86. //
  87. // Purpose: Retrieves a type'd value from the registry and returns a
  88. // pre-allocated buffer with the data and optionally the size of
  89. // the returned buffer.
  90. //
  91. // Arguments:
  92. // hkey [in] Handle of parent key
  93. // pszValueName [in] Name of value to query
  94. // ppbValue [out] Buffer with binary data
  95. // pcbValue [out] Size of buffer in bytes. If NULL, size is not
  96. // returned.
  97. //
  98. // Returns: S_OK or an HRESULT_FROM_WIN32 error code.
  99. //
  100. // Author: danielwe 16 Apr 1997
  101. //
  102. // Notes: Free the returned buffer with MemFree.
  103. //
  104. HRESULT
  105. HrRegQueryTypeWithAlloc (
  106. HKEY hkey,
  107. PCWSTR pszValueName,
  108. DWORD dwType,
  109. LPBYTE* ppbValue,
  110. DWORD* pcbValue)
  111. {
  112. HRESULT hr;
  113. DWORD dwTypeRet;
  114. LPBYTE pbData;
  115. DWORD cbData;
  116. Assert(hkey);
  117. Assert(ppbValue);
  118. // Get the value.
  119. //
  120. hr = HrRegQueryValueWithAlloc(hkey, pszValueName, &dwTypeRet,
  121. &pbData, &cbData);
  122. // It's type should be REG_BINARY. (duh).
  123. //
  124. if ((S_OK == hr) && (dwTypeRet != dwType))
  125. {
  126. MemFree(pbData);
  127. pbData = NULL;
  128. TraceTag(ttidError, "Expected a type of REG_BINARY for %S.",
  129. pszValueName);
  130. hr = HRESULT_FROM_WIN32 (ERROR_INVALID_DATATYPE);
  131. }
  132. // Assign the output parameters.
  133. if (S_OK == hr)
  134. {
  135. *ppbValue = pbData;
  136. if (pcbValue)
  137. {
  138. *pcbValue = cbData;
  139. }
  140. }
  141. else
  142. {
  143. *ppbValue = NULL;
  144. if (pcbValue)
  145. {
  146. *pcbValue = 0;
  147. }
  148. }
  149. TraceHr (ttidError, FAL, hr,
  150. (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr),
  151. "HrRegQueryTypeWithAlloc");
  152. return hr;
  153. }
  154. //+---------------------------------------------------------------------------
  155. //
  156. // Function: HrRegQueryDword
  157. //
  158. // Purpose: Gets a DWORD from the registry. Checks that its type and
  159. // size are correct. Easier to understand than HrRegQueryValueEx
  160. // with 5 parameters. Type safe (no LPBYTE stuff).
  161. //
  162. // Arguments:
  163. // hkey [in] The registry key.
  164. // pszValueName [in] The name of the value to get.
  165. // pdwValue [out] The returned DWORD value if successful. Zero
  166. // if not.
  167. //
  168. // Returns: S_OK or HRESULT_FROM_WIN32.
  169. //
  170. // Author: shaunco 27 Mar 1997
  171. //
  172. // Side Effects: On error, the output DWORD is set to zero to line-up
  173. // with the rules of COM in this regard.
  174. //
  175. HRESULT
  176. HrRegQueryDword (
  177. HKEY hkey,
  178. PCWSTR pszValueName,
  179. LPDWORD pdwValue)
  180. {
  181. Assert (hkey);
  182. Assert (pszValueName);
  183. Assert (pdwValue);
  184. // Get the value.
  185. DWORD dwType;
  186. DWORD cbData = sizeof(DWORD);
  187. HRESULT hr = HrRegQueryValueEx (hkey, pszValueName, &dwType,
  188. (LPBYTE)pdwValue, &cbData);
  189. // It's type should be REG_DWORD. (duh).
  190. //
  191. if ((S_OK == hr) && (REG_DWORD != dwType))
  192. {
  193. TraceTag (ttidError, "Expected a type of REG_DWORD for %S.",
  194. pszValueName);
  195. hr = HRESULT_FROM_WIN32 (ERROR_INVALID_DATATYPE);
  196. }
  197. // It's size should be correct too.
  198. //
  199. AssertSz (FImplies(S_OK == hr, sizeof(DWORD) == cbData),
  200. "Expected sizeof(DWORD) bytes to be returned.");
  201. // Make sure we initialize the output value on error.
  202. // (We don't know for sure that RegQueryValueEx does this.)
  203. //
  204. if (S_OK != hr)
  205. {
  206. *pdwValue = 0;
  207. }
  208. TraceHr (ttidError, FAL, hr,
  209. (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr),
  210. "HrRegQueryDword");
  211. return hr;
  212. }
  213. //+---------------------------------------------------------------------------
  214. //
  215. // Function: HrRegQueryExpandString
  216. //
  217. // Purpose: Query a REG_EXPAND_SZ value from the registry and
  218. // expand it using ExpandEnvironmentStrings. Return the
  219. // result in a tstring.
  220. //
  221. // Arguments:
  222. // hkey [in] The parent HKEY of szValueName
  223. // pszValueName [in] The name of the value to query.
  224. // pstrValue [out] The returned (expanded) value.
  225. //
  226. // Returns: S_OK or an error code.
  227. //
  228. // Author: shaunco 6 Jun 1998
  229. //
  230. // Notes:
  231. //
  232. HRESULT
  233. HrRegQueryExpandString (
  234. HKEY hkey,
  235. PCWSTR pszValueName,
  236. tstring* pstrValue)
  237. {
  238. Assert (hkey);
  239. Assert (pszValueName);
  240. Assert (pstrValue);
  241. tstring strToExpand;
  242. HRESULT hr = HrRegQueryTypeString (hkey, pszValueName,
  243. REG_EXPAND_SZ, &strToExpand);
  244. if (S_OK == hr)
  245. {
  246. ExpandEnvironmentStringsIntoTstring (strToExpand.c_str(), pstrValue);
  247. }
  248. TraceHr (ttidError, FAL, hr,
  249. (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) ||
  250. (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr),
  251. "HrRegQueryExpandString");
  252. return hr;
  253. }
  254. //+---------------------------------------------------------------------------
  255. //
  256. // Function: HrRegQueryInfoKey
  257. //
  258. // Purpose: Retrieves information about a registry key by calling
  259. // RegQueryInfoKey.
  260. //
  261. // Arguments:
  262. // hkey [in]
  263. // pszClass [out]
  264. // pcbClass [inout]
  265. // pcSubKeys [out]
  266. // pcbMaxSubKeyLen [out] See the Win32 documentation for the
  267. // pcbMaxClassLen [out] RegQueryInfoKey function.
  268. // pcValues [out]
  269. // pcbMaxValueNameLen [out]
  270. // pcbMaxValueLen [out]
  271. // pcbSecurityDescriptor [out]
  272. // pftLastWriteTime [out]
  273. //
  274. // Returns: S_OK or an HRESULT_FROM_WIN32 error code.
  275. //
  276. // Author: BillBe 28 Aug 1998
  277. //
  278. // Notes: Note that pcbClass is an *in/out* param. Set this to the size
  279. // of the buffer pointed to by pszClass *before* calling this
  280. // function!
  281. //
  282. HRESULT
  283. HrRegQueryInfoKey (
  284. IN HKEY hkey,
  285. OUT PWSTR pszClass,
  286. IN OUT LPDWORD pcbClass,
  287. OUT LPDWORD pcSubKeys,
  288. OUT LPDWORD pcbMaxSubKeyLen,
  289. OUT LPDWORD pcbMaxClassLen,
  290. OUT LPDWORD pcValues,
  291. OUT LPDWORD pcbMaxValueNameLen,
  292. OUT LPDWORD pcbMaxValueLen,
  293. OUT LPDWORD pcbSecurityDescriptor,
  294. OUT PFILETIME pftLastWriteTime)
  295. {
  296. Assert(hkey);
  297. LONG lr = RegQueryInfoKeyW(hkey, pszClass, pcbClass, NULL,pcSubKeys,
  298. pcbMaxSubKeyLen, pcbMaxClassLen, pcValues, pcbMaxValueNameLen,
  299. pcbMaxValueLen, pcbSecurityDescriptor, pftLastWriteTime);
  300. HRESULT hr = HRESULT_FROM_WIN32 (lr);
  301. TraceHr (ttidError, FAL, hr, FALSE, "HrRegQueryInfoKey");
  302. return hr;
  303. }
  304. //+---------------------------------------------------------------------------
  305. //
  306. // Function: HrRegQueryStringAsUlong
  307. //
  308. // Purpose: Reads a REG_SZ from the registry and converts it to a ulong
  309. // before returning
  310. //
  311. // Arguments:
  312. // hkey [in] The registry key.
  313. // pszValueName [in] The name of the value to get.
  314. // nBase [in] The numeric base to convert to
  315. // pulValue [out] The returned converted string if successful.
  316. //
  317. // Returns: S_OK or an HRESULT_FROM_WIN32 error code.
  318. //
  319. // Author: billbe 13 Jun 1997
  320. //
  321. // Notes:
  322. //
  323. //
  324. HRESULT
  325. HrRegQueryStringAsUlong (
  326. IN HKEY hkey,
  327. IN PCWSTR pszValueName,
  328. IN int nBase,
  329. OUT ULONG* pulValue)
  330. {
  331. Assert (hkey);
  332. Assert (pszValueName);
  333. Assert (nBase);
  334. Assert (pulValue);
  335. // Get the value.
  336. //
  337. tstring strValue;
  338. HRESULT hr = HrRegQueryString (hkey, pszValueName, &strValue);
  339. if (S_OK == hr)
  340. {
  341. // Convert and assign the output parameters.
  342. PWSTR pszStopString;
  343. *pulValue = wcstoul (strValue.c_str(), &pszStopString, nBase);
  344. }
  345. else
  346. {
  347. *pulValue = 0;
  348. }
  349. TraceHr (ttidError, FAL, hr,
  350. (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr),
  351. "HrRegQueryStringAsUlong");
  352. return hr;
  353. }
  354. //+---------------------------------------------------------------------------
  355. //
  356. // Function: HrRegQueryTypeString
  357. //
  358. // Purpose: Query a REG_SZ or REG_EXPAND_SZ value and returns it
  359. // in a tstring.
  360. //
  361. // Arguments:
  362. // hkey [in] The parent HKEY of szValueName
  363. // pszValueName [in] The name of the value to query.
  364. // dwType [in] REG_SZ or REG_EXPAND_SZ
  365. // pstr [out] The returned value.
  366. //
  367. // Returns: S_OK or an error code.
  368. //
  369. // Author: shaunco 6 Jun 1998
  370. //
  371. // Notes: REG_EXPAND_SZ values ARE NOT expanded using
  372. // ExpandEnvironentStrings. Use HrRegQueryExpandString instead.
  373. //
  374. HRESULT
  375. HrRegQueryTypeString (
  376. IN HKEY hkey,
  377. IN PCWSTR pszValueName,
  378. IN DWORD dwType,
  379. OUT tstring* pstr)
  380. {
  381. Assert (hkey);
  382. Assert (pszValueName);
  383. Assert (pstr);
  384. AssertSz ((REG_SZ == dwType) ||
  385. (REG_EXPAND_SZ == dwType), "Only REG_SZ or REG_EXPAND_SZ "
  386. "types accepted.");
  387. BOOL fErase = TRUE;
  388. // Get size of the data.
  389. //
  390. DWORD dwTypeRet;
  391. DWORD cbData = 0;
  392. HRESULT hr = HrRegQueryValueEx (hkey, pszValueName, &dwTypeRet,
  393. NULL, &cbData);
  394. // Make sure it has the correct type.
  395. //
  396. if ((S_OK == hr) && (dwTypeRet != dwType))
  397. {
  398. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
  399. }
  400. if (S_OK == hr)
  401. {
  402. // Compute the number of characters in the data including the
  403. // NULL terminator. After dividing the number of bytes by
  404. // the sizeof a WCHAR, add 1 if there is a remainder. If we didn't,
  405. // and the number of bytes was not a multiple of the sizeof a WCHAR,
  406. // we'd come up short because integer division rounds down.
  407. // (The only time I can think of cbData would not be a multiple
  408. // of sizeof(WCHAR) is if the registry data were somehow corrupted.
  409. // It's not that I think corruption deserves a special case, but
  410. // we shouldn't AV in light of it.)
  411. //
  412. DWORD cchIncludingNull;
  413. cchIncludingNull = cbData / sizeof(WCHAR);
  414. if (cbData % sizeof(WCHAR))
  415. {
  416. cchIncludingNull++;
  417. }
  418. // If we have more than just the terminator, allocate and
  419. // get the string. Otherwise, we want it empty.
  420. //
  421. if (cchIncludingNull > 1)
  422. {
  423. // Reserve room for the correct number of characters.
  424. // cch is the count of characters without the terminator
  425. // since that is what tstring operates with.
  426. //
  427. DWORD cch = cchIncludingNull - 1;
  428. Assert (cch > 0);
  429. // assign will reserve cch characters and set them all to 0.
  430. // checking capacity afterwards ensures the allocation made
  431. // internally didn't fail.
  432. //
  433. pstr->assign (cch, 0);
  434. if (cch <= pstr->capacity ())
  435. {
  436. hr = HrRegQueryValueEx (hkey, pszValueName, &dwType,
  437. (LPBYTE)pstr->data (), &cbData);
  438. if (S_OK == hr)
  439. {
  440. // If everything went according to plan, the length
  441. // of the string should now match what wcslen
  442. // returns on the string itself. The reason it will
  443. // match is because we passed cch to assign.
  444. //
  445. Assert (pstr->length() == (size_t)wcslen (pstr->c_str()));
  446. fErase = FALSE;
  447. }
  448. }
  449. else
  450. {
  451. hr = E_OUTOFMEMORY;
  452. }
  453. }
  454. }
  455. // Empty the output string on failure or if we think it should be
  456. // empty.
  457. //
  458. if (FAILED(hr) || fErase)
  459. {
  460. pstr->erase();
  461. }
  462. TraceHr (ttidError, FAL, hr,
  463. (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) ||
  464. (HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE) == hr),
  465. "HrRegQueryTypeString");
  466. return hr;
  467. }
  468. //+---------------------------------------------------------------------------
  469. //
  470. // Function: HrRegQueryTypeSzBuffer
  471. //
  472. // Purpose: Gets a string from the registry using the given buffer. Checks
  473. // that its type is correct. Type safe (no LPBYTE stuff).
  474. //
  475. // Arguments:
  476. // hkey [in] The registry key.
  477. // pszValueName [in] The name of the value to get.
  478. // dwType [in] Desired type. (REG_SZ, REG_EXPAND_SZ, etc.)
  479. // szData [out] String buffer to hold the data.
  480. // pcbData [in,out] IN: Number of *bytes* in buffer pointed to by
  481. // szData. OUT: Number of bytes actually copied
  482. // into the buffer.
  483. //
  484. // Returns: S_OK or an HRESULT_FROM_WIN32 error code.
  485. //
  486. // Author: danielwe 3 Apr 1997
  487. //
  488. // Notes: If the function fails, the buffer passed in is guaranteed to
  489. // be an empty string.
  490. //
  491. HRESULT
  492. HrRegQueryTypeSzBuffer (
  493. IN HKEY hkey,
  494. IN PCWSTR pszValueName,
  495. IN DWORD dwType,
  496. OUT PWSTR pszData,
  497. OUT DWORD* pcbData)
  498. {
  499. Assert (hkey);
  500. Assert (pszValueName);
  501. Assert (pcbData);
  502. DWORD dwTypeRet;
  503. HRESULT hr = HrRegQueryValueEx (hkey, pszValueName, &dwTypeRet,
  504. (LPBYTE)pszData, pcbData);
  505. if ((S_OK == hr) && (dwTypeRet != dwType))
  506. {
  507. TraceTag (ttidError, "Expected a type of 0x%x for %S.",
  508. dwType, pszValueName);
  509. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
  510. }
  511. if (FAILED(hr) && pszData)
  512. {
  513. // Make sure empty string is returned on failure.
  514. //
  515. *pszData = 0;
  516. }
  517. TraceHr (ttidError, FAL, hr,
  518. (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr),
  519. "HrRegQueryTypeSzBuffer");
  520. return hr;
  521. }
  522. //+---------------------------------------------------------------------------
  523. //
  524. // Function: HrRegQueryValueEx
  525. //
  526. // Purpose: Retrieves the data from the given registry value by calling
  527. // RegQueryValueEx.
  528. //
  529. // Arguments:
  530. // hkey [in]
  531. // pszValueName [in]
  532. // pdwType [out] See the Win32 documentation for the
  533. // pbData [out] RegQueryValueEx function.
  534. // pcbData [in,out]
  535. //
  536. // Returns: S_OK or an HRESULT_FROM_WIN32 error code.
  537. //
  538. // Author: shaunco 25 Feb 1997
  539. //
  540. // Notes: Note that pcbData is an *in/out* param. Set this to the size
  541. // of the buffer pointed to by pbData *before* calling this
  542. // function!
  543. //
  544. HRESULT
  545. HrRegQueryValueEx (
  546. IN HKEY hkey,
  547. IN PCWSTR pszValueName,
  548. OUT LPDWORD pdwType,
  549. OUT LPBYTE pbData,
  550. OUT LPDWORD pcbData)
  551. {
  552. Assert (hkey);
  553. AssertSz (FImplies(pbData && pcbData, pdwType),
  554. "pdwType not provided to HrRegQueryValueEx. You should be "
  555. "retrieving the type as well so you can make sure it is "
  556. "correct.");
  557. LONG lr = RegQueryValueExW (hkey, pszValueName, NULL, pdwType,
  558. pbData, pcbData);
  559. HRESULT hr = HRESULT_FROM_WIN32 (lr);
  560. TraceHr (ttidError, FAL, hr,
  561. (ERROR_MORE_DATA == lr) || (ERROR_FILE_NOT_FOUND == lr),
  562. "HrRegQueryValueEx (%S)", pszValueName);
  563. return hr;
  564. }
  565. //+---------------------------------------------------------------------------
  566. //
  567. // Function: HrRegQueryValueWithAlloc
  568. //
  569. // Purpose: Retrieve a registry value in a buffer allocated by this
  570. // function. This goes through the mess of checking the value
  571. // size, allocating the buffer, and then calling back to get the
  572. // actual value. Returns the buffer to the user.
  573. //
  574. // Arguments:
  575. // hkey [in] An open HKEY (the one that contains the value
  576. // to be read)
  577. // pszValueName [in] Name of the registry value
  578. // pdwType [in/out] The REG_ type that we plan to be reading
  579. // ppbBuffer [out] Pointer to an LPBYTE buffer that will contain
  580. // the registry value
  581. // pdwSize [out] Pointer to a DWORD that will contain the size
  582. // of the ppbBuffer.
  583. //
  584. // Returns: S_OK or an HRESULT_FROM_WIN32 error code.
  585. //
  586. // Author: jeffspr 27 Mar 1997
  587. //
  588. HRESULT
  589. HrRegQueryValueWithAlloc (
  590. IN HKEY hkey,
  591. IN PCWSTR pszValueName,
  592. LPDWORD pdwType,
  593. LPBYTE* ppbBuffer,
  594. LPDWORD pdwSize)
  595. {
  596. HRESULT hr;
  597. BYTE abData [256];
  598. DWORD cbData;
  599. BOOL fReQuery = FALSE;
  600. Assert (hkey);
  601. Assert (pdwType);
  602. Assert (ppbBuffer);
  603. // Initialize the output parameters.
  604. //
  605. *ppbBuffer = NULL;
  606. if (pdwSize)
  607. {
  608. *pdwSize = 0;
  609. }
  610. // Get the size of the data, and if it will fit, the data too.
  611. //
  612. cbData = sizeof(abData);
  613. hr = HrRegQueryValueEx (
  614. hkey,
  615. pszValueName,
  616. pdwType,
  617. abData,
  618. &cbData);
  619. if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr)
  620. {
  621. // The data didn't fit, so we'll have to requery for it after
  622. // we allocate our buffer.
  623. //
  624. fReQuery = TRUE;
  625. hr = S_OK;
  626. }
  627. if (S_OK == hr)
  628. {
  629. // Allocate the buffer for the required size.
  630. //
  631. BYTE* pbBuffer = (BYTE*)MemAlloc (cbData);
  632. if (pbBuffer)
  633. {
  634. if (fReQuery)
  635. {
  636. hr = HrRegQueryValueEx (
  637. hkey,
  638. pszValueName,
  639. pdwType,
  640. pbBuffer,
  641. &cbData);
  642. }
  643. else
  644. {
  645. CopyMemory (pbBuffer, abData, cbData);
  646. }
  647. if (S_OK == hr)
  648. {
  649. // Fill in the return values.
  650. //
  651. *ppbBuffer = pbBuffer;
  652. if (pdwSize)
  653. {
  654. *pdwSize = cbData;
  655. }
  656. }
  657. else
  658. {
  659. MemFree (pbBuffer);
  660. }
  661. }
  662. else
  663. {
  664. hr = E_OUTOFMEMORY;
  665. }
  666. }
  667. TraceHr (ttidError, FAL, hr,
  668. (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr),
  669. "HrRegQueryValueWithAlloc");
  670. return hr;
  671. }