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.

708 lines
17 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Module: fn.cpp
  4. //
  5. // Description:
  6. //
  7. //
  8. //@@BEGIN_MSINTERNAL
  9. // Development Team:
  10. // Mike McLaughlin
  11. //
  12. // History: Date Author Comment
  13. //
  14. // To Do: Date Author Comment
  15. //
  16. //@@END_MSINTERNAL
  17. //
  18. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  19. // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  20. // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  21. // PURPOSE.
  22. //
  23. // Copyright (c) 1996-1999 Microsoft Corporation. All Rights Reserved.
  24. //
  25. //---------------------------------------------------------------------------
  26. #include "common.h"
  27. EXTERN_C VOID KeAttachProcess(PVOID);
  28. EXTERN_C VOID KeDetachProcess(VOID);
  29. //---------------------------------------------------------------------------
  30. //---------------------------------------------------------------------------
  31. PLIST_FILTER_NODE gplstFilterNode = NULL;
  32. PLIST_MULTI_LOGICAL_FILTER_NODE gplstLogicalFilterNode = NULL;
  33. //---------------------------------------------------------------------------
  34. //---------------------------------------------------------------------------
  35. NTSTATUS
  36. InitializeFilterNode()
  37. {
  38. if(gplstFilterNode == NULL) {
  39. gplstFilterNode = new LIST_FILTER_NODE;
  40. if(gplstFilterNode == NULL) {
  41. return(STATUS_INSUFFICIENT_RESOURCES);
  42. }
  43. }
  44. if(gplstLogicalFilterNode == NULL) {
  45. gplstLogicalFilterNode = new LIST_MULTI_LOGICAL_FILTER_NODE;
  46. if(gplstLogicalFilterNode == NULL) {
  47. return(STATUS_INSUFFICIENT_RESOURCES);
  48. }
  49. }
  50. return(STATUS_SUCCESS);
  51. }
  52. #pragma PAGEABLE_CODE
  53. #pragma PAGEABLE_DATA
  54. VOID
  55. UninitializeFilterNode()
  56. {
  57. PFILTER_NODE pFilterNode;
  58. FOR_EACH_LIST_ITEM_DELETE(gplstFilterNode, pFilterNode) {
  59. if (pFilterNode->pDeviceNode) {
  60. pFilterNode->pDeviceNode->pFilterNode=NULL;
  61. }
  62. delete pFilterNode;
  63. DELETE_LIST_ITEM(gplstFilterNode);
  64. } END_EACH_LIST_ITEM
  65. delete gplstFilterNode;
  66. gplstFilterNode = NULL;
  67. delete gplstLogicalFilterNode;
  68. gplstLogicalFilterNode = NULL;
  69. }
  70. //---------------------------------------------------------------------------
  71. CFilterNode::CFilterNode(
  72. ULONG fulType
  73. )
  74. {
  75. ASSERT(gplstFilterNode != NULL);
  76. SetType(fulType);
  77. AddListEnd(gplstFilterNode);
  78. DPF2(60, "CFilterNode: %08x %s", this, DumpName());
  79. }
  80. CFilterNode::~CFilterNode(
  81. )
  82. {
  83. PFILTER_NODE pFilterNode;
  84. Assert(this);
  85. DPF2(60, "~CFilterNode: %08x %s", this, DumpName());
  86. RemoveListCheck();
  87. if (pDeviceNode) {
  88. pDeviceNode->pFilterNode = NULL;
  89. }
  90. if (pFileObject) {
  91. ::ObDereferenceObject(pFileObject);
  92. pFileObject = NULL;
  93. }
  94. delete pDeviceNode;
  95. FOR_EACH_LIST_ITEM(gplstFilterNode, pFilterNode) {
  96. pFilterNode->lstConnectedFilterNode.RemoveList(this);
  97. } END_EACH_LIST_ITEM
  98. // Free all other memory
  99. lstFreeMem.EnumerateList(CListDataItem::Destroy);
  100. }
  101. NTSTATUS
  102. CFilterNode::Create(
  103. PWSTR pwstrDeviceInterface
  104. )
  105. {
  106. PFILTER_NODE_INSTANCE pFilterNodeInstance = NULL;
  107. PKEY_VALUE_FULL_INFORMATION pkvfi = NULL;
  108. NTSTATUS Status = STATUS_SUCCESS;
  109. HANDLE hkeyDeviceClass = NULL;
  110. HANDLE hkeySysaudio = NULL;
  111. UNICODE_STRING ustrFilterName;
  112. KSPROPERTY PropertyComponentId;
  113. KSCOMPONENTID ComponentId;
  114. ULONG BytesReturned;
  115. PFILE_OBJECT pFileObject;
  116. this->pwstrDeviceInterface = new WCHAR[wcslen(pwstrDeviceInterface) + 1];
  117. if(this->pwstrDeviceInterface == NULL) {
  118. Status = STATUS_INSUFFICIENT_RESOURCES;
  119. goto exit;
  120. }
  121. wcscpy(this->pwstrDeviceInterface, pwstrDeviceInterface);
  122. Status = lstFreeMem.AddList(this->pwstrDeviceInterface);
  123. if(!NT_SUCCESS(Status)) {
  124. Trap();
  125. delete this->pwstrDeviceInterface;
  126. goto exit;
  127. }
  128. Status = CFilterNodeInstance::Create(&pFilterNodeInstance, this);
  129. if(!NT_SUCCESS(Status)) {
  130. goto exit;
  131. }
  132. pFileObject = pFilterNodeInstance->pFileObject;
  133. // Get the filter's friendly name
  134. RtlInitUnicodeString(&ustrFilterName, this->pwstrDeviceInterface);
  135. Status = IoOpenDeviceInterfaceRegistryKey(
  136. &ustrFilterName,
  137. KEY_READ,
  138. &hkeyDeviceClass);
  139. if(!NT_SUCCESS(Status)) {
  140. goto exit;
  141. }
  142. Status = OpenRegistryKey(L"Sysaudio", &hkeySysaudio, hkeyDeviceClass);
  143. if(NT_SUCCESS(Status)) {
  144. Status = QueryRegistryValue(hkeySysaudio, L"Disabled", &pkvfi);
  145. if(NT_SUCCESS(Status)) {
  146. if(pkvfi->Type == REG_DWORD) {
  147. if(*((PULONG)(((PUCHAR)pkvfi) + pkvfi->DataOffset))) {
  148. Status = STATUS_SERVER_DISABLED;
  149. delete pkvfi;
  150. goto exit;
  151. }
  152. }
  153. delete pkvfi;
  154. }
  155. Status = QueryRegistryValue(hkeySysaudio, L"Device", &pkvfi);
  156. if(NT_SUCCESS(Status)) {
  157. if(pkvfi->Type == REG_SZ && pkvfi->DataLength > 0) {
  158. Status = lstFreeMem.AddList(pkvfi);
  159. if(!NT_SUCCESS(Status)) {
  160. Trap();
  161. delete pkvfi;
  162. goto exit;
  163. }
  164. Status = AddDeviceInterfaceMatch(
  165. (PWSTR)(((PUCHAR)pkvfi) + pkvfi->DataOffset));
  166. if(!NT_SUCCESS(Status)) {
  167. Trap();
  168. delete pkvfi;
  169. goto exit;
  170. }
  171. }
  172. else {
  173. delete pkvfi;
  174. }
  175. }
  176. Status = QueryRegistryValue(hkeySysaudio, L"Order", &pkvfi);
  177. if(NT_SUCCESS(Status)) {
  178. if(pkvfi->Type == REG_DWORD) {
  179. ulOrder = *((PULONG)(((PUCHAR)pkvfi) + pkvfi->DataOffset));
  180. }
  181. delete pkvfi;
  182. }
  183. Status = QueryRegistryValue(hkeySysaudio, L"Capture", &pkvfi);
  184. if(NT_SUCCESS(Status)) {
  185. if(pkvfi->Type == REG_DWORD) {
  186. if(*((PULONG)(((PUCHAR)pkvfi) + pkvfi->DataOffset))) {
  187. ulFlags |= FN_FLAGS_CAPTURE;
  188. }
  189. else {
  190. ulFlags |= FN_FLAGS_NO_CAPTURE;
  191. }
  192. }
  193. delete pkvfi;
  194. }
  195. Status = QueryRegistryValue(hkeySysaudio, L"Render", &pkvfi);
  196. if(NT_SUCCESS(Status)) {
  197. if(pkvfi->Type == REG_DWORD) {
  198. if(*((PULONG)(((PUCHAR)pkvfi) + pkvfi->DataOffset))) {
  199. ulFlags |= FN_FLAGS_RENDER;
  200. }
  201. else {
  202. ulFlags |= FN_FLAGS_NO_RENDER;
  203. }
  204. }
  205. delete pkvfi;
  206. }
  207. }
  208. Status = QueryRegistryValue(hkeyDeviceClass, L"FriendlyName", &pkvfi);
  209. if(NT_SUCCESS(Status)) {
  210. if(pkvfi->Type == REG_SZ && pkvfi->DataLength > 0) {
  211. Status = lstFreeMem.AddList(pkvfi);
  212. if(!NT_SUCCESS(Status)) {
  213. Trap();
  214. delete pkvfi;
  215. goto exit;
  216. }
  217. pwstrFriendlyName = (PWSTR)(((PUCHAR)pkvfi) + pkvfi->DataOffset);
  218. }
  219. else {
  220. delete pkvfi;
  221. }
  222. }
  223. // Get the component Id of the filter
  224. PropertyComponentId.Set = KSPROPSETID_General;
  225. PropertyComponentId.Id = KSPROPERTY_GENERAL_COMPONENTID;
  226. PropertyComponentId.Flags = KSPROPERTY_TYPE_GET;
  227. Status = KsSynchronousIoControlDevice(
  228. pFileObject,
  229. KernelMode,
  230. IOCTL_KS_PROPERTY,
  231. &PropertyComponentId,
  232. sizeof(PropertyComponentId),
  233. &ComponentId,
  234. sizeof(ComponentId),
  235. &BytesReturned);
  236. // Store the component Id
  237. if (NT_SUCCESS(Status)) {
  238. ASSERT(BytesReturned >= sizeof(ComponentId));
  239. this->ComponentId = new KSCOMPONENTID;
  240. if (this->ComponentId) {
  241. RtlCopyMemory(this->ComponentId,
  242. &ComponentId,
  243. sizeof(KSCOMPONENTID));
  244. Status = lstFreeMem.AddList(this->ComponentId);
  245. if(!NT_SUCCESS(Status)) {
  246. delete this->ComponentId;
  247. this->ComponentId = NULL;
  248. }
  249. }
  250. }
  251. else {
  252. this->ComponentId = NULL;
  253. }
  254. Status = this->ProfileFilter(pFileObject);
  255. exit:
  256. if(hkeySysaudio != NULL) {
  257. ZwClose(hkeySysaudio);
  258. }
  259. if(hkeyDeviceClass != NULL) {
  260. ZwClose(hkeyDeviceClass);
  261. }
  262. if (pFilterNodeInstance) {
  263. pFilterNodeInstance->Destroy();
  264. }
  265. return(Status);
  266. }
  267. NTSTATUS
  268. CFilterNode::ProfileFilter(
  269. PFILE_OBJECT pFileObject
  270. )
  271. {
  272. NTSTATUS Status = STATUS_SUCCESS;
  273. PKSMULTIPLE_ITEM pCategories = NULL;
  274. PKSMULTIPLE_ITEM pConnections = NULL;
  275. PKSMULTIPLE_ITEM pNodes = NULL;
  276. ULONG PinId;
  277. PPIN_INFO pPinInfo;
  278. ULONG i;
  279. KSTOPOLOGY Topology;
  280. RtlZeroMemory(&Topology, sizeof(Topology));
  281. // Get the number of pins
  282. Status = GetPinProperty(
  283. pFileObject,
  284. KSPROPERTY_PIN_CTYPES,
  285. 0, // doesn't matter for ctypes
  286. sizeof(cPins),
  287. (PVOID)&cPins);
  288. if(!NT_SUCCESS(Status)) {
  289. Trap();
  290. goto exit;
  291. }
  292. // Get the topology of the filter
  293. Status = GetProperty(
  294. pFileObject,
  295. &KSPROPSETID_Topology,
  296. KSPROPERTY_TOPOLOGY_CATEGORIES,
  297. 0,
  298. NULL,
  299. (PVOID*)&pCategories);
  300. if(!NT_SUCCESS(Status)) {
  301. Trap();
  302. goto exit;
  303. }
  304. if(pCategories != NULL) {
  305. Topology.CategoriesCount = pCategories->Count;
  306. Topology.Categories = (GUID*)(pCategories + 1);
  307. ULONG fulType = 0;
  308. for(i = 0; i < pCategories->Count; i++) {
  309. GetFilterTypeFromGuid((LPGUID)&Topology.Categories[i], &fulType);
  310. }
  311. SetType(fulType);
  312. }
  313. Status = GetProperty(
  314. pFileObject,
  315. &KSPROPSETID_Topology,
  316. KSPROPERTY_TOPOLOGY_NODES,
  317. 0,
  318. NULL,
  319. (PVOID*)&pNodes);
  320. if(!NT_SUCCESS(Status)) {
  321. Trap();
  322. goto exit;
  323. }
  324. if(pNodes != NULL) {
  325. Status = lstFreeMem.AddList(pNodes);
  326. if(!NT_SUCCESS(Status)) {
  327. Trap();
  328. delete pNodes;
  329. goto exit;
  330. }
  331. Topology.TopologyNodesCount = pNodes->Count;
  332. Topology.TopologyNodes = (GUID*)(pNodes + 1);
  333. }
  334. Status = GetProperty(
  335. pFileObject,
  336. &KSPROPSETID_Topology,
  337. KSPROPERTY_TOPOLOGY_CONNECTIONS,
  338. 0,
  339. NULL,
  340. (PVOID*)&pConnections);
  341. if(!NT_SUCCESS(Status)) {
  342. Trap();
  343. goto exit;
  344. }
  345. if(pConnections != NULL) {
  346. Topology.TopologyConnectionsCount = pConnections->Count;
  347. Topology.TopologyConnections =
  348. (PKSTOPOLOGY_CONNECTION)(pConnections + 1);
  349. }
  350. // Each pin loop
  351. for(PinId = 0; PinId < cPins; PinId++) {
  352. pPinInfo = new PIN_INFO(this, PinId);
  353. if(pPinInfo == NULL) {
  354. Status = STATUS_INSUFFICIENT_RESOURCES;
  355. Trap();
  356. goto exit;
  357. }
  358. Status = pPinInfo->Create(pFileObject);
  359. if(!NT_SUCCESS(Status)) {
  360. goto exit;
  361. }
  362. }
  363. Status = CreateTopology(this, &Topology);
  364. if(!NT_SUCCESS(Status)) {
  365. goto exit;
  366. }
  367. Status = lstPinInfo.EnumerateList(CPinInfo::CreatePhysicalConnection);
  368. if(Status == STATUS_CONTINUE) {
  369. Status = STATUS_SUCCESS;
  370. }
  371. else if(!NT_SUCCESS(Status)) {
  372. goto exit;
  373. }
  374. Status = CLogicalFilterNode::CreateAll(this);
  375. if(!NT_SUCCESS(Status)) {
  376. goto exit;
  377. }
  378. exit:
  379. delete pCategories;
  380. delete pConnections;
  381. return(Status);
  382. }
  383. NTSTATUS
  384. CFilterNode::DuplicateForCapture(
  385. )
  386. {
  387. PLOGICAL_FILTER_NODE pLogicalFilterNode;
  388. NTSTATUS Status = STATUS_SUCCESS;
  389. PFILTER_NODE pFilterNode = NULL;
  390. if(GetType() & FILTER_TYPE_DUP_FOR_CAPTURE) {
  391. FOR_EACH_LIST_ITEM(&lstLogicalFilterNode, pLogicalFilterNode) {
  392. if(!pLogicalFilterNode->IsRenderAndCapture()) {
  393. ASSERT(NT_SUCCESS(Status));
  394. goto exit;
  395. }
  396. } END_EACH_LIST_ITEM
  397. pFilterNode = new FILTER_NODE(GetType());
  398. if(pFilterNode == NULL) {
  399. Status = STATUS_INSUFFICIENT_RESOURCES;
  400. Trap();
  401. goto exit;
  402. }
  403. Status = pFilterNode->Create(GetDeviceInterface());
  404. if(!NT_SUCCESS(Status)) {
  405. goto exit;
  406. }
  407. SetRenderOnly();
  408. pFilterNode->SetCaptureOnly();
  409. }
  410. exit:
  411. if(!NT_SUCCESS(Status)) {
  412. Trap();
  413. delete pFilterNode;
  414. }
  415. return(Status);
  416. }
  417. BOOL
  418. CFilterNode::IsDeviceInterfaceMatch(
  419. PDEVICE_NODE pDeviceNode
  420. )
  421. {
  422. PWSTR pwstr, pwstrDeviceInterface;
  423. UNICODE_STRING String1, String2;
  424. Assert(this);
  425. if(lstwstrDeviceInterfaceMatch.IsLstEmpty()) {
  426. return(TRUE);
  427. }
  428. //
  429. // The +4 for both the strings is to eliminate the \\.\ differences in
  430. // user mode device interface names & kernel mode device interface names
  431. //
  432. pwstrDeviceInterface = pDeviceNode->GetDeviceInterface()+4;
  433. RtlInitUnicodeString(&String2, pwstrDeviceInterface);
  434. FOR_EACH_LIST_ITEM(&lstwstrDeviceInterfaceMatch, pwstr) {
  435. RtlInitUnicodeString(&String1, (pwstr+4));
  436. if (RtlEqualUnicodeString(&String1, &String2, TRUE)) {
  437. return(TRUE);
  438. }
  439. } END_EACH_LIST_ITEM
  440. return(FALSE);
  441. }
  442. VOID
  443. CFilterNode::SetType(
  444. ULONG fulType
  445. )
  446. {
  447. this->fulType |= fulType;
  448. //
  449. // This is because of left overs (type bridge) in the registry
  450. // that look like aliases.
  451. //
  452. if(this->fulType & FILTER_TYPE_TOPOLOGY) {
  453. this->fulType = (FILTER_TYPE_AUDIO | FILTER_TYPE_TOPOLOGY);
  454. }
  455. GetDefaultOrder(this->fulType, &ulOrder);
  456. }
  457. NTSTATUS
  458. CFilterNode::CreatePin(
  459. PKSPIN_CONNECT pPinConnect,
  460. ACCESS_MASK Access,
  461. PHANDLE pHandle
  462. )
  463. {
  464. NTSTATUS Status;
  465. ::KeAttachProcess(this->pProcess);
  466. Status = KsCreatePin(this->hFileHandle,
  467. pPinConnect,
  468. Access,
  469. pHandle);
  470. ::KeDetachProcess();
  471. return(Status);
  472. }
  473. BOOL
  474. CFilterNode::DoesGfxMatch(
  475. HANDLE hGfx,
  476. PWSTR pwstrDeviceName,
  477. ULONG GfxOrder
  478. )
  479. {
  480. ULONG DeviceCount;
  481. UNICODE_STRING usInDevice, usfnDevice;
  482. PWSTR pwstr;
  483. RtlInitUnicodeString(&usInDevice, (pwstrDeviceName+4));
  484. //
  485. // Skip if it is not a GFX
  486. //
  487. DPF1(90, "DoesGfxMatch:: fultype=%x", this->fulType);
  488. if (!(this->fulType & FILTER_TYPE_GFX)) {
  489. return(FALSE);
  490. }
  491. //
  492. // If it is a valid handle value, check whether the handle matches
  493. //
  494. if (hGfx) {
  495. if (this->hFileHandle != hGfx) {
  496. return(FALSE);
  497. }
  498. }
  499. //
  500. // Skip if the order does not match
  501. //
  502. DPF1(90, "DoesGfxMatch:: order=%x", this->ulOrder);
  503. if (GfxOrder != this->ulOrder) {
  504. return(FALSE);
  505. }
  506. //
  507. // Skip if the match device list is empty :: should not happen with GFX
  508. //
  509. if(lstwstrDeviceInterfaceMatch.IsLstEmpty()) {
  510. ASSERT(!"GFX with no device to attach to!\n");
  511. return(FALSE);
  512. }
  513. //
  514. // Check if any of the Match device strings matches the device interface
  515. // passed in. (In case of GFX we should have only one string though)
  516. //
  517. DeviceCount = 0;
  518. FOR_EACH_LIST_ITEM(&lstwstrDeviceInterfaceMatch, pwstr) {
  519. ASSERT(DeviceCount == 0);
  520. RtlInitUnicodeString(&usfnDevice, (pwstr+4));
  521. DPF1(95, "DoesGfxMatch:: new di = %s)", DbgUnicode2Sz(pwstrDeviceName));
  522. DPF1(95, "DoesGfxMatch:: old di = %s)", DbgUnicode2Sz(pwstr));
  523. if (RtlEqualUnicodeString(&usInDevice, &usfnDevice, TRUE)) {
  524. DPF1(90, "Found a duplicate GFX, pFilterNode = %x", this);
  525. return(TRUE);
  526. }
  527. DeviceCount++;
  528. } END_EACH_LIST_ITEM
  529. return (FALSE);
  530. }
  531. //---------------------------------------------------------------------------
  532. #ifdef DEBUG
  533. ULONG nFilter = 0;
  534. VOID
  535. DumpSysAudio(
  536. )
  537. {
  538. if(gplstFilterNode != NULL) {
  539. gplstFilterNode->Dump();
  540. }
  541. if(gplstDeviceNode != NULL) {
  542. gplstDeviceNode->Dump();
  543. }
  544. if(gplstLogicalFilterNode != NULL &&
  545. (ulDebugFlags & (DEBUG_FLAGS_FILTER |
  546. DEBUG_FLAGS_DEVICE |
  547. DEBUG_FLAGS_LOGICAL_FILTER)) ==
  548. DEBUG_FLAGS_LOGICAL_FILTER) {
  549. gplstLogicalFilterNode->Dump();
  550. }
  551. }
  552. ENUMFUNC
  553. CFilterNode::Dump(
  554. )
  555. {
  556. Assert(this);
  557. // .sf
  558. if(ulDebugFlags & (DEBUG_FLAGS_FILTER | DEBUG_FLAGS_OBJECT)) {
  559. if(ulDebugNumber == MAXULONG || ulDebugNumber == nFilter) {
  560. if(ulDebugFlags & DEBUG_FLAGS_ADDRESS) {
  561. dprintf("%d: %08x %s\n", nFilter, this, DumpName());
  562. }
  563. else {
  564. dprintf("%d: %s\n", nFilter, DumpName());
  565. }
  566. // .sfv
  567. if(ulDebugFlags & (DEBUG_FLAGS_VERBOSE | DEBUG_FLAGS_OBJECT)) {
  568. dprintf("FN: %08x DN %08x cPins %08x ulOrder %08x\n",
  569. this,
  570. pDeviceNode,
  571. cPins,
  572. ulOrder);
  573. dprintf(" fulType: %08x ", fulType);
  574. DumpfulType(fulType);
  575. dprintf("\n ulFlags: %08x ", ulFlags);
  576. if(ulFlags & FN_FLAGS_RENDER) {
  577. dprintf("RENDER ");
  578. }
  579. if(ulFlags & FN_FLAGS_NO_RENDER) {
  580. dprintf("NO_RENDER ");
  581. }
  582. if(ulFlags & FN_FLAGS_CAPTURE) {
  583. dprintf("CAPTURE ");
  584. }
  585. if(ulFlags & FN_FLAGS_NO_CAPTURE) {
  586. dprintf("NO_CAPTURE ");
  587. }
  588. dprintf("\n");
  589. if(pwstrDeviceInterface != NULL) {
  590. dprintf(" %s\n", DumpDeviceInterface());
  591. }
  592. PWSTR pwstr;
  593. FOR_EACH_LIST_ITEM(&lstwstrDeviceInterfaceMatch, pwstr) {
  594. dprintf(" pwstrDevice:\n %s\n", DbgUnicode2Sz(pwstr));
  595. } END_EACH_LIST_ITEM
  596. dprintf(" lstLFN: ");
  597. lstLogicalFilterNode.DumpAddress();
  598. dprintf("\n lstConnFN:");
  599. lstConnectedFilterNode.DumpAddress();
  600. dprintf("\n");
  601. }
  602. // .sfp
  603. if(ulDebugFlags & DEBUG_FLAGS_PIN) {
  604. lstPinInfo.Dump();
  605. }
  606. // .sft
  607. if(ulDebugFlags & DEBUG_FLAGS_TOPOLOGY) {
  608. lstTopologyNode.Dump();
  609. // .sftx
  610. if(ulDebugFlags & DEBUG_FLAGS_DETAILS) {
  611. lstTopologyConnection.Dump();
  612. }
  613. }
  614. // .sfl
  615. if(ulDebugFlags & DEBUG_FLAGS_LOGICAL_FILTER) {
  616. lstLogicalFilterNode.Dump();
  617. }
  618. if(ulDebugFlags &
  619. (DEBUG_FLAGS_VERBOSE | DEBUG_FLAGS_PIN | DEBUG_FLAGS_TOPOLOGY)) {
  620. dprintf("\n");
  621. }
  622. }
  623. nFilter++;
  624. }
  625. return(STATUS_CONTINUE);
  626. }
  627. #endif