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.

1475 lines
42 KiB

  1. /*++
  2. Copyright (c) 1995-2000 Microsoft Corporation
  3. Module Name:
  4. rlogconf.c
  5. Abstract:
  6. This module contains the server-side logical configuration APIs.
  7. PNP_AddEmptyLogConf
  8. PNP_FreeLogConf
  9. PNP_GetFirstLogConf
  10. PNP_GetNextLogConf
  11. PNP_GetLogConfPriority
  12. Author:
  13. Paula Tomlinson (paulat) 9-27-1995
  14. Environment:
  15. User-mode only.
  16. Revision History:
  17. 27-Sept-1995 paulat
  18. Creation and initial implementation.
  19. --*/
  20. //
  21. // includes
  22. //
  23. #include "precomp.h"
  24. #include "umpnpi.h"
  25. #include "umpnpdat.h"
  26. //
  27. // Prototypes used in this routine and in rresdes.c
  28. //
  29. CONFIGRET
  30. GetLogConfData(
  31. IN HKEY hKey,
  32. IN ULONG ulLogConfType,
  33. OUT PULONG pulRegDataType,
  34. OUT LPWSTR pszValueName,
  35. OUT LPBYTE *ppBuffer,
  36. OUT PULONG pulBufferSize
  37. );
  38. PCM_FULL_RESOURCE_DESCRIPTOR
  39. AdvanceResourcePtr(
  40. IN PCM_FULL_RESOURCE_DESCRIPTOR pRes
  41. );
  42. PIO_RESOURCE_LIST
  43. AdvanceRequirementsPtr(
  44. IN PIO_RESOURCE_LIST pReq
  45. );
  46. //
  47. // Prototypes from rresdes.c
  48. //
  49. BOOL
  50. FindLogConf(
  51. IN LPBYTE pList,
  52. OUT LPBYTE *ppLogConf,
  53. IN ULONG RegDataType,
  54. IN ULONG ulTag
  55. );
  56. PIO_RESOURCE_DESCRIPTOR
  57. AdvanceRequirementsDescriptorPtr(
  58. IN PIO_RESOURCE_DESCRIPTOR pReqDesStart,
  59. IN ULONG ulIncrement,
  60. IN ULONG ulRemainingRanges,
  61. OUT PULONG pulRangeCount
  62. );
  63. //
  64. // private prototypes
  65. //
  66. BOOL
  67. MigrateObsoleteDetectionInfo(
  68. IN LPWSTR pszDeviceID,
  69. IN HKEY hLogConfKey
  70. );
  71. //
  72. // global data
  73. //
  74. extern HKEY ghEnumKey; // Key to HKLM\CCC\System\Enum - DO NOT MODIFY
  75. CONFIGRET
  76. PNP_AddEmptyLogConf(
  77. IN handle_t hBinding,
  78. IN LPWSTR pDeviceID,
  79. IN ULONG ulPriority,
  80. OUT PULONG pulTag,
  81. IN ULONG ulFlags
  82. )
  83. /*++
  84. Routine Description:
  85. This the server-side of an RPC remote call. This routine adds
  86. an empty logical configuration.
  87. Arguments:
  88. hBinding RPC binding handle.
  89. pDeviceID Null-terminated device instance id string.
  90. ulPriority Priority for new log conf.
  91. pulTag Returns tag that identifies which log config this is.
  92. ulFlags Describes type of log conf to add.
  93. Return Value:
  94. If the specified device instance is valid, it returns CR_SUCCESS,
  95. otherwise it returns CR_ error code.
  96. --*/
  97. {
  98. CONFIGRET Status = CR_SUCCESS;
  99. HKEY hKey = NULL;
  100. WCHAR szValueName[64];
  101. LPBYTE pList = NULL;
  102. ULONG Index = 0, ulListSize = 0, ulAddListSize = 0;
  103. ULONG RegDataType = 0;
  104. //------------------------------------------------------------------
  105. // The BOOT, ALLOC, and FORCED config types are stored in a registry
  106. // value name of the format XxxConfig and the BASIC, FILTERED, and
  107. // OVERRIDE configs are stored in a registr value name of the format
  108. // XxxConfigVector. XxxConfig values contain the actual resource
  109. // description (REG_RESOURCE_LIST, CM_RESOURCE_LIST) while
  110. // XxxConfigVector values contain a list of resource requirements
  111. // (REG_RESOURCE_REQUIREMENTS_LIST, IO_RESOURCE_REQUIREMENTS_LIST).
  112. //
  113. // The policy for using the log conf and res des APIs is:
  114. // - BOOT, ALLOC, and FORCED are defined to only have one log conf.
  115. // - Although callers always specify a complete XXX_RESOURCE type
  116. // structure for the data when adding resource descriptors to
  117. // a log conf, I will ignore the resource specific portion of
  118. // the XXX_DES structure for FILTERED, BASIC, and OVERRIDE.
  119. // Likewise I will ignore any XXX_RANGE structures for ALLOC,
  120. // BOOT or FORCED log config types.
  121. //------------------------------------------------------------------
  122. try {
  123. //
  124. // verify client access
  125. //
  126. if (!VerifyClientAccess(hBinding, &gLuidLoadDriverPrivilege)) {
  127. Status = CR_ACCESS_DENIED;
  128. goto Clean0;
  129. }
  130. //
  131. // initialize output parameters
  132. //
  133. if (!ARGUMENT_PRESENT(pulTag)) {
  134. Status = CR_INVALID_POINTER;
  135. goto Clean0;
  136. } else {
  137. *pulTag = 0;
  138. }
  139. //
  140. // validate parameters
  141. //
  142. if (INVALID_FLAGS(ulFlags, LOG_CONF_BITS | PRIORITY_BIT)) {
  143. Status = CR_INVALID_FLAG;
  144. goto Clean0;
  145. }
  146. //
  147. // make sure original caller didn't specify root devnode
  148. //
  149. if (!IsLegalDeviceId(pDeviceID) || IsRootDeviceID(pDeviceID)) {
  150. Status = CR_INVALID_DEVNODE;
  151. goto Clean0;
  152. }
  153. //
  154. // open a key to the device's LogConf subkey
  155. //
  156. Status = OpenLogConfKey(pDeviceID, ulFlags & LOG_CONF_BITS, &hKey);
  157. if (Status != CR_SUCCESS) {
  158. goto Clean0;
  159. }
  160. MigrateObsoleteDetectionInfo(pDeviceID, hKey);
  161. //
  162. // Retrieve log conf data from the registry
  163. //
  164. Status = GetLogConfData(hKey, ulFlags & LOG_CONF_BITS,
  165. &RegDataType, szValueName,
  166. &pList, &ulListSize);
  167. //-----------------------------------------------------------
  168. // Specified log conf type contains Resource Data only
  169. //-----------------------------------------------------------
  170. if (RegDataType == REG_RESOURCE_LIST) {
  171. if (Status != CR_SUCCESS || ulListSize == 0) {
  172. //
  173. // This is the first log conf of this type: create a new
  174. // log conf with an empty res des.
  175. //
  176. PCM_RESOURCE_LIST pResList = NULL;
  177. Status = CR_SUCCESS;
  178. ulListSize = sizeof(CM_RESOURCE_LIST) -
  179. sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
  180. pList = HeapAlloc(ghPnPHeap, HEAP_ZERO_MEMORY, ulListSize);
  181. if (pList == NULL) {
  182. Status = CR_OUT_OF_MEMORY;
  183. goto Clean0;
  184. }
  185. //
  186. // initialize the config list header info
  187. //
  188. pResList = (PCM_RESOURCE_LIST)pList;
  189. pResList->Count = 1;
  190. pResList->List[0].InterfaceType = InterfaceTypeUndefined;
  191. pResList->List[0].BusNumber = 0;
  192. pResList->List[0].PartialResourceList.Version = NT_RESLIST_VERSION;
  193. pResList->List[0].PartialResourceList.Revision = NT_RESLIST_REVISION;
  194. pResList->List[0].PartialResourceList.Count = 0;
  195. } else {
  196. //
  197. // There is already at least one log conf of this type, so add
  198. // a new empty log conf to the log conf list (priority ignored)
  199. //
  200. PCM_RESOURCE_LIST pResList = (PCM_RESOURCE_LIST)pList;
  201. PCM_FULL_RESOURCE_DESCRIPTOR pRes = NULL;
  202. //
  203. // realloc the existing log conf list structs to hold another
  204. // log conf
  205. //
  206. ulAddListSize = sizeof(CM_FULL_RESOURCE_DESCRIPTOR) -
  207. sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
  208. pResList = (PCM_RESOURCE_LIST)HeapReAlloc(ghPnPHeap, 0, pResList,
  209. ulListSize + ulAddListSize);
  210. if (pResList == NULL) {
  211. Status = CR_OUT_OF_MEMORY;
  212. goto Clean0;
  213. }
  214. pList = (LPBYTE)pResList;
  215. //
  216. // Priorities are ignored for resource lists, so just add any
  217. // subsequent log confs to the end (they will be ignored by the
  218. // system anyway).
  219. //
  220. pRes = (PCM_FULL_RESOURCE_DESCRIPTOR)(&pResList->List[0]); // first lc
  221. for (Index = 0; Index < pResList->Count; Index++) {
  222. pRes = AdvanceResourcePtr(pRes); // next lc
  223. }
  224. //
  225. // initialize the new empty log config
  226. //
  227. pResList->Count++;
  228. pRes->InterfaceType = InterfaceTypeUndefined;
  229. pRes->BusNumber = 0;
  230. pRes->PartialResourceList.Version = NT_RESLIST_VERSION;
  231. pRes->PartialResourceList.Revision = NT_RESLIST_REVISION;
  232. pRes->PartialResourceList.Count = 0;
  233. *pulTag = Index;
  234. }
  235. }
  236. //-----------------------------------------------------------
  237. // Specified log conf type contains requirements data only
  238. //-----------------------------------------------------------
  239. else if (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST) {
  240. if (Status != CR_SUCCESS || ulListSize == 0) {
  241. //
  242. // This is the first log conf of this type: create a new
  243. // log conf (IO_RESOURCE_LIST) with a single res des
  244. // (IO_RESOURCE_DESCRIPTOR) for the config data.
  245. //
  246. PIO_RESOURCE_REQUIREMENTS_LIST pReqList = NULL;
  247. PIO_RESOURCE_LIST pReq = NULL;
  248. Status = CR_SUCCESS;
  249. ulListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
  250. pReqList = HeapAlloc(ghPnPHeap, HEAP_ZERO_MEMORY, ulListSize);
  251. if (pReqList == NULL) {
  252. Status = CR_OUT_OF_MEMORY;
  253. goto Clean0;
  254. }
  255. pList = (LPBYTE)pReqList;
  256. //
  257. // initialize the config list header info
  258. //
  259. // There's room for one IO_RESOURCE_DESCRIPTOR embedded in
  260. // the IO_RESOURCE_LIST structure and by definition the first
  261. // one is a ConfigData type descriptor (user-mode always
  262. // specifies a priority value so we always have a ConfigData
  263. // struct).
  264. //
  265. pReqList->ListSize = ulListSize;
  266. pReqList->InterfaceType = InterfaceTypeUndefined;
  267. pReqList->BusNumber = 0;
  268. pReqList->SlotNumber = 0;
  269. pReqList->AlternativeLists = 1;
  270. pReq = (PIO_RESOURCE_LIST)(&pReqList->List[0]); // first lc
  271. pReq->Version = NT_REQLIST_VERSION;
  272. pReq->Revision = NT_REQLIST_REVISION;
  273. pReq->Count = 1;
  274. pReq->Descriptors[0].Option = IO_RESOURCE_PREFERRED;
  275. pReq->Descriptors[0].Type = CmResourceTypeConfigData;
  276. pReq->Descriptors[0].u.ConfigData.Priority = ulPriority;
  277. } else {
  278. //
  279. // There is already at least one log conf of this type, so add
  280. // a new empty log conf to the log conf list (always at the end)
  281. //
  282. PIO_RESOURCE_REQUIREMENTS_LIST pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)pList;
  283. PIO_RESOURCE_LIST pReq = NULL;
  284. //
  285. // realloc the existing log conf list structs to hold another
  286. // log conf
  287. //
  288. ulAddListSize = sizeof(IO_RESOURCE_LIST);
  289. pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)HeapReAlloc(ghPnPHeap, 0, pReqList,
  290. ulListSize + ulAddListSize);
  291. if (pReqList == NULL) {
  292. Status = CR_OUT_OF_MEMORY;
  293. goto Clean0;
  294. }
  295. pList = (LPBYTE)pReqList;
  296. //
  297. // Skip past all the existing log confs to the new space at the
  298. // end of the reallocated buffer.
  299. //
  300. pReq = (PIO_RESOURCE_LIST)(&pReqList->List[0]); // first lc
  301. for (Index = 0; Index < pReqList->AlternativeLists; Index++) {
  302. pReq = AdvanceRequirementsPtr(pReq); // next lc
  303. }
  304. //
  305. // initialize the new empty log config (including the embedded
  306. // ConfigData structure).
  307. //
  308. pReqList->AlternativeLists++;
  309. pReqList->ListSize = ulListSize + ulAddListSize;
  310. pReq->Version = NT_REQLIST_VERSION;
  311. pReq->Revision = NT_REQLIST_REVISION;
  312. pReq->Count = 1;
  313. memset(&pReq->Descriptors[0], 0, sizeof(IO_RESOURCE_DESCRIPTOR));
  314. pReq->Descriptors[0].Option = IO_RESOURCE_PREFERRED;
  315. pReq->Descriptors[0].Type = CmResourceTypeConfigData;
  316. pReq->Descriptors[0].u.ConfigData.Priority = ulPriority;
  317. *pulTag = Index;
  318. }
  319. } else {
  320. Status = CR_FAILURE;
  321. goto Clean0;
  322. }
  323. //
  324. // Write out the new/updated log conf list to the registry
  325. //
  326. if (RegSetValueEx(hKey, szValueName, 0, RegDataType,
  327. pList, ulListSize + ulAddListSize)
  328. != ERROR_SUCCESS) {
  329. Status = CR_REGISTRY_ERROR;
  330. goto Clean0;
  331. }
  332. Clean0:
  333. NOTHING;
  334. } except(EXCEPTION_EXECUTE_HANDLER) {
  335. Status = CR_FAILURE;
  336. }
  337. if (hKey != NULL) {
  338. RegCloseKey(hKey);
  339. }
  340. if (pList != NULL) {
  341. HeapFree(ghPnPHeap, 0, pList);
  342. }
  343. return Status;
  344. } // PNP_AddEmptyLogConf
  345. CONFIGRET
  346. PNP_FreeLogConf(
  347. IN handle_t hBinding,
  348. IN LPWSTR pDeviceID,
  349. IN ULONG ulType,
  350. IN ULONG ulTag,
  351. IN ULONG ulFlags
  352. )
  353. /*++
  354. Routine Description:
  355. This the server-side of an RPC remote call. This routine frees a
  356. logical configuration.
  357. Arguments:
  358. hBinding RPC binding handle.
  359. pDeviceID Null-terminated device instance id string.
  360. ulType Identifies which type of log conf is requested.
  361. ulTag Identifies which log conf from the specified type
  362. of log conf we want.
  363. ulFlags Not used, must be zero.
  364. Return Value:
  365. If the specified device instance is valid, it returns CR_SUCCESS,
  366. otherwise it returns CR_ error code.
  367. --*/
  368. {
  369. CONFIGRET Status = CR_SUCCESS;
  370. HKEY hKey = NULL;
  371. WCHAR szValueName[64];
  372. LPBYTE pList = NULL, pTemp = NULL, pNext = NULL;
  373. ULONG RegDataType = 0, Index = 0, ulListSize = 0, ulSize = 0;
  374. try {
  375. //
  376. // verify client access
  377. //
  378. if (!VerifyClientAccess(hBinding, &gLuidLoadDriverPrivilege)) {
  379. Status = CR_ACCESS_DENIED;
  380. goto Clean0;
  381. }
  382. //
  383. // validate parameters
  384. //
  385. if (INVALID_FLAGS(ulFlags, 0)) {
  386. Status = CR_INVALID_FLAG;
  387. goto Clean0;
  388. }
  389. //
  390. // make sure original caller didn't specify root devnode (this
  391. // can't happen but Win95 does the check anyway)
  392. //
  393. if (!IsLegalDeviceId(pDeviceID) || IsRootDeviceID(pDeviceID)) {
  394. Status = CR_INVALID_DEVNODE;
  395. goto Clean0;
  396. }
  397. //
  398. // open a key to the device's LogConf subkey
  399. //
  400. Status = OpenLogConfKey(pDeviceID, ulType, &hKey);
  401. if (Status != CR_SUCCESS) {
  402. //
  403. // if the device id or LogConf subkey is not in registry,
  404. // that's okay, by definition the log conf is freed since it
  405. // doesn't exist
  406. //
  407. goto Clean0;
  408. }
  409. //
  410. // Retrieve log conf data from the registry
  411. //
  412. Status = GetLogConfData(hKey, ulType,
  413. &RegDataType, szValueName,
  414. &pList, &ulListSize);
  415. if (Status != CR_SUCCESS) {
  416. Status = CR_INVALID_LOG_CONF;
  417. goto Clean0;
  418. }
  419. //
  420. // If the log conf to free is the one and only log conf of this
  421. // type then delete the corresponding registry values
  422. //
  423. if ((RegDataType == REG_RESOURCE_LIST &&
  424. ((PCM_RESOURCE_LIST)pList)->Count <= 1) ||
  425. (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST &&
  426. ((PIO_RESOURCE_REQUIREMENTS_LIST)pList)->AlternativeLists <= 1)) {
  427. KdPrintEx((DPFLTR_PNPMGR_ID,
  428. DBGF_REGISTRY,
  429. "PNP_FreeLogConf: Deleting Value %ws from Device %ws\r\n",
  430. szValueName,
  431. pDeviceID));
  432. RegDeleteValue(hKey, szValueName);
  433. goto Clean0;
  434. }
  435. //
  436. // There are other log confs besides the one to delete, so I'll
  437. // have to remove the log conf from the data structs and resave
  438. // to the registry
  439. //
  440. //-----------------------------------------------------------
  441. // Specified log conf type contains Resource Data only
  442. //-----------------------------------------------------------
  443. if (RegDataType == REG_RESOURCE_LIST) {
  444. PCM_RESOURCE_LIST pResList = (PCM_RESOURCE_LIST)pList;
  445. PCM_FULL_RESOURCE_DESCRIPTOR pRes = NULL;
  446. if (ulTag >= pResList->Count) {
  447. Status = CR_INVALID_LOG_CONF;
  448. goto Clean0;
  449. }
  450. //
  451. // skip to the log conf to be deleted
  452. //
  453. pRes = (PCM_FULL_RESOURCE_DESCRIPTOR)(&pResList->List[0]); // first lc
  454. for (Index = 0; Index < ulTag; Index++) {
  455. pRes = AdvanceResourcePtr(pRes); // next lc
  456. }
  457. if (ulTag == pResList->Count-1) {
  458. //
  459. // If deleting the last log conf in the list, just truncate it
  460. //
  461. ulListSize = (ULONG)((ULONG_PTR)pRes - (ULONG_PTR)pResList);
  462. } else {
  463. //
  464. // Shift remaining log confs (after the log conf to be deleted)
  465. // up in the list, writing over the log conf to be deleted
  466. //
  467. pNext = (LPBYTE)AdvanceResourcePtr(pRes);
  468. ulSize = ulListSize - (DWORD)((ULONG_PTR)pNext - (ULONG_PTR)pResList);
  469. pTemp = HeapAlloc(ghPnPHeap, 0, ulSize);
  470. if (pTemp == NULL) {
  471. Status = CR_OUT_OF_MEMORY;
  472. goto Clean0;
  473. }
  474. memcpy(pTemp, pNext, ulSize); // save in temp buffer
  475. memcpy(pRes, pTemp, ulSize); // copy to deleted lc
  476. ulListSize -= (DWORD)((ULONG_PTR)pNext - (ULONG_PTR)pRes);
  477. }
  478. //
  479. // update the log conf list header
  480. //
  481. pResList->Count--;
  482. }
  483. //-----------------------------------------------------------
  484. // Specified log conf type contains requirements data only
  485. //-----------------------------------------------------------
  486. else if (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST) {
  487. PIO_RESOURCE_REQUIREMENTS_LIST pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)pList;
  488. PIO_RESOURCE_LIST pReq = NULL;
  489. if (ulTag >= pReqList->AlternativeLists) {
  490. Status = CR_INVALID_LOG_CONF;
  491. goto Clean0;
  492. }
  493. //
  494. // skip to the log conf to be deleted
  495. //
  496. pReq = (PIO_RESOURCE_LIST)(&pReqList->List[0]); // first lc
  497. for (Index = 0; Index < ulTag; Index++) {
  498. pReq = AdvanceRequirementsPtr(pReq); // next lc
  499. }
  500. //
  501. // If there's any log confs after the log conf that will be deleted,
  502. // then write them over the top of the log conf we're deleting and
  503. // truncate any left over data.
  504. //
  505. pNext = (LPBYTE)AdvanceRequirementsPtr(pReq);
  506. if (ulListSize > ((DWORD_PTR)pNext - (DWORD_PTR)pReqList)) {
  507. ulSize = ulListSize - (DWORD)((ULONG_PTR)pNext - (ULONG_PTR)pReqList);
  508. pTemp = HeapAlloc(ghPnPHeap, 0, ulSize);
  509. if (pTemp == NULL) {
  510. Status = CR_OUT_OF_MEMORY;
  511. goto Clean0;
  512. }
  513. memcpy(pTemp, pNext, ulSize); // save in temp buffer
  514. memcpy(pReq, pTemp, ulSize); // copy to deleted lc
  515. ulListSize -= (DWORD)((ULONG_PTR)pNext - (ULONG_PTR)pReq);
  516. } else {
  517. //
  518. // No log confs trailing the log conf to be deleted so just
  519. // truncate it.
  520. //
  521. ulListSize = (ULONG)((ULONG_PTR)pReq - (ULONG_PTR)pReqList);
  522. }
  523. //
  524. // update the log conf list header
  525. //
  526. pReqList->AlternativeLists--;
  527. pReqList->ListSize = ulListSize;
  528. }
  529. //
  530. // Write out the updated log conf list to the registry
  531. //
  532. if (RegSetValueEx(hKey, szValueName, 0, RegDataType, pList,
  533. ulListSize) != ERROR_SUCCESS) {
  534. Status = CR_REGISTRY_ERROR;
  535. goto Clean0;
  536. }
  537. Clean0:
  538. NOTHING;
  539. } except(EXCEPTION_EXECUTE_HANDLER) {
  540. Status = CR_FAILURE;
  541. }
  542. if (hKey != NULL) {
  543. RegCloseKey(hKey);
  544. }
  545. if (pList != NULL) {
  546. HeapFree(ghPnPHeap, 0, pList);
  547. }
  548. if (pTemp != NULL) {
  549. HeapFree(ghPnPHeap, 0, pTemp);
  550. }
  551. return Status;
  552. } // PNP_FreeLogConf
  553. CONFIGRET
  554. PNP_GetFirstLogConf(
  555. IN handle_t hBinding,
  556. IN LPWSTR pDeviceID,
  557. IN ULONG ulType,
  558. OUT PULONG pulTag,
  559. IN ULONG ulFlags
  560. )
  561. /*++
  562. Routine Description:
  563. This the server-side of an RPC remote call. This routine finds the
  564. first log conf of this type for this devnode.
  565. Arguments:
  566. hBinding RPC binding handle, not used.
  567. pDeviceID Null-terminated device instance id string.
  568. ulType Describes the type of log conf to find.
  569. pulTag Returns tag that identifies which log config this is.
  570. ulFlags Not used (but may specify LOG_CONF_BITS).
  571. Return Value:
  572. If the specified device instance is valid, it returns CR_SUCCESS,
  573. otherwise it returns CR_ error code.
  574. --*/
  575. {
  576. CONFIGRET Status = CR_SUCCESS;
  577. HKEY hKey = NULL;
  578. LPBYTE pList = NULL;
  579. WCHAR szValueName[64];
  580. ULONG RegDataType = 0, ulListSize = 0;
  581. UNREFERENCED_PARAMETER(hBinding);
  582. try {
  583. //
  584. // Initialize output parameters. The index of the "first" lc will always
  585. // be zero as long as at least one lc exists.
  586. //
  587. if (!ARGUMENT_PRESENT(pulTag)) {
  588. Status = CR_INVALID_POINTER;
  589. } else {
  590. *pulTag = 0;
  591. }
  592. //
  593. // validate parameters
  594. //
  595. if (INVALID_FLAGS(ulFlags, LOG_CONF_BITS)) {
  596. Status = CR_INVALID_FLAG;
  597. goto Clean0;
  598. }
  599. if (!IsLegalDeviceId(pDeviceID)) {
  600. Status = CR_INVALID_DEVNODE;
  601. goto Clean0;
  602. }
  603. //
  604. // open a key to the device's LogConf subkey. If the device id is not
  605. // in the registry, the devnode doesn't exist and therefore neither
  606. // does the log conf
  607. //
  608. Status = OpenLogConfKey(pDeviceID, ulType, &hKey);
  609. if (Status != CR_SUCCESS) {
  610. Status = CR_NO_MORE_LOG_CONF;
  611. goto Clean0;
  612. }
  613. //
  614. // Migrate any log conf data that might have been written to
  615. // registry by NT 4.0 Beta I code.
  616. //
  617. MigrateObsoleteDetectionInfo(pDeviceID, hKey);
  618. //
  619. // Retrieve log conf data from the registry
  620. //
  621. Status = GetLogConfData(hKey, ulType,
  622. &RegDataType, szValueName,
  623. &pList, &ulListSize);
  624. if (Status != CR_SUCCESS) {
  625. Status = CR_NO_MORE_LOG_CONF;
  626. goto Clean0;
  627. }
  628. //
  629. // Specified log conf type contains Resource Data only
  630. //
  631. if (RegDataType == REG_RESOURCE_LIST) {
  632. PCM_RESOURCE_LIST pResList = (PCM_RESOURCE_LIST)pList;
  633. if (pResList->Count == 0) {
  634. Status = CR_NO_MORE_LOG_CONF;
  635. goto Clean0;
  636. }
  637. }
  638. //
  639. // Specified log conf type contains requirements data only
  640. //
  641. else if (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST) {
  642. PIO_RESOURCE_REQUIREMENTS_LIST pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)pList;
  643. if (pReqList->AlternativeLists == 0) {
  644. Status = CR_NO_MORE_LOG_CONF;
  645. goto Clean0;
  646. }
  647. }
  648. Clean0:
  649. NOTHING;
  650. } except(EXCEPTION_EXECUTE_HANDLER) {
  651. Status = CR_FAILURE;
  652. }
  653. if (hKey != NULL) {
  654. RegCloseKey(hKey);
  655. }
  656. if (pList != NULL) {
  657. HeapFree(ghPnPHeap, 0, pList);
  658. }
  659. return Status;
  660. } // PNP_GetFirstLogConf
  661. CONFIGRET
  662. PNP_GetNextLogConf(
  663. IN handle_t hBinding,
  664. IN LPWSTR pDeviceID,
  665. IN ULONG ulType,
  666. IN ULONG ulCurrentTag,
  667. OUT PULONG pulNextTag,
  668. IN ULONG ulFlags
  669. )
  670. /*++
  671. Routine Description:
  672. This the server-side of an RPC remote call. This routine finds the
  673. next log conf of this type for this devnode.
  674. Arguments:
  675. hBinding RPC binding handle, not used.
  676. pDeviceID Null-terminated device instance id string.
  677. ulType Specifies what type of log conf to retrieve.
  678. ulCurrent Specifies current log conf in the enumeration.
  679. pulNext Returns next log conf of this type for this device id.
  680. ulFlags Not used, must be zero.
  681. Return Value:
  682. If the specified device instance is valid, it returns CR_SUCCESS,
  683. otherwise it returns CR_ error code.
  684. --*/
  685. {
  686. CONFIGRET Status = CR_SUCCESS;
  687. HKEY hKey = NULL;
  688. WCHAR szValueName[64];
  689. ULONG RegDataType = 0, ulListSize = 0;
  690. LPBYTE pList = NULL;
  691. UNREFERENCED_PARAMETER(hBinding);
  692. try {
  693. //
  694. // Initialize output parameters
  695. //
  696. if (!ARGUMENT_PRESENT(pulNextTag)) {
  697. Status = CR_INVALID_POINTER;
  698. goto Clean0;
  699. } else {
  700. *pulNextTag = 0;
  701. }
  702. //
  703. // validate parameters
  704. //
  705. if (INVALID_FLAGS(ulFlags, 0)) {
  706. Status = CR_INVALID_FLAG;
  707. goto Clean0;
  708. }
  709. if (!IsLegalDeviceId(pDeviceID)) {
  710. Status = CR_INVALID_DEVNODE;
  711. goto Clean0;
  712. }
  713. //
  714. // open a key to the device's LogConf subkey. If the device id is not
  715. // in the registry, the devnode doesn't exist and therefore neither
  716. // does the log conf
  717. //
  718. Status = OpenLogConfKey(pDeviceID, ulType, &hKey);
  719. if (Status != CR_SUCCESS) {
  720. Status = CR_INVALID_LOG_CONF;
  721. goto Clean0;
  722. }
  723. //
  724. // Retrieve log conf data from the registry
  725. //
  726. Status = GetLogConfData(hKey, ulType,
  727. &RegDataType, szValueName,
  728. &pList, &ulListSize);
  729. if (Status != CR_SUCCESS) {
  730. Status = CR_NO_MORE_LOG_CONF;
  731. goto Clean0;
  732. }
  733. //
  734. // Specified log conf type contains Resource Data only
  735. //
  736. if (RegDataType == REG_RESOURCE_LIST) {
  737. PCM_RESOURCE_LIST pResList = (PCM_RESOURCE_LIST)pList;
  738. if (ulCurrentTag >= pResList->Count) {
  739. Status = CR_INVALID_LOG_CONF;
  740. goto Clean0;
  741. }
  742. //
  743. // Is the "current" log conf the last log conf?
  744. //
  745. if (ulCurrentTag == pResList->Count - 1) {
  746. Status = CR_NO_MORE_LOG_CONF;
  747. goto Clean0;
  748. }
  749. }
  750. //
  751. // Specified log conf type contains requirements data only
  752. //
  753. else if (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST) {
  754. PIO_RESOURCE_REQUIREMENTS_LIST pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)pList;
  755. if (ulCurrentTag >= pReqList->AlternativeLists) {
  756. Status = CR_INVALID_LOG_CONF;
  757. goto Clean0;
  758. }
  759. //
  760. // Is the "current" log conf the last log conf?
  761. //
  762. if (ulCurrentTag == pReqList->AlternativeLists - 1) {
  763. Status = CR_NO_MORE_LOG_CONF;
  764. goto Clean0;
  765. }
  766. }
  767. //
  768. // There's at least one more log conf, return the next index value
  769. //
  770. *pulNextTag = ulCurrentTag + 1;
  771. Clean0:
  772. NOTHING;
  773. } except(EXCEPTION_EXECUTE_HANDLER) {
  774. Status = CR_FAILURE;
  775. }
  776. if (hKey != NULL) {
  777. RegCloseKey(hKey);
  778. }
  779. if (pList != NULL) {
  780. HeapFree(ghPnPHeap, 0, pList);
  781. }
  782. return Status;
  783. } // PNP_GetNextLogConf
  784. CONFIGRET
  785. PNP_GetLogConfPriority(
  786. IN handle_t hBinding,
  787. IN LPWSTR pDeviceID,
  788. IN ULONG ulType,
  789. IN ULONG ulTag,
  790. OUT PULONG pPriority,
  791. IN ULONG ulFlags
  792. )
  793. /*++
  794. Routine Description:
  795. This the server-side of an RPC remote call. This routine returns the
  796. priority value assigned to the specified log config.
  797. Arguments:
  798. hBinding RPC binding handle, not used.
  799. pDeviceID Null-terminated device instance id string.
  800. ulType Specifies what type of log conf to retrieve priority for.
  801. ulCurrent Specifies current log conf in the enumeration.
  802. pulNext Returns priority value of specified log conf.
  803. ulFlags Not used, must be zero.
  804. Return Value:
  805. If the specified device instance is valid, it returns CR_SUCCESS,
  806. otherwise it returns CR_ error code.
  807. --*/
  808. {
  809. CONFIGRET Status = CR_SUCCESS;
  810. HKEY hKey = NULL;
  811. WCHAR szValueName[64];
  812. ULONG RegDataType = 0, ulListSize = 0, index, count;
  813. LPBYTE pList = NULL, pLogConf = NULL, pRD = NULL;
  814. PIO_RESOURCE_REQUIREMENTS_LIST pReqList = NULL;
  815. PIO_RESOURCE_LIST pReq = NULL;
  816. PIO_RESOURCE_DESCRIPTOR pReqDes = NULL;
  817. UNREFERENCED_PARAMETER(hBinding);
  818. try {
  819. //
  820. // Initialize output parameters
  821. //
  822. if (!ARGUMENT_PRESENT(pPriority)) {
  823. Status = CR_INVALID_POINTER;
  824. goto Clean0;
  825. } else {
  826. *pPriority = 0;
  827. }
  828. //
  829. // validate parameters
  830. //
  831. if (INVALID_FLAGS(ulFlags, 0)) {
  832. Status = CR_INVALID_FLAG;
  833. goto Clean0;
  834. }
  835. if (!IsLegalDeviceId(pDeviceID)) {
  836. Status = CR_INVALID_DEVNODE;
  837. goto Clean0;
  838. }
  839. //
  840. // open a key to the device's LogConf subkey. If the device id is not
  841. // in the registry, the devnode doesn't exist and therefore neither
  842. // does the log conf
  843. //
  844. Status = OpenLogConfKey(pDeviceID, ulType, &hKey);
  845. if (Status != CR_SUCCESS) {
  846. Status = CR_INVALID_LOG_CONF;
  847. goto Clean0;
  848. }
  849. //
  850. // Retrieve log conf data from the registry
  851. //
  852. Status = GetLogConfData(hKey, ulType,
  853. &RegDataType, szValueName,
  854. &pList, &ulListSize);
  855. if (Status != CR_SUCCESS) {
  856. Status = CR_INVALID_LOG_CONF;
  857. goto Clean0;
  858. }
  859. //
  860. // Priority values are only stored in requirements lists
  861. //
  862. if (RegDataType != REG_RESOURCE_REQUIREMENTS_LIST) {
  863. Status = CR_INVALID_LOG_CONF;
  864. goto Clean0;
  865. }
  866. //
  867. // Seek to the log conf that matches the log conf tag
  868. //
  869. if (!FindLogConf(pList, &pLogConf, RegDataType, ulTag)) {
  870. Status = CR_NO_SUCH_VALUE;
  871. goto Clean0;
  872. }
  873. //
  874. // Seek to the ConfigData res des, if any.
  875. //
  876. pReq = (PIO_RESOURCE_LIST)pLogConf;
  877. pReqDes = &pReq->Descriptors[0]; // first rd
  878. index = 0;
  879. count = 0;
  880. while (index < pReq->Count && pReqDes != NULL &&
  881. pReqDes->Type != CmResourceTypeConfigData) {
  882. pReqDes = AdvanceRequirementsDescriptorPtr(pReqDes, 1, pReq->Count - index, &count);
  883. index += count; // index of actual rd's in the struct
  884. }
  885. if (pReqDes == NULL || pReqDes->Type != CmResourceTypeConfigData) {
  886. //
  887. // No config data so we can't determine the priority.
  888. //
  889. Status = CR_NO_SUCH_VALUE;
  890. goto Clean0;
  891. }
  892. *pPriority = pReqDes->u.ConfigData.Priority;
  893. Clean0:
  894. NOTHING;
  895. } except(EXCEPTION_EXECUTE_HANDLER) {
  896. Status = CR_FAILURE;
  897. }
  898. if (hKey != NULL) {
  899. RegCloseKey(hKey);
  900. }
  901. if (pList != NULL) {
  902. HeapFree(ghPnPHeap, 0, pList);
  903. }
  904. return Status;
  905. } // PNP_GetLogConfPriority
  906. //------------------------------------------------------------------------
  907. // Private Utility Routines
  908. //------------------------------------------------------------------------
  909. CONFIGRET
  910. GetLogConfData(
  911. IN HKEY hKey,
  912. IN ULONG ulLogConfType,
  913. OUT PULONG pulRegDataType,
  914. OUT LPWSTR pszValueName,
  915. OUT LPBYTE *ppBuffer,
  916. OUT PULONG pulBufferSize
  917. )
  918. {
  919. switch (ulLogConfType) {
  920. //
  921. // BOOT, ALLOC, FORCED only have a Config value
  922. //
  923. case BOOT_LOG_CONF:
  924. lstrcpy(pszValueName, pszRegValueBootConfig);
  925. *pulRegDataType = REG_RESOURCE_LIST;
  926. break;
  927. case ALLOC_LOG_CONF:
  928. lstrcpy(pszValueName, pszRegValueAllocConfig);
  929. *pulRegDataType = REG_RESOURCE_LIST;
  930. break;
  931. case FORCED_LOG_CONF:
  932. lstrcpy(pszValueName, pszRegValueForcedConfig);
  933. *pulRegDataType = REG_RESOURCE_LIST;
  934. break;
  935. //
  936. // FILTERED, BASIC, OVERRIDE only have a Vector value
  937. //
  938. case FILTERED_LOG_CONF:
  939. lstrcpy(pszValueName, pszRegValueFilteredVector);
  940. *pulRegDataType = REG_RESOURCE_REQUIREMENTS_LIST;
  941. break;
  942. case BASIC_LOG_CONF:
  943. lstrcpy(pszValueName, pszRegValueBasicVector);
  944. *pulRegDataType = REG_RESOURCE_REQUIREMENTS_LIST;
  945. break;
  946. case OVERRIDE_LOG_CONF:
  947. lstrcpy(pszValueName, pszRegValueOverrideVector);
  948. *pulRegDataType = REG_RESOURCE_REQUIREMENTS_LIST;
  949. break;
  950. default:
  951. return CR_FAILURE;
  952. }
  953. //
  954. // retrieve the Log Conf registry data
  955. //
  956. if (RegQueryValueEx(hKey, pszValueName, NULL, NULL, NULL,
  957. pulBufferSize) != ERROR_SUCCESS) {
  958. return CR_INVALID_LOG_CONF;
  959. }
  960. *ppBuffer = HeapAlloc(ghPnPHeap, 0, *pulBufferSize);
  961. if (*ppBuffer == NULL) {
  962. return CR_OUT_OF_MEMORY;
  963. }
  964. if (RegQueryValueEx(hKey, pszValueName, NULL, NULL,
  965. (LPBYTE)*ppBuffer, pulBufferSize) != ERROR_SUCCESS) {
  966. return CR_INVALID_LOG_CONF;
  967. }
  968. return CR_SUCCESS;
  969. } // GetLogConfData
  970. PCM_FULL_RESOURCE_DESCRIPTOR
  971. AdvanceResourcePtr(
  972. IN PCM_FULL_RESOURCE_DESCRIPTOR pRes
  973. )
  974. {
  975. // Given a resource pointer, this routine advances to the beginning
  976. // of the next resource and returns a pointer to it. I assume that
  977. // at least one more resource exists in the resource list.
  978. LPBYTE p = NULL;
  979. ULONG LastResIndex = 0;
  980. if (pRes == NULL) {
  981. return NULL;
  982. }
  983. //
  984. // account for the size of the CM_FULL_RESOURCE_DESCRIPTOR
  985. // (includes the header plus a single imbedded
  986. // CM_PARTIAL_RESOURCE_DESCRIPTOR struct)
  987. //
  988. p = (LPBYTE)pRes + sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
  989. //
  990. // account for any resource descriptors in addition to the single
  991. // imbedded one I've already accounted for (if there aren't any,
  992. // then I'll end up subtracting off the extra imbedded descriptor
  993. // from the previous step)
  994. //
  995. p += (pRes->PartialResourceList.Count - 1) *
  996. sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
  997. //
  998. // finally, account for any extra device specific data at the end of
  999. // the last partial resource descriptor (if any)
  1000. //
  1001. if (pRes->PartialResourceList.Count > 0) {
  1002. LastResIndex = pRes->PartialResourceList.Count - 1;
  1003. if (pRes->PartialResourceList.PartialDescriptors[LastResIndex].Type ==
  1004. CmResourceTypeDeviceSpecific) {
  1005. p += pRes->PartialResourceList.PartialDescriptors[LastResIndex].
  1006. u.DeviceSpecificData.DataSize;
  1007. }
  1008. }
  1009. return (PCM_FULL_RESOURCE_DESCRIPTOR)p;
  1010. } // AdvanceResourcePtr
  1011. PIO_RESOURCE_LIST
  1012. AdvanceRequirementsPtr(
  1013. IN PIO_RESOURCE_LIST pReq
  1014. )
  1015. {
  1016. LPBYTE p = NULL;
  1017. if (pReq == NULL) {
  1018. return NULL;
  1019. }
  1020. //
  1021. // account for the size of the IO_RESOURCE_LIST (includes header plus
  1022. // a single imbedded IO_RESOURCE_DESCRIPTOR struct)
  1023. //
  1024. p = (LPBYTE)pReq + sizeof(IO_RESOURCE_LIST);
  1025. //
  1026. // account for any requirements descriptors in addition to the single
  1027. // imbedded one I've already accounted for (if there aren't any,
  1028. // then I'll end up subtracting off the extra imbedded descriptor
  1029. // from the previous step)
  1030. //
  1031. p += (pReq->Count - 1) * sizeof(IO_RESOURCE_DESCRIPTOR);
  1032. return (PIO_RESOURCE_LIST)p;
  1033. } // AdvanceRequirementsPtr
  1034. BOOL
  1035. MigrateObsoleteDetectionInfo(
  1036. IN LPWSTR pszDeviceID,
  1037. IN HKEY hLogConfKey
  1038. )
  1039. {
  1040. LONG RegStatus = ERROR_SUCCESS;
  1041. HKEY hKey = NULL;
  1042. ULONG ulSize = 0;
  1043. LPBYTE ptr = NULL;
  1044. PCM_RESOURCE_LIST pResList = NULL;
  1045. PPrivate_Log_Conf pDetectData = NULL;
  1046. //
  1047. // First, delete any of the log conf pairs that aren't valid any more
  1048. //
  1049. RegDeleteValue(hLogConfKey, TEXT("BootConfigVector"));
  1050. RegDeleteValue(hLogConfKey, TEXT("AllocConfigVector"));
  1051. RegDeleteValue(hLogConfKey, TEXT("ForcedConfigVector"));
  1052. RegDeleteValue(hLogConfKey, TEXT("BasicConfig"));
  1053. RegDeleteValue(hLogConfKey, TEXT("FilteredConfig"));
  1054. RegDeleteValue(hLogConfKey, TEXT("OverrideConfig"));
  1055. //
  1056. // open the device instance key in the registry
  1057. //
  1058. if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0,
  1059. KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS) {
  1060. goto Clean0; // nothing to migrate
  1061. }
  1062. //
  1063. // If there is already a boot log config value then we can't
  1064. // migrate any old detect info
  1065. //
  1066. RegStatus = RegQueryValueEx(hLogConfKey, pszRegValueBootConfig,
  1067. NULL, NULL, NULL, &ulSize);
  1068. if (RegStatus == ERROR_SUCCESS && ulSize > 0) {
  1069. goto Clean0; // can't migrate
  1070. }
  1071. //
  1072. // retrieve any old detect signature info
  1073. //
  1074. RegStatus = RegQueryValueEx(hKey, pszRegValueDetectSignature,
  1075. NULL, NULL, NULL, &ulSize);
  1076. if ((RegStatus != ERROR_SUCCESS) || (ulSize == 0)) {
  1077. goto Clean0; // nothing to migrate
  1078. }
  1079. pDetectData = (PPrivate_Log_Conf)HeapAlloc(ghPnPHeap, 0, ulSize);
  1080. if (pDetectData == NULL) {
  1081. goto Clean0; // insufficient memory
  1082. }
  1083. RegStatus = RegQueryValueEx(hKey, pszRegValueDetectSignature,
  1084. NULL, NULL, (LPBYTE)pDetectData, &ulSize);
  1085. if ((RegStatus != ERROR_SUCCESS) || (ulSize == 0)) {
  1086. goto Clean0; // nothing to migrate
  1087. }
  1088. //
  1089. // Create an empty boot log conf and add this class specific data
  1090. // to it
  1091. //
  1092. ulSize = pDetectData->LC_CS.CS_Header.CSD_SignatureLength +
  1093. pDetectData->LC_CS.CS_Header.CSD_LegacyDataSize +
  1094. sizeof(GUID);
  1095. pResList = HeapAlloc(ghPnPHeap, HEAP_ZERO_MEMORY, sizeof(CM_RESOURCE_LIST) + ulSize);
  1096. if (pResList == NULL) {
  1097. goto Clean0; // insufficient memory
  1098. }
  1099. //
  1100. // initialize resource list
  1101. //
  1102. pResList->Count = 1;
  1103. pResList->List[0].InterfaceType = InterfaceTypeUndefined;
  1104. pResList->List[0].BusNumber = 0;
  1105. pResList->List[0].PartialResourceList.Version = NT_RESLIST_VERSION;
  1106. pResList->List[0].PartialResourceList.Revision = NT_RESLIST_REVISION;
  1107. pResList->List[0].PartialResourceList.Count = 1;
  1108. pResList->List[0].PartialResourceList.PartialDescriptors[0].Type =
  1109. CmResourceTypeDeviceSpecific;
  1110. pResList->List[0].PartialResourceList.PartialDescriptors[0].ShareDisposition =
  1111. CmResourceShareUndetermined;
  1112. pResList->List[0].PartialResourceList.PartialDescriptors[0].Flags =
  1113. (USHORT)pDetectData->LC_CS.CS_Header.CSD_Flags;
  1114. pResList->List[0].PartialResourceList.PartialDescriptors[0].
  1115. u.DeviceSpecificData.DataSize = ulSize;
  1116. pResList->List[0].PartialResourceList.PartialDescriptors[0].
  1117. u.DeviceSpecificData.Reserved1 =
  1118. pDetectData->LC_CS.CS_Header.CSD_LegacyDataSize;
  1119. pResList->List[0].PartialResourceList.PartialDescriptors[0].
  1120. u.DeviceSpecificData.Reserved2 =
  1121. pDetectData->LC_CS.CS_Header.CSD_SignatureLength;
  1122. //
  1123. // copy the legacy and class-specific signature data
  1124. //
  1125. ptr = (LPBYTE)(&pResList->List[0].PartialResourceList.PartialDescriptors[1]);
  1126. memcpy(ptr,
  1127. pDetectData->LC_CS.CS_Header.CSD_Signature +
  1128. pDetectData->LC_CS.CS_Header.CSD_LegacyDataOffset,
  1129. pDetectData->LC_CS.CS_Header.CSD_LegacyDataSize); // legacy data
  1130. ptr += pDetectData->LC_CS.CS_Header.CSD_LegacyDataSize;
  1131. memcpy(ptr,
  1132. pDetectData->LC_CS.CS_Header.CSD_Signature,
  1133. pDetectData->LC_CS.CS_Header.CSD_SignatureLength); // signature
  1134. ptr += pDetectData->LC_CS.CS_Header.CSD_SignatureLength;
  1135. memcpy(ptr,
  1136. &pDetectData->LC_CS.CS_Header.CSD_ClassGuid,
  1137. sizeof(GUID)); // GUID
  1138. //
  1139. // Write out the new/updated log conf list to the registry
  1140. //
  1141. RegSetValueEx(hLogConfKey, pszRegValueBootConfig, 0,
  1142. REG_RESOURCE_LIST, (LPBYTE)pResList,
  1143. ulSize + sizeof(CM_RESOURCE_LIST));
  1144. //
  1145. // Delete the old detect signature info
  1146. //
  1147. RegDeleteValue(hKey, pszRegValueDetectSignature);
  1148. Clean0:
  1149. if (hKey != NULL) {
  1150. RegCloseKey(hKey);
  1151. }
  1152. if (pDetectData != NULL) {
  1153. HeapFree(ghPnPHeap, 0, pDetectData);
  1154. }
  1155. if (pResList != NULL) {
  1156. HeapFree(ghPnPHeap, 0, pResList);
  1157. }
  1158. return TRUE;
  1159. } // MigrateObsoleteDetectionInfo