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.

1766 lines
40 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. rutil.c
  5. Abstract:
  6. This module contains general utility routines used by umpnpmgr.
  7. PNP_ENTER_SYNCHRONOUS_CALL
  8. PNP_LEAVE_SYNCHRONOUS_CALL
  9. CreateDeviceIDRegKey
  10. IsRootDeviceID
  11. MultiSzAppendW
  12. MultiSzFindNextStringW
  13. MultiSzSearchStringW
  14. MultiSzSizeW
  15. MultiSzDeleteStringW
  16. IsValidDeviceID
  17. IsDevicePhantom
  18. GetDeviceStatus
  19. SetDeviceStatus
  20. ClearDeviceStatus
  21. CopyRegistryTree
  22. PathToString
  23. IsDeviceMoved
  24. MakeKeyVolatile
  25. MakeKeyNonVolatile
  26. OpenLogConfKey
  27. GetActiveService
  28. IsDeviceIdPresent
  29. GetDeviceConfigFlags
  30. MapNtStatusToCmError
  31. IsValidGuid
  32. GuidEqual
  33. GuidFromString
  34. StringFromGuid
  35. Author:
  36. Paula Tomlinson (paulat) 7-12-1995
  37. Environment:
  38. User mode only.
  39. Revision History:
  40. 12-July-1995 paulat
  41. Creation and initial implementation.
  42. --*/
  43. //
  44. // includes
  45. //
  46. #include "precomp.h"
  47. #pragma hdrstop
  48. #include "umpnpi.h"
  49. #include "umpnpdat.h"
  50. #pragma warning(push)
  51. #pragma warning(disable:4214) // warning C4214: nonstandard extension used : bit field types other than int
  52. #pragma warning(disable:4201) // warning C4201: nonstandard extension used : nameless struct/union
  53. #include <winsta.h>
  54. #pragma warning(pop)
  55. #include <syslib.h>
  56. //
  57. // global data
  58. //
  59. extern HKEY ghEnumKey; // Key to HKLM\CCC\System\Enum - DO NOT MODIFY
  60. extern HKEY ghServicesKey; // Key to HKLM\CCC\System\Services - DO NOT MODIFY
  61. extern CRITICAL_SECTION PnpSynchronousCall;
  62. //
  63. // Declare data used in GUID->string conversion (from ole32\common\ccompapi.cxx).
  64. //
  65. static const BYTE GuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
  66. 8, 9, '-', 10, 11, 12, 13, 14, 15 };
  67. static const WCHAR szDigits[] = TEXT("0123456789ABCDEF");
  68. VOID
  69. PNP_ENTER_SYNCHRONOUS_CALL(
  70. VOID
  71. )
  72. {
  73. EnterCriticalSection(&PnpSynchronousCall);
  74. } // PNP_ENTER_SYNCHRONOUS_CALL
  75. VOID
  76. PNP_LEAVE_SYNCHRONOUS_CALL(
  77. VOID
  78. )
  79. {
  80. LeaveCriticalSection(&PnpSynchronousCall);
  81. } // PNP_LEAVE_SYNCHRONOUS_CALL
  82. BOOL
  83. CreateDeviceIDRegKey(
  84. HKEY hParentKey,
  85. LPCWSTR pDeviceID
  86. )
  87. /*++
  88. Routine Description:
  89. This routine creates the specified device id subkeys in the registry.
  90. Arguments:
  91. hParentKey Key under which the device id key will be created
  92. pDeviceID Device instance ID string to open
  93. Return value:
  94. The return value is TRUE if the function suceeds and FALSE if it fails.
  95. --*/
  96. {
  97. WCHAR szBase[MAX_DEVICE_ID_LEN];
  98. WCHAR szDevice[MAX_DEVICE_ID_LEN];
  99. WCHAR szInstance[MAX_DEVICE_ID_LEN];
  100. HKEY hBaseKey, hDeviceKey, hInstanceKey;
  101. if (!SplitDeviceInstanceString(
  102. pDeviceID, szBase, szDevice, szInstance)) {
  103. return FALSE;
  104. }
  105. //
  106. // just try creating each component of the device id
  107. //
  108. if (RegCreateKeyEx(
  109. hParentKey, szBase, 0, NULL, REG_OPTION_NON_VOLATILE,
  110. KEY_ALL_ACCESS, NULL, &hBaseKey, NULL) != ERROR_SUCCESS) {
  111. return FALSE;
  112. }
  113. if (RegCreateKeyEx(
  114. hBaseKey, szDevice, 0, NULL, REG_OPTION_NON_VOLATILE,
  115. KEY_ALL_ACCESS, NULL, &hDeviceKey, NULL) != ERROR_SUCCESS) {
  116. RegCloseKey(hBaseKey);
  117. return FALSE;
  118. }
  119. if (RegCreateKeyEx(
  120. hDeviceKey, szInstance, 0, NULL, REG_OPTION_NON_VOLATILE,
  121. KEY_ALL_ACCESS, NULL, &hInstanceKey, NULL) != ERROR_SUCCESS) {
  122. RegCloseKey(hBaseKey);
  123. RegCloseKey(hDeviceKey);
  124. return FALSE;
  125. }
  126. RegCloseKey(hBaseKey);
  127. RegCloseKey(hDeviceKey);
  128. RegCloseKey(hInstanceKey);
  129. return TRUE;
  130. } // CreateDeviceIDRegKey
  131. BOOL
  132. IsRootDeviceID(
  133. LPCWSTR pDeviceID
  134. )
  135. /*++
  136. Routine Description:
  137. This routine determines whether the specified device id is the root
  138. device id.
  139. Arguments:
  140. pDeviceID Pointer to a device id string
  141. Return value:
  142. The return value is TRUE if the string is the root device id and
  143. FALSE if it is not.
  144. --*/
  145. {
  146. size_t DeviceIDLen = 0;
  147. ASSERT(ARGUMENT_PRESENT(pDeviceID));
  148. if (FAILED(StringCchLength(
  149. pDeviceID,
  150. MAX_DEVICE_ID_LEN,
  151. &DeviceIDLen))) {
  152. return FALSE;
  153. }
  154. if (CompareString(LOCALE_INVARIANT,
  155. NORM_IGNORECASE,
  156. pDeviceID,
  157. -1,
  158. pszRegRootEnumerator,
  159. -1) == CSTR_EQUAL) {
  160. return TRUE;
  161. }
  162. return FALSE;
  163. } // IsRootDeviceID
  164. BOOL
  165. MultiSzAppendW(
  166. LPWSTR pszMultiSz,
  167. PULONG pulSize,
  168. LPCWSTR pszString
  169. )
  170. /*++
  171. Routine Description:
  172. Appends a string to a multi_sz string.
  173. Arguments:
  174. pszMultiSz Pointer to a multi_sz string
  175. pulSize On input, Size of the multi_sz string buffer in bytes,
  176. On return, amount copied to the buffer (in bytes)
  177. pszString String to append to pszMultiSz
  178. Return value:
  179. The return value is TRUE if the function succeeded and FALSE if an
  180. error occured.
  181. --*/
  182. {
  183. BOOL bStatus = TRUE;
  184. HRESULT hr;
  185. LPWSTR pTail;
  186. ULONG ulSize;
  187. try {
  188. //
  189. // if it's an empty string, just copy it
  190. //
  191. if (*pszMultiSz == L'\0') {
  192. ulSize = (lstrlen(pszString) + 2) * sizeof(WCHAR);
  193. if (ulSize > *pulSize) {
  194. bStatus = FALSE;
  195. goto Clean0;
  196. }
  197. hr = StringCchCopyEx(pszMultiSz,
  198. *pulSize,
  199. pszString,
  200. NULL, NULL,
  201. STRSAFE_NULL_ON_FAILURE |
  202. STRSAFE_IGNORE_NULLS);
  203. ASSERT(SUCCEEDED(hr));
  204. if (FAILED(hr)) {
  205. bStatus = FALSE;
  206. goto Clean0;
  207. }
  208. pszMultiSz[lstrlen(pszMultiSz) + 1] = L'\0'; // add second NULL term char
  209. *pulSize = ulSize;
  210. goto Clean0;
  211. }
  212. //
  213. // first find the end of the multi_sz string
  214. //
  215. pTail = pszMultiSz;
  216. while ((ULONG)(pTail - pszMultiSz) * sizeof(WCHAR) < *pulSize) {
  217. while (*pTail != L'\0') {
  218. pTail++;
  219. }
  220. pTail++; // skip past the null terminator
  221. if (*pTail == L'\0') {
  222. break; // found the double null terminator
  223. }
  224. }
  225. if ((pTail - pszMultiSz + lstrlen(pszString) + 2) * sizeof(WCHAR)
  226. > *pulSize) {
  227. bStatus = FALSE; // the copy would overflow the buffer
  228. goto Clean0;
  229. }
  230. hr = StringCchCopyEx(pTail,
  231. *pulSize,
  232. pszString,
  233. NULL, NULL,
  234. STRSAFE_NULL_ON_FAILURE |
  235. STRSAFE_IGNORE_NULLS);
  236. ASSERT(SUCCEEDED(hr));
  237. if (FAILED(hr)) {
  238. bStatus = FALSE;
  239. goto Clean0;
  240. }
  241. pTail += lstrlen(pszString) + 1;
  242. *pTail = L'\0'; // add second null terminator
  243. //
  244. // return buffer size in bytes
  245. //
  246. *pulSize = (ULONG)((pTail - pszMultiSz + 1)) * sizeof(WCHAR);
  247. Clean0:
  248. NOTHING;
  249. } except(EXCEPTION_EXECUTE_HANDLER) {
  250. bStatus = FALSE;
  251. }
  252. return bStatus;
  253. } // MultiSzAppendW
  254. LPWSTR
  255. MultiSzFindNextStringW(
  256. LPWSTR pMultiSz
  257. )
  258. /*++
  259. Routine Description:
  260. Finds next string in a multi_sz string.
  261. device id.
  262. Arguments:
  263. pMultiSz Pointer to a multi_sz string
  264. Return value:
  265. The return value is a pointer to the next string or NULL.
  266. --*/
  267. {
  268. LPWSTR lpNextString = pMultiSz;
  269. //
  270. // find the next NULL terminator
  271. //
  272. while (*lpNextString != L'\0') {
  273. lpNextString++;
  274. }
  275. lpNextString++; // skip over the NULL terminator
  276. if (*lpNextString == L'\0') {
  277. //
  278. // two NULL terminators in a row means we're at the end
  279. //
  280. lpNextString = NULL;
  281. }
  282. return lpNextString;
  283. } // MultiSzFindNextStringW
  284. BOOL
  285. MultiSzSearchStringW(
  286. IN LPCWSTR pString,
  287. IN LPCWSTR pSubString
  288. )
  289. {
  290. LPCWSTR pCurrent = pString;
  291. //
  292. // compare each string in the multi_sz pString with pSubString
  293. //
  294. while (*pCurrent != L'\0') {
  295. if (lstrcmpi(pCurrent, pSubString) == 0) {
  296. return TRUE;
  297. }
  298. //
  299. // go to the next string
  300. //
  301. while (*pCurrent != L'\0') {
  302. pCurrent++;
  303. }
  304. pCurrent++; // skip past the null terminator
  305. if (*pCurrent == L'\0') {
  306. break; // found the double null terminator
  307. }
  308. }
  309. return FALSE; // pSubString match not found within pString
  310. } // MultiSzSearchStringW
  311. ULONG
  312. MultiSzSizeW(
  313. IN LPCWSTR pString
  314. )
  315. {
  316. LPCWSTR p = NULL;
  317. if (pString == NULL) {
  318. return 0;
  319. }
  320. for (p = pString; *p; p += lstrlen(p)+1) {
  321. //
  322. // this should fall out with p pointing to the
  323. // second null in double-null terminator
  324. //
  325. NOTHING;
  326. }
  327. //
  328. // returns size in WCHAR
  329. //
  330. return (ULONG)(p - pString + 1);
  331. } // MultiSzSizeW
  332. BOOL
  333. MultiSzDeleteStringW(
  334. IN OUT LPWSTR pString,
  335. IN LPCWSTR pSubString
  336. )
  337. {
  338. LPWSTR p = NULL, pNext = NULL, pBuffer = NULL;
  339. ULONG ulSize = 0;
  340. if (pString == NULL || pSubString == NULL) {
  341. return FALSE;
  342. }
  343. for (p = pString; *p; p += lstrlen(p)+1) {
  344. if (lstrcmpi(p, pSubString) == 0) {
  345. //
  346. // found a match, this is the string to remove.
  347. //
  348. pNext = p + lstrlen(p) + 1;
  349. //
  350. // If this is the last string then just truncate it
  351. //
  352. if (*pNext == L'\0') {
  353. *p = L'\0';
  354. *(++p) = L'\0'; // double null-terminator
  355. return TRUE;
  356. }
  357. //
  358. // retrieve the size of the multi_sz string (in bytes)
  359. // starting with the substring after the matching substring
  360. //
  361. ulSize = MultiSzSizeW(pNext) * sizeof(WCHAR);
  362. if (ulSize == 0) {
  363. return FALSE;
  364. }
  365. pBuffer = HeapAlloc(ghPnPHeap, 0, ulSize);
  366. if (pBuffer == NULL) {
  367. return FALSE;
  368. }
  369. //
  370. // Make a copy of the multi_sz string starting at the
  371. // substring immediately after the matching substring
  372. //
  373. memcpy(pBuffer, pNext, ulSize);
  374. //
  375. // Copy that buffer back to the original buffer, but this
  376. // time copy over the top of the matching substring. This
  377. // effectively removes the matching substring and shifts
  378. // any remaining substrings up in multi_sz string.
  379. //
  380. memcpy(p, pBuffer, ulSize);
  381. HeapFree(ghPnPHeap, 0, pBuffer);
  382. return TRUE;
  383. }
  384. }
  385. //
  386. // if we got here, there was no match but I consider this a success
  387. // since the multi_sz does not contain the substring when we're done
  388. // (which is the desired goal)
  389. //
  390. return TRUE;
  391. } // MultiSzDeleteStringW
  392. BOOL
  393. IsValidDeviceID(
  394. IN LPCWSTR pszDeviceID,
  395. IN HKEY hKey,
  396. IN ULONG ulFlags
  397. )
  398. /*++
  399. Routine Description:
  400. This routine checks if the given device id is valid (present, not moved,
  401. not phantom).
  402. Arguments:
  403. pszDeviceID Device instance string to validate
  404. hKey Can specify open registry key to pszDeviceID, also
  405. ulFlag Controls how much verification to do
  406. Return value:
  407. The return value is CR_SUCCESS if the function suceeds and one of the
  408. CR_* values if it fails.
  409. --*/
  410. {
  411. BOOL Status = TRUE;
  412. LONG RegStatus = ERROR_SUCCESS;
  413. WCHAR RegStr[MAX_CM_PATH];
  414. HKEY hDevKey = NULL;
  415. ULONG ulValue = 0, ulSize = sizeof(ULONG);
  416. //
  417. // Does the device id exist in the registry?
  418. //
  419. if (hKey == NULL) {
  420. if (FAILED(StringCchPrintf(
  421. RegStr,
  422. SIZECHARS(RegStr),
  423. L"%s\\%s",
  424. pszRegPathEnum,
  425. pszDeviceID))) {
  426. return FALSE;
  427. }
  428. RegStatus =
  429. RegOpenKeyEx(
  430. HKEY_LOCAL_MACHINE, RegStr, 0,
  431. KEY_READ, &hDevKey);
  432. if (RegStatus != ERROR_SUCCESS) {
  433. return FALSE;
  434. }
  435. } else {
  436. hDevKey = hKey;
  437. }
  438. //-----------------------------------------------------------
  439. // Is the device id present?
  440. //-----------------------------------------------------------
  441. if (ulFlags & PNP_PRESENT) {
  442. if (!IsDeviceIdPresent(pszDeviceID)) {
  443. Status = FALSE;
  444. goto Clean0;
  445. }
  446. }
  447. //-----------------------------------------------------------
  448. // Is it a phantom device id?
  449. //-----------------------------------------------------------
  450. if (ulFlags & PNP_NOT_PHANTOM) {
  451. RegStatus = RegQueryValueEx(
  452. hDevKey, pszRegValuePhantom, NULL, NULL,
  453. (LPBYTE)&ulValue, &ulSize);
  454. if (RegStatus == ERROR_SUCCESS) {
  455. if (ulValue) {
  456. Status = FALSE;
  457. goto Clean0;
  458. }
  459. }
  460. }
  461. //-----------------------------------------------------------
  462. // Has the device id been moved?
  463. //-----------------------------------------------------------
  464. if (ulFlags & PNP_NOT_MOVED) {
  465. if (IsDeviceMoved(pszDeviceID, hDevKey)) {
  466. Status = FALSE;
  467. goto Clean0;
  468. }
  469. }
  470. //-----------------------------------------------------------
  471. // Has the device id been removed?
  472. //-----------------------------------------------------------
  473. if (ulFlags & PNP_NOT_REMOVED) {
  474. ULONG ulProblem = 0, ulStatus = 0;
  475. if (GetDeviceStatus(pszDeviceID, &ulStatus, &ulProblem) == CR_SUCCESS) {
  476. if (ulStatus & DN_WILL_BE_REMOVED) {
  477. Status = FALSE;
  478. goto Clean0;
  479. }
  480. }
  481. }
  482. Clean0:
  483. if ((hKey == NULL) && (hDevKey != NULL)) {
  484. RegCloseKey(hDevKey);
  485. }
  486. return Status;
  487. } // IsValidDeviceID
  488. BOOL
  489. IsDevicePhantom(
  490. IN LPWSTR pszDeviceID
  491. )
  492. /*++
  493. Routine Description:
  494. In this case, the check is actually really "is this not present?". The
  495. only comparison is done against FoundAtEnum. UPDATE: for NT 5.0, the
  496. FoundAtEnum registry value has been obsoleted, it's been replaced by a
  497. simple check for the presense of the devnode in memory.
  498. Arguments:
  499. pszDeviceID Device instance string to validate
  500. Return value:
  501. Returns TRUE if the device is a phantom and FALSE if it isn't.
  502. --*/
  503. {
  504. return !IsDeviceIdPresent(pszDeviceID);
  505. } // IsDevicePhantom
  506. CONFIGRET
  507. GetDeviceStatus(
  508. IN LPCWSTR pszDeviceID,
  509. OUT PULONG pulStatus,
  510. OUT PULONG pulProblem
  511. )
  512. /*++
  513. Routine Description:
  514. This routine retrieves the status and problem values for the given
  515. device instance.
  516. Arguments:
  517. pszDeviceID Specifies the device instance to retrieve info for
  518. pulStatus Returns the device's status
  519. pulProblem Returns the device's problem
  520. Return value:
  521. The return value is CR_SUCCESS if the function suceeds and one of the
  522. CR_* values if it fails.
  523. --*/
  524. {
  525. CONFIGRET Status = CR_SUCCESS;
  526. PLUGPLAY_CONTROL_STATUS_DATA ControlData;
  527. NTSTATUS ntStatus;
  528. memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
  529. RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
  530. ControlData.Operation = PNP_GET_STATUS;
  531. ControlData.DeviceStatus = 0;
  532. ControlData.DeviceProblem = 0;
  533. ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
  534. &ControlData,
  535. sizeof(ControlData));
  536. if (NT_SUCCESS(ntStatus)) {
  537. *pulStatus = ControlData.DeviceStatus;
  538. *pulProblem = ControlData.DeviceProblem;
  539. } else {
  540. Status = MapNtStatusToCmError(ntStatus);
  541. }
  542. return Status;
  543. } // GetDeviceStatus
  544. CONFIGRET
  545. SetDeviceStatus(
  546. IN LPCWSTR pszDeviceID,
  547. IN ULONG ulStatus,
  548. IN ULONG ulProblem
  549. )
  550. /*++
  551. Routine Description:
  552. This routine sets the specified status and problem values for the given
  553. device instance.
  554. Arguments:
  555. pszDeviceID Specifies the device instance to retrieve info for
  556. pulStatus Specifies the device's status
  557. pulProblem Specifies the device's problem
  558. Return value:
  559. The return value is CR_SUCCESS if the function suceeds and one of the
  560. CR_* values if it fails.
  561. --*/
  562. {
  563. CONFIGRET Status = CR_SUCCESS;
  564. PLUGPLAY_CONTROL_STATUS_DATA ControlData;
  565. NTSTATUS ntStatus;
  566. memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
  567. RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
  568. ControlData.Operation = PNP_SET_STATUS;
  569. ControlData.DeviceStatus = ulStatus;
  570. ControlData.DeviceProblem = ulProblem;
  571. ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
  572. &ControlData,
  573. sizeof(ControlData));
  574. if (!NT_SUCCESS(ntStatus)) {
  575. Status = MapNtStatusToCmError(ntStatus);
  576. }
  577. return Status;
  578. } // SetDeviceStatus
  579. CONFIGRET
  580. ClearDeviceStatus(
  581. IN LPCWSTR pszDeviceID,
  582. IN ULONG ulStatus,
  583. IN ULONG ulProblem
  584. )
  585. /*++
  586. Routine Description:
  587. This routine clears the specified status and problem values for the given
  588. device instance.
  589. Arguments:
  590. pszDeviceID Specifies the device instance to retrieve info for
  591. pulStatus Specifies the device's status
  592. pulProblem Specifies the device's problem
  593. Return value:
  594. The return value is CR_SUCCESS if the function suceeds and one of the
  595. CR_* values if it fails.
  596. --*/
  597. {
  598. CONFIGRET Status = CR_SUCCESS;
  599. PLUGPLAY_CONTROL_STATUS_DATA ControlData;
  600. NTSTATUS ntStatus;
  601. memset(&ControlData, 0, sizeof(PLUGPLAY_CONTROL_STATUS_DATA));
  602. RtlInitUnicodeString(&ControlData.DeviceInstance, pszDeviceID);
  603. ControlData.Operation = PNP_CLEAR_STATUS;
  604. ControlData.DeviceStatus = ulStatus;
  605. ControlData.DeviceProblem = ulProblem;
  606. ntStatus = NtPlugPlayControl(PlugPlayControlDeviceStatus,
  607. &ControlData,
  608. sizeof(ControlData));
  609. if (!NT_SUCCESS(ntStatus)) {
  610. Status = MapNtStatusToCmError(ntStatus);
  611. }
  612. return Status;
  613. } // ClearDeviceStatus
  614. CONFIGRET
  615. CopyRegistryTree(
  616. IN HKEY hSrcKey,
  617. IN HKEY hDestKey,
  618. IN ULONG ulOption
  619. )
  620. {
  621. CONFIGRET Status = CR_SUCCESS;
  622. LONG RegStatus = ERROR_SUCCESS;
  623. HKEY hSrcSubKey, hDestSubKey;
  624. WCHAR RegStr[MAX_PATH];
  625. ULONG ulMaxValueName, ulMaxValueData;
  626. ULONG ulDataSize, ulLength, ulType, i;
  627. LPWSTR pszValueName=NULL;
  628. LPBYTE pValueData=NULL;
  629. PSECURITY_DESCRIPTOR pSecDesc;
  630. //----------------------------------------------------------------
  631. // copy all values for this key
  632. //----------------------------------------------------------------
  633. //
  634. // find out the maximum size of any of the value names
  635. // and value data under the source device instance key
  636. //
  637. RegStatus = RegQueryInfoKey(
  638. hSrcKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  639. &ulMaxValueName, &ulMaxValueData, NULL, NULL);
  640. if (RegStatus != ERROR_SUCCESS) {
  641. Status = CR_REGISTRY_ERROR;
  642. goto Clean0;
  643. }
  644. ulMaxValueName++; // size doesn't already include null terminator
  645. //
  646. // allocate a buffer big enough to hold the largest value name and
  647. // the largest value data (note that the max value name is in chars
  648. // (not including the null terminator) and the max value data is
  649. // in bytes
  650. //
  651. pszValueName = HeapAlloc(ghPnPHeap, 0, ulMaxValueName * sizeof(WCHAR));
  652. if (pszValueName == NULL) {
  653. Status = CR_OUT_OF_MEMORY;
  654. goto Clean0;
  655. }
  656. pValueData = HeapAlloc(ghPnPHeap, 0, ulMaxValueData);
  657. if (pValueData == NULL) {
  658. Status = CR_OUT_OF_MEMORY;
  659. goto Clean0;
  660. }
  661. //
  662. // enumerate and copy each value
  663. //
  664. for (i=0; RegStatus == ERROR_SUCCESS; i++) {
  665. ulLength = ulMaxValueName;
  666. ulDataSize = ulMaxValueData;
  667. RegStatus = RegEnumValue(
  668. hSrcKey, i, pszValueName, &ulLength, NULL,
  669. &ulType, pValueData, &ulDataSize);
  670. if (RegStatus == ERROR_SUCCESS) {
  671. RegSetValueEx(
  672. hDestKey, pszValueName, 0, ulType, pValueData,
  673. ulDataSize);
  674. }
  675. }
  676. HeapFree(ghPnPHeap, 0, pszValueName);
  677. pszValueName = NULL;
  678. HeapFree(ghPnPHeap, 0, pValueData);
  679. pValueData = NULL;
  680. //---------------------------------------------------------------
  681. // recursively call CopyRegistryNode to copy all subkeys
  682. //---------------------------------------------------------------
  683. RegStatus = ERROR_SUCCESS;
  684. for (i=0; RegStatus == ERROR_SUCCESS; i++) {
  685. ulLength = MAX_PATH;
  686. RegStatus = RegEnumKey(hSrcKey, i, RegStr, ulLength);
  687. if (RegStatus == ERROR_SUCCESS) {
  688. if (RegOpenKey(hSrcKey, RegStr, &hSrcSubKey) == ERROR_SUCCESS) {
  689. if (RegCreateKeyEx(
  690. hDestKey, RegStr, 0, NULL, ulOption, KEY_ALL_ACCESS,
  691. NULL, &hDestSubKey, NULL) == ERROR_SUCCESS) {
  692. RegGetKeySecurity(hSrcSubKey, DACL_SECURITY_INFORMATION,
  693. NULL, &ulDataSize);
  694. pSecDesc = HeapAlloc(ghPnPHeap, 0, ulDataSize);
  695. if (pSecDesc == NULL) {
  696. Status = CR_OUT_OF_MEMORY;
  697. RegCloseKey(hSrcSubKey);
  698. RegCloseKey(hDestSubKey);
  699. goto Clean0;
  700. }
  701. RegGetKeySecurity(hSrcSubKey, DACL_SECURITY_INFORMATION,
  702. pSecDesc, &ulDataSize);
  703. CopyRegistryTree(hSrcSubKey, hDestSubKey, ulOption);
  704. RegSetKeySecurity(hDestSubKey, DACL_SECURITY_INFORMATION, pSecDesc);
  705. HeapFree(ghPnPHeap, 0, pSecDesc);
  706. RegCloseKey(hDestSubKey);
  707. }
  708. RegCloseKey(hSrcSubKey);
  709. }
  710. }
  711. }
  712. Clean0:
  713. if (pszValueName != NULL) {
  714. HeapFree(ghPnPHeap, 0, pszValueName);
  715. }
  716. if (pValueData != NULL) {
  717. pValueData = NULL;
  718. }
  719. return Status;
  720. } // CopyRegistryTree
  721. BOOL
  722. PathToString(
  723. IN LPWSTR pszString,
  724. IN LPCWSTR pszPath,
  725. IN ULONG ulLen
  726. )
  727. {
  728. LPWSTR p;
  729. HRESULT hr;
  730. hr = StringCchCopyEx(pszString,
  731. ulLen,
  732. pszPath,
  733. NULL, NULL,
  734. STRSAFE_NULL_ON_FAILURE);
  735. ASSERT(SUCCEEDED(hr));
  736. if (FAILED(hr)) {
  737. return FALSE;
  738. }
  739. for (p = pszString; *p; p++) {
  740. if (*p == TEXT('\\')) {
  741. *p = TEXT('&');
  742. }
  743. }
  744. return TRUE;
  745. } // PathToString
  746. BOOL
  747. IsDeviceMoved(
  748. IN LPCWSTR pszDeviceID,
  749. IN HKEY hKey
  750. )
  751. {
  752. HKEY hTempKey;
  753. WCHAR RegStr[MAX_CM_PATH];
  754. PathToString(RegStr, pszDeviceID,MAX_CM_PATH);
  755. if (RegOpenKeyEx(
  756. hKey, RegStr, 0, KEY_READ, &hTempKey) == ERROR_SUCCESS) {
  757. RegCloseKey(hTempKey);
  758. return TRUE;
  759. }
  760. return FALSE;
  761. } // IsDeviceMoved
  762. CONFIGRET
  763. SetKeyVolatileState(
  764. IN LPCWSTR pszParentKey,
  765. IN LPCWSTR pszChildKey,
  766. IN DWORD dwRegOptions
  767. )
  768. {
  769. CONFIGRET Status = CR_SUCCESS;
  770. WCHAR RegStr[MAX_CM_PATH], szTempKey[MAX_CM_PATH];
  771. HKEY hParentKey = NULL, hChildKey = NULL, hKey = NULL;
  772. HKEY hTempKey = NULL;
  773. //---------------------------------------------------------------------
  774. // Convert the registry key specified by pszChildKey (a subkey of
  775. // pszParentKey) to a key with the volatile state specified by copying it to
  776. // a temporary key and recreating the key, then copying the original
  777. // registry info back. This also converts any subkeys of pszChildKey.
  778. //---------------------------------------------------------------------
  779. ASSERT(ARGUMENT_PRESENT(pszParentKey));
  780. ASSERT(ARGUMENT_PRESENT(pszChildKey));
  781. //
  782. // This routine only handles the REG_OPTION bits that specify the volatile
  783. // state of the key.
  784. //
  785. ASSERT((dwRegOptions == REG_OPTION_VOLATILE) || (dwRegOptions == REG_OPTION_NON_VOLATILE));
  786. if (dwRegOptions & REG_OPTION_VOLATILE) {
  787. dwRegOptions = REG_OPTION_VOLATILE;
  788. } else {
  789. dwRegOptions = REG_OPTION_NON_VOLATILE;
  790. }
  791. //
  792. // Open a key to the parent
  793. //
  794. if (RegOpenKeyEx(
  795. HKEY_LOCAL_MACHINE, pszParentKey, 0,
  796. KEY_ALL_ACCESS, &hParentKey) != ERROR_SUCCESS) {
  797. goto Clean0; // nothing to convert
  798. }
  799. //
  800. // open a key to the child subkey
  801. //
  802. if (RegOpenKeyEx(
  803. hParentKey, pszChildKey, 0,
  804. KEY_ALL_ACCESS, &hChildKey) != ERROR_SUCCESS) {
  805. goto Clean0; // nothing to convert
  806. }
  807. //
  808. // 1. Open a unique temporary key under the special Deleted Key.
  809. // Use the parent key path to form the unique tempory key. There shouldn't
  810. // already be such a key, but if there is then just overwrite it.
  811. //
  812. if (RegOpenKeyEx(
  813. HKEY_LOCAL_MACHINE, pszRegPathCurrentControlSet, 0,
  814. KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS) {
  815. Status = CR_REGISTRY_ERROR;
  816. goto Clean0;
  817. }
  818. if (FAILED(StringCchPrintf(
  819. RegStr,
  820. SIZECHARS(RegStr),
  821. L"%s\\%s",
  822. pszParentKey,
  823. pszChildKey))) {
  824. Status = CR_FAILURE;
  825. goto Clean0;
  826. }
  827. PathToString(szTempKey, RegStr,MAX_CM_PATH);
  828. if (FAILED(StringCchPrintf(
  829. RegStr,
  830. SIZECHARS(RegStr),
  831. L"%s\\%s",
  832. pszRegKeyDeleted,
  833. szTempKey))) {
  834. Status = CR_FAILURE;
  835. goto Clean0;
  836. }
  837. if (RegCreateKeyEx(
  838. hKey, RegStr, 0, NULL, dwRegOptions,
  839. KEY_ALL_ACCESS, NULL, &hTempKey, NULL) != ERROR_SUCCESS) {
  840. Status = CR_REGISTRY_ERROR;
  841. goto Clean0;
  842. }
  843. //
  844. // 2. Save the current child key (and any subkeys) to a temporary
  845. // location
  846. //
  847. Status = CopyRegistryTree(hChildKey, hTempKey, dwRegOptions);
  848. if (Status != CR_SUCCESS) {
  849. goto CleanupTempKeys;
  850. }
  851. RegCloseKey(hChildKey);
  852. hChildKey = NULL;
  853. //
  854. // 3. Delete the current child key (and any subkeys)
  855. //
  856. if (!RegDeleteNode(hParentKey, pszChildKey)) {
  857. Status = CR_REGISTRY_ERROR;
  858. goto CleanupTempKeys;
  859. }
  860. //
  861. // 4. Recreate the current child key using the volatile state specified
  862. //
  863. if (RegCreateKeyEx(
  864. hParentKey, pszChildKey, 0, NULL,
  865. dwRegOptions, KEY_ALL_ACCESS, NULL,
  866. &hChildKey, NULL) != ERROR_SUCCESS) {
  867. Status = CR_REGISTRY_ERROR;
  868. goto CleanupTempKeys;
  869. }
  870. //
  871. // 5. Copy the original child key (and any subkeys) back
  872. // to the new child key as specified by the volatile state.
  873. //
  874. Status = CopyRegistryTree(hTempKey, hChildKey, dwRegOptions);
  875. if (Status != CR_SUCCESS) {
  876. goto CleanupTempKeys;
  877. }
  878. //
  879. // 6. Remove the temporary instance key (and any subkeys)
  880. //
  881. CleanupTempKeys:
  882. if (hTempKey != NULL) {
  883. RegCloseKey(hTempKey);
  884. hTempKey = NULL;
  885. }
  886. if (SUCCEEDED(StringCchPrintf(
  887. RegStr,
  888. SIZECHARS(RegStr),
  889. L"%s\\%s",
  890. pszRegPathCurrentControlSet,
  891. pszRegKeyDeleted))) {
  892. if (RegOpenKeyEx(
  893. HKEY_LOCAL_MACHINE, RegStr, 0,
  894. KEY_ALL_ACCESS, &hTempKey) == ERROR_SUCCESS) {
  895. RegDeleteNode(hTempKey, szTempKey);
  896. RegCloseKey(hTempKey);
  897. hTempKey = NULL;
  898. }
  899. }
  900. Clean0:
  901. if (hParentKey != NULL) {
  902. RegCloseKey(hParentKey);
  903. }
  904. if (hChildKey != NULL) {
  905. RegCloseKey(hChildKey);
  906. }
  907. if (hKey != NULL) {
  908. RegCloseKey(hKey);
  909. }
  910. if (hTempKey != NULL) {
  911. RegCloseKey(hTempKey);
  912. }
  913. return Status;
  914. } // SetKeyVolatileState
  915. CONFIGRET
  916. MakeKeyVolatile(
  917. IN LPCWSTR pszParentKey,
  918. IN LPCWSTR pszChildKey
  919. )
  920. {
  921. CONFIGRET Status;
  922. //
  923. // Set the state of the key to volatile.
  924. //
  925. Status =
  926. SetKeyVolatileState(
  927. pszParentKey,
  928. pszChildKey,
  929. REG_OPTION_VOLATILE);
  930. return Status;
  931. } // MakeKeyVolatile
  932. CONFIGRET
  933. MakeKeyNonVolatile(
  934. IN LPCWSTR pszParentKey,
  935. IN LPCWSTR pszChildKey
  936. )
  937. {
  938. CONFIGRET Status;
  939. //
  940. // Set the state of the key to non-volatile.
  941. //
  942. Status =
  943. SetKeyVolatileState(
  944. pszParentKey,
  945. pszChildKey,
  946. REG_OPTION_NON_VOLATILE);
  947. return Status;
  948. } // MakeKeyNonVolatile
  949. CONFIGRET
  950. OpenLogConfKey(
  951. IN LPCWSTR pszDeviceID,
  952. IN ULONG LogConfType,
  953. OUT PHKEY phKey
  954. )
  955. {
  956. CONFIGRET Status = CR_SUCCESS;
  957. LONG RegStatus = ERROR_SUCCESS;
  958. HKEY hKey = NULL;
  959. ULONG ulSize = 0;
  960. try {
  961. //
  962. // Open a key to the device ID
  963. //
  964. RegStatus = RegOpenKeyEx(ghEnumKey, pszDeviceID, 0,
  965. KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY,
  966. &hKey);
  967. if (RegStatus != ERROR_SUCCESS) {
  968. Status = CR_INVALID_DEVINST;
  969. goto Clean0;
  970. }
  971. //
  972. // Alloc/Filtered configs are the exception, it's stored in the volative Control
  973. // subkey, all the other log confs are stored under the nonvolatile
  974. // LogConf subkey.
  975. //
  976. if ((LogConfType == ALLOC_LOG_CONF) || (LogConfType == FILTERED_LOG_CONF)) {
  977. //
  978. // Try the control key first, if no alloc config value there,
  979. // then try the log conf key.
  980. //
  981. RegStatus = RegCreateKeyEx(hKey, pszRegKeyDeviceControl, 0, NULL,
  982. REG_OPTION_VOLATILE, KEY_ALL_ACCESS,
  983. NULL, phKey, NULL);
  984. if (RegStatus == ERROR_SUCCESS) {
  985. if (RegQueryValueEx(*phKey, pszRegValueAllocConfig, NULL, NULL,
  986. NULL, &ulSize) == ERROR_SUCCESS) {
  987. goto Clean0;
  988. }
  989. RegCloseKey(*phKey);
  990. }
  991. RegStatus = RegCreateKeyEx(hKey, pszRegKeyLogConf, 0, NULL,
  992. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
  993. NULL, phKey, NULL);
  994. } else {
  995. RegStatus = RegCreateKeyEx(hKey, pszRegKeyLogConf, 0, NULL,
  996. REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
  997. NULL, phKey, NULL);
  998. }
  999. if (RegStatus != ERROR_SUCCESS) {
  1000. Status = CR_REGISTRY_ERROR;
  1001. }
  1002. Clean0:
  1003. NOTHING;
  1004. } except(EXCEPTION_EXECUTE_HANDLER) {
  1005. Status = CR_FAILURE;
  1006. }
  1007. if (hKey != NULL) {
  1008. RegCloseKey(hKey);
  1009. }
  1010. return Status;
  1011. } // OpenLogConfKey
  1012. BOOL
  1013. GetActiveService(
  1014. IN PCWSTR pszDevice,
  1015. OUT PWSTR pszService
  1016. )
  1017. {
  1018. WCHAR RegStr[MAX_CM_PATH];
  1019. HKEY hKey = NULL;
  1020. ULONG ulSize;
  1021. if ((!ARGUMENT_PRESENT(pszService)) ||
  1022. (!ARGUMENT_PRESENT(pszDevice))) {
  1023. return FALSE;
  1024. }
  1025. *pszService = TEXT('\0');
  1026. //
  1027. // open the volatile control key under the device instance
  1028. //
  1029. if (FAILED(StringCchPrintf(
  1030. RegStr,
  1031. SIZECHARS(RegStr),
  1032. L"%s\\%s\\%s",
  1033. pszRegPathEnum,
  1034. pszDevice,
  1035. pszRegKeyDeviceControl))) {
  1036. return FALSE;
  1037. }
  1038. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegStr, 0, KEY_READ,
  1039. &hKey) != ERROR_SUCCESS) {
  1040. return FALSE;
  1041. }
  1042. //
  1043. // query the active service value
  1044. //
  1045. ulSize = MAX_SERVICE_NAME_LEN * sizeof(WCHAR);
  1046. if (RegQueryValueEx(hKey, pszRegValueActiveService, NULL, NULL,
  1047. (LPBYTE)pszService, &ulSize) != ERROR_SUCCESS) {
  1048. RegCloseKey(hKey);
  1049. *pszService = TEXT('\0');
  1050. return FALSE;
  1051. }
  1052. RegCloseKey(hKey);
  1053. return TRUE;
  1054. } // GetActiveService
  1055. BOOL
  1056. IsDeviceIdPresent(
  1057. IN LPCWSTR pszDeviceID
  1058. )
  1059. /*++
  1060. Routine Description:
  1061. This routine determines whether the specified device instance is
  1062. considered physically present or not. This used to be based on a check
  1063. of the old "FoundAtEnum" registry setting. Now we just look for the presense
  1064. of an in-memory devnode associated with this device instance to decide whether
  1065. it's present or not.
  1066. Arguments:
  1067. pszDeviceID - device instance string to test for presense on
  1068. Return value:
  1069. The return value is TRUE if the function suceeds and FALSE if it fails.
  1070. --*/
  1071. {
  1072. ULONG ulStatus, ulProblem;
  1073. //
  1074. // If the call failed, then assume the device isn't present
  1075. //
  1076. return GetDeviceStatus(pszDeviceID, &ulStatus, &ulProblem) == CR_SUCCESS;
  1077. } // IsDeviceIdPresent
  1078. ULONG
  1079. GetDeviceConfigFlags(
  1080. IN LPCWSTR pszDeviceID,
  1081. IN HKEY hKey
  1082. )
  1083. {
  1084. HKEY hDevKey = NULL;
  1085. ULONG ulValue = 0, ulSize = sizeof(ULONG);
  1086. //
  1087. // If hKey is null, then open a key to the device instance.
  1088. //
  1089. if (hKey == NULL) {
  1090. if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0, KEY_READ,
  1091. &hDevKey) != ERROR_SUCCESS) {
  1092. goto Clean0;
  1093. }
  1094. } else {
  1095. hDevKey = hKey;
  1096. }
  1097. //
  1098. // Retrieve the configflag value
  1099. //
  1100. if (RegQueryValueEx(hDevKey, pszRegValueConfigFlags, NULL, NULL,
  1101. (LPBYTE)&ulValue, &ulSize) != ERROR_SUCCESS) {
  1102. ulValue = 0;
  1103. }
  1104. Clean0:
  1105. if ((hKey == NULL) && (hDevKey != NULL)) {
  1106. RegCloseKey(hDevKey);
  1107. }
  1108. return ulValue;
  1109. } // GetDeviceConfigFlags
  1110. ULONG
  1111. MapNtStatusToCmError(
  1112. ULONG NtStatus
  1113. )
  1114. {
  1115. switch (NtStatus) {
  1116. case STATUS_BUFFER_TOO_SMALL:
  1117. return CR_BUFFER_SMALL;
  1118. case STATUS_NO_SUCH_DEVICE:
  1119. return CR_NO_SUCH_DEVINST;
  1120. case STATUS_INVALID_PARAMETER:
  1121. case STATUS_INVALID_PARAMETER_1:
  1122. case STATUS_INVALID_PARAMETER_2:
  1123. return CR_INVALID_DATA;
  1124. case STATUS_NOT_IMPLEMENTED:
  1125. return CR_CALL_NOT_IMPLEMENTED;
  1126. case STATUS_ACCESS_DENIED:
  1127. return CR_ACCESS_DENIED;
  1128. case STATUS_OBJECT_NAME_NOT_FOUND:
  1129. return CR_NO_SUCH_VALUE;
  1130. default:
  1131. return CR_FAILURE;
  1132. }
  1133. } // MapNtStatusToCmError
  1134. //
  1135. // GUID-related utility routines.
  1136. //
  1137. BOOL
  1138. IsValidGuid(
  1139. LPWSTR pszGuid
  1140. )
  1141. /*++
  1142. Routine Description:
  1143. This routine determines whether a string is of the proper Guid form.
  1144. Arguments:
  1145. pszGuid Pointer to a string that will be checked for the standard Guid
  1146. format.
  1147. Return value:
  1148. The return value is TRUE if the string is a valid Guid and FALSE if it
  1149. is not.
  1150. --*/
  1151. {
  1152. //----------------------------------------------------------------
  1153. // NOTE: This may change later, but for now I am just verifying
  1154. // that the string has exactly MAX_GUID_STRING_LEN characters
  1155. //----------------------------------------------------------------
  1156. if (lstrlen(pszGuid) != MAX_GUID_STRING_LEN-1) {
  1157. return FALSE;
  1158. }
  1159. return TRUE;
  1160. } // IsValidGuid
  1161. BOOL
  1162. GuidEqual(
  1163. CONST GUID UNALIGNED *Guid1,
  1164. CONST GUID UNALIGNED *Guid2
  1165. )
  1166. {
  1167. RPC_STATUS rpcStatus;
  1168. //
  1169. // Note - rpcStatus is ignored, the RPC runtime always sets it to RPC_S_OK.
  1170. // The UUID comparison result is returned directly, non-zero if the UUIDs
  1171. // are equal, zero otherwise.
  1172. //
  1173. return (BOOL)(UuidEqual((LPGUID)Guid1, (LPGUID)Guid2, &rpcStatus));
  1174. } // GuidEqual
  1175. DWORD
  1176. GuidFromString(
  1177. IN PCWSTR GuidString,
  1178. OUT LPGUID Guid
  1179. )
  1180. /*++
  1181. Routine Description:
  1182. This routine converts the character representation of a GUID into its binary
  1183. form (a GUID struct). The GUID is in the following form:
  1184. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
  1185. where 'x' is a hexadecimal digit.
  1186. Arguments:
  1187. GuidString - Supplies a pointer to the null-terminated GUID string. The
  1188. Guid - Supplies a pointer to the variable that receives the GUID structure.
  1189. Return Value:
  1190. If the function succeeds, the return value is NO_ERROR.
  1191. If the function fails, the return value is RPC_S_INVALID_STRING_UUID.
  1192. --*/
  1193. {
  1194. WCHAR UuidBuffer[GUID_STRING_LEN - 1];
  1195. size_t UuidLen = 0;
  1196. //
  1197. // Since we're using a RPC UUID routine, we need to strip off the surrounding
  1198. // curly braces first.
  1199. //
  1200. if (*GuidString++ != TEXT('{')) {
  1201. return RPC_S_INVALID_STRING_UUID;
  1202. }
  1203. if (FAILED(StringCchCopy(UuidBuffer,
  1204. SIZECHARS(UuidBuffer),
  1205. GuidString))) {
  1206. return RPC_S_INVALID_STRING_UUID;
  1207. }
  1208. if (FAILED(StringCchLength(UuidBuffer,
  1209. SIZECHARS(UuidBuffer),
  1210. &UuidLen))) {
  1211. return RPC_S_INVALID_STRING_UUID;
  1212. }
  1213. if ((UuidLen != GUID_STRING_LEN - 2) ||
  1214. (UuidBuffer[GUID_STRING_LEN - 3] != TEXT('}'))) {
  1215. return RPC_S_INVALID_STRING_UUID;
  1216. }
  1217. UuidBuffer[GUID_STRING_LEN - 3] = TEXT('\0');
  1218. if (UuidFromString(UuidBuffer, Guid) != RPC_S_OK) {
  1219. return RPC_S_INVALID_STRING_UUID;
  1220. }
  1221. return NO_ERROR;
  1222. } // GuidFromString
  1223. DWORD
  1224. StringFromGuid(
  1225. IN CONST GUID *Guid,
  1226. OUT PWSTR GuidString,
  1227. IN DWORD GuidStringSize
  1228. )
  1229. /*++
  1230. Routine Description:
  1231. This routine converts a GUID into a null-terminated string which represents
  1232. it. This string is of the form:
  1233. {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
  1234. where x represents a hexadecimal digit.
  1235. This routine comes from ole32\common\ccompapi.cxx. It is included here to avoid linking
  1236. to ole32.dll. (The RPC version allocates memory, so it was avoided as well.)
  1237. Arguments:
  1238. Guid - Supplies a pointer to the GUID whose string representation is
  1239. to be retrieved.
  1240. GuidString - Supplies a pointer to character buffer that receives the
  1241. string. This buffer must be _at least_ 39 (GUID_STRING_LEN) characters
  1242. long.
  1243. Return Value:
  1244. If success, the return value is NO_ERROR.
  1245. if failure, the return value is
  1246. --*/
  1247. {
  1248. CONST BYTE *GuidBytes;
  1249. INT i;
  1250. if(GuidStringSize < GUID_STRING_LEN) {
  1251. return ERROR_INSUFFICIENT_BUFFER;
  1252. }
  1253. GuidBytes = (CONST BYTE *)Guid;
  1254. *GuidString++ = TEXT('{');
  1255. for(i = 0; i < sizeof(GuidMap); i++) {
  1256. if(GuidMap[i] == '-') {
  1257. *GuidString++ = TEXT('-');
  1258. } else {
  1259. *GuidString++ = szDigits[ (GuidBytes[GuidMap[i]] & 0xF0) >> 4 ];
  1260. *GuidString++ = szDigits[ (GuidBytes[GuidMap[i]] & 0x0F) ];
  1261. }
  1262. }
  1263. *GuidString++ = TEXT('}');
  1264. *GuidString = TEXT('\0');
  1265. return NO_ERROR;
  1266. } // StringFromGuid
  1267.