[DEVMGR]
[reactos.git] / reactos / dll / win32 / devmgr / devmgmt / DeviceView.cpp
1 /*
2 * PROJECT: ReactOS Device Manager
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/devmgr/devmgr/DeviceView.cpp
5 * PURPOSE: Implements the tree view which contains the devices
6 * COPYRIGHT: Copyright 2015 Ged Murphy <gedmurphy@reactos.org>
7 *
8 */
9
10
11 #include "stdafx.h"
12 #include "devmgmt.h"
13 #include "DeviceView.h"
14
15
16 /* DATA *********************************************/
17
18 #define CLASS_NAME_LEN 256
19 #define CLASS_DESC_LEN 256
20
21 INT_PTR
22 WINAPI
23 DevicePropertiesExW(
24 IN HWND hWndParent OPTIONAL,
25 IN LPCWSTR lpMachineName OPTIONAL,
26 IN LPCWSTR lpDeviceID OPTIONAL,
27 IN DWORD dwFlags OPTIONAL,
28 IN BOOL bShowDevMgr
29 );
30
31 typedef INT_PTR(WINAPI *pDevicePropertiesExW)(HWND,LPCWSTR,LPCWSTR,DWORD,BOOL);
32
33 struct RefreshThreadData
34 {
35 CDeviceView *This;
36 BOOL ScanForChanges;
37 BOOL UpdateView;
38 };
39
40
41 /* PUBLIC METHODS *************************************/
42
43 CDeviceView::CDeviceView(
44 HWND hMainWnd
45 ) :
46 m_hMainWnd(hMainWnd),
47 m_hTreeView(NULL),
48 m_hPropertyDialog(NULL),
49 m_hShortcutMenu(NULL),
50 m_ViewType(DevicesByType),
51 m_ShowHidden(FALSE),
52 m_RootClassImage(-1),
53 m_RootDevInst(0)
54 {
55 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
56 }
57
58 CDeviceView::~CDeviceView(void)
59 {
60 }
61
62 bool
63 CDeviceView::Initialize()
64 {
65 // Get the device image list
66 m_ImageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA);
67 BOOL bSuccess = SetupDiGetClassImageList(&m_ImageListData);
68 if (bSuccess == FALSE) return false;
69
70 // Create the main treeview
71 m_hTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
72 WC_TREEVIEW,
73 NULL,
74 WS_CHILD | WS_VISIBLE | WS_BORDER | TVS_HASLINES |
75 TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_LINESATROOT,
76 0, 0, 0, 0,
77 m_hMainWnd,
78 (HMENU)IDC_TREEVIEW,
79 g_hInstance,
80 NULL);
81 if (m_hTreeView)
82 {
83 // Set the image list against the treeview
84 (void)TreeView_SetImageList(m_hTreeView,
85 m_ImageListData.ImageList,
86 TVSIL_NORMAL);
87
88 // Give the treeview arrows instead of +/- boxes (on Win7)
89 SetWindowTheme(m_hTreeView, L"explorer", NULL);
90 }
91
92 return !!(m_hTreeView);
93 }
94
95 bool
96 CDeviceView::Uninitialize()
97 {
98 EmptyDeviceView();
99
100 if (m_ImageListData.ImageList != NULL)
101 {
102 SetupDiDestroyClassImageList(&m_ImageListData);
103 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
104 }
105
106 return true;
107 }
108
109 void
110 CDeviceView::Size(
111 _In_ int x,
112 _In_ int y,
113 _In_ int cx,
114 _In_ int cy
115 )
116 {
117 // Resize the treeview
118 SetWindowPos(m_hTreeView,
119 NULL,
120 x,
121 y,
122 cx,
123 cy,
124 SWP_NOZORDER);
125 }
126
127 void
128 CDeviceView::Refresh(_In_ ViewType Type,
129 _In_ bool ScanForChanges,
130 _In_ bool UpdateView)
131 {
132 // Enum devices on a seperate thread to keep the gui responsive
133
134 m_ViewType = Type;
135
136 RefreshThreadData *ThreadData;
137 ThreadData = new RefreshThreadData();
138 ThreadData->This = this;
139 ThreadData->ScanForChanges = ScanForChanges;
140 ThreadData->UpdateView = UpdateView;
141
142 HANDLE hThread;
143 hThread = (HANDLE)_beginthreadex(NULL,
144 0,
145 &RefreshThread,
146 ThreadData,
147 0,
148 NULL);
149
150 if (hThread) CloseHandle(hThread);
151 }
152
153 void
154 CDeviceView::DisplayPropertySheet()
155 {
156 //
157 // In ReactOS we can link to DevicePropertiesEx but
158 // not in windows as it's not part of the SDK
159
160 #ifndef __REACTOS__
161 HMODULE hModule = LoadLibraryW(L"devmgr.dll");
162 if (hModule == NULL) return;
163
164 pDevicePropertiesExW DevicePropertiesExW;
165 DevicePropertiesExW = (pDevicePropertiesExW)GetProcAddress(hModule,
166 "DevicePropertiesExW");
167 if (DevicePropertiesExW == NULL)
168 {
169 FreeLibrary(hModule);
170 return;
171 }
172 #endif
173
174 CNode *Node = GetSelectedNode();
175 if (Node && Node->HasProperties())
176 {
177 DevicePropertiesExW(m_hTreeView,
178 NULL,
179 Node->GetDeviceId(),
180 1,//DPF_EXTENDED,
181 FALSE);
182 }
183
184 #ifndef __REACTOS__
185 FreeLibrary(hModule);
186 #endif
187 }
188
189 void
190 CDeviceView::SetFocus()
191 {
192 }
193
194 bool
195 CDeviceView::HasProperties(_In_ LPTV_ITEMW TvItem)
196 {
197 CNode *Node = GetNode(TvItem);
198 if (Node)
199 {
200 return Node->HasProperties();
201 }
202 return false;
203 }
204
205 bool
206 CDeviceView::IsDisabled(_In_ LPTV_ITEMW TvItem)
207 {
208 CNode *Node = GetNode(TvItem);
209 if (Node)
210 {
211 return Node->IsDisabled();
212 }
213 return false;
214 }
215
216 bool
217 CDeviceView::CanDisable(_In_ LPTV_ITEMW TvItem)
218 {
219 CNode *Node = GetNode(TvItem);
220 if (Node)
221 {
222 return Node->CanDisable();
223 }
224 return false;
225 }
226
227
228 /* PRIVATE METHODS ********************************************/
229
230 bool
231 CDeviceView::AddRootDevice()
232 {
233 // Check whether we've loaded the root bitmap into the imagelist (done on first run)
234 if (m_RootClassImage == -1)
235 {
236 // Load the bitmap we'll be using as the root image
237 HBITMAP hRootImage;
238 hRootImage = LoadBitmapW(g_hInstance,
239 MAKEINTRESOURCEW(IDB_ROOT_IMAGE));
240 if (hRootImage == NULL) return FALSE;
241
242 // Add this bitmap to the device image list. This is a bit hacky, but it's safe
243 m_RootClassImage = ImageList_Add(m_ImageListData.ImageList,
244 hRootImage,
245 NULL);
246 DeleteObject(hRootImage);
247 }
248
249 /* Get the root instance */
250 CONFIGRET cr;
251 cr = CM_Locate_DevNodeW(&m_RootDevInst,
252 NULL,
253 CM_LOCATE_DEVNODE_NORMAL);
254 if (cr != CR_SUCCESS)
255 {
256 return false;
257 }
258
259 /* The root name is the computer name */
260 WCHAR RootDeviceName[ROOT_NAME_SIZE];
261 DWORD Size = ROOT_NAME_SIZE;
262 if (GetComputerNameW(RootDeviceName, &Size))
263 _wcslwr_s(RootDeviceName);
264
265 TV_ITEMW tvi;
266 TV_INSERTSTRUCT tvins;
267 ZeroMemory(&tvi, sizeof(tvi));
268 ZeroMemory(&tvins, sizeof(tvins));
269
270 // Insert the root / parent item into our treeview
271 tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
272 tvi.pszText = RootDeviceName;
273 tvi.cchTextMax = wcslen(RootDeviceName);
274 tvi.iImage = m_RootClassImage;
275 tvi.iSelectedImage = m_RootClassImage;
276 tvins.item = tvi;
277 m_hTreeRoot = TreeView_InsertItem(m_hTreeView, &tvins);
278
279 return (m_hTreeRoot != NULL);
280
281 }
282
283 bool
284 CDeviceView::GetNextClass(_In_ ULONG ClassIndex,
285 _Out_ LPGUID ClassGuid,
286 _Out_ HDEVINFO *hDevInfo)
287 {
288 CONFIGRET cr;
289
290 // Get the next class in the list
291 cr = CM_Enumerate_Classes(ClassIndex,
292 ClassGuid,
293 0);
294 if (cr != CR_SUCCESS) return false;
295
296 // Check for devices without a class
297 if (IsEqualGUID(*ClassGuid, GUID_DEVCLASS_UNKNOWN))
298 {
299 // Get device info for all devices for all classes
300 *hDevInfo = SetupDiGetClassDevsW(NULL,
301 NULL,
302 NULL,
303 DIGCF_ALLCLASSES);
304 }
305 else
306 {
307 // We only want the devices for this class
308 *hDevInfo = SetupDiGetClassDevsW(ClassGuid,
309 NULL,
310 NULL,
311 DIGCF_PRESENT);
312
313 }
314
315 return (hDevInfo != INVALID_HANDLE_VALUE);
316 }
317
318 unsigned int __stdcall CDeviceView::RefreshThread(void *Param)
319 {
320 RefreshThreadData *ThreadData = (RefreshThreadData *)Param;
321 CDeviceView *This = ThreadData->This;
322
323
324 // Empty the treeview
325 This->EmptyDeviceView();
326 This->m_hTreeRoot = NULL;
327
328 // Refresh the devices only if requested. This means
329 // switching views uses the cache and remains fast
330 if (ThreadData->ScanForChanges)
331 {
332 This->RefreshDeviceList();
333 }
334
335 // display the type of view the user wants
336 switch (This->m_ViewType)
337 {
338 case DevicesByType:
339 (void)This->ListDevicesByType();
340 break;
341
342 case DevicesByConnection:
343 (VOID)This->ListDevicesByConnection();
344 break;
345
346 case ResourcesByType:
347 break;
348
349 case ResourcesByConnection:
350 break;
351 }
352
353 delete ThreadData;
354
355 return 0;
356 }
357
358
359 bool
360 CDeviceView::ListDevicesByType()
361 {
362 CNode *ClassNode, *DeviceNode;
363 HDEVINFO hDevInfo;
364 HTREEITEM hTreeItem = NULL;
365 GUID ClassGuid;
366 INT ClassIndex;
367 LPTSTR DeviceId = NULL;
368 BOOL bClassSuccess, bSuccess;
369
370 // Start by adding the root node to the tree
371 bSuccess = AddRootDevice();
372 if (bSuccess == false) return false;
373
374 ClassIndex = 0;
375 do
376 {
377 // Loop through all the device classes
378 bClassSuccess = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
379 if (bClassSuccess)
380 {
381 bool bClassUnknown = false;
382 bool AddedParent = false;
383 INT DeviceIndex = 0;
384 BOOL MoreItems;
385
386 // Get the cached class node
387 ClassNode = GetClassNode(&ClassGuid);
388 if (ClassNode == NULL)
389 {
390 ATLASSERT(FALSE);
391 ClassIndex++;
392 continue;
393 }
394
395 // Set a flag is this is the (special case) unknown class
396 if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_UNKNOWN))
397 bClassUnknown = true;
398
399 do
400 {
401 // Get a handle to all the devices in this class
402 SP_DEVINFO_DATA DeviceInfoData;
403 ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
404 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
405 bSuccess = SetupDiEnumDeviceInfo(hDevInfo,
406 DeviceIndex,
407 &DeviceInfoData);
408 if (bSuccess == FALSE && GetLastError() == ERROR_NO_MORE_ITEMS)
409 MoreItems = FALSE;
410
411 if (bSuccess)
412 {
413 MoreItems = TRUE;
414
415 // The unknown class handle contains all devices on the system,
416 // and we're just looking for the ones with a null GUID
417 if (bClassUnknown)
418 {
419 if (IsEqualGUID(DeviceInfoData.ClassGuid, GUID_NULL) == FALSE)
420 {
421 // This is a known device, we aren't interested in it
422 DeviceIndex++;
423 continue;
424 }
425 }
426
427 // Get the cached device node
428 DeviceNode = GetDeviceNode(DeviceInfoData.DevInst);
429 if (DeviceNode == NULL)
430 {
431 ATLASSERT(bClassUnknown == true);
432 DeviceIndex++;
433 continue;
434 }
435
436 // Check if this is a hidden device
437 if (DeviceNode->IsHidden())
438 {
439 // Ignore this device if we aren't displaying hidden devices
440 if (m_ShowHidden == FALSE)
441 {
442 DeviceIndex++;
443 continue;
444 }
445 }
446
447 // We have a device, we need to add the parent if it hasn't yet been added
448 if (AddedParent == false)
449 {
450 // Insert the new class under the root item
451 hTreeItem = InsertIntoTreeView(m_hTreeRoot,
452 ClassNode);
453 AddedParent = true;
454 }
455
456 // Add the device under the class item node
457 (void)InsertIntoTreeView(hTreeItem, DeviceNode);
458
459 // Expand the class if it has a problem device
460 if (DeviceNode->HasProblem())
461 {
462 (void)TreeView_Expand(m_hTreeView,
463 hTreeItem,
464 TVE_EXPAND);
465 }
466 }
467
468 DeviceIndex++;
469
470 } while (MoreItems);
471
472 // If this class has devices, sort them alphabetically
473 if (AddedParent == true)
474 {
475 (void)TreeView_SortChildren(m_hTreeView,
476 hTreeItem,
477 0);
478 }
479 }
480
481 ClassIndex++;
482
483 } while (bClassSuccess);
484
485 // Sort the classes alphabetically
486 (void)TreeView_SortChildren(m_hTreeView,
487 m_hTreeRoot,
488 0);
489
490 // Expand the root item
491 (void)TreeView_Expand(m_hTreeView,
492 m_hTreeRoot,
493 TVE_EXPAND);
494
495 // Pre-select the root item
496 (VOID)TreeView_SelectItem(m_hTreeView,
497 m_hTreeRoot);
498
499 return 0;
500 }
501
502 bool
503 CDeviceView::ListDevicesByConnection()
504 {
505 BOOL bSuccess;
506
507 // Start by adding the root node to the tree
508 bSuccess = AddRootDevice();
509 if (bSuccess == false) return false;
510
511 /* Walk the device tree and add all the devices */
512 RecurseChildDevices(m_RootDevInst, m_hTreeRoot);
513
514 /* Expand the root item */
515 (VOID)TreeView_Expand(m_hTreeView,
516 m_hTreeRoot,
517 TVE_EXPAND);
518
519 return true;
520 }
521
522 VOID
523 CDeviceView::RecurseChildDevices(
524 _In_ DEVINST ParentDevice,
525 _In_ HTREEITEM hParentTreeItem
526 )
527 {
528
529 HTREEITEM hDevItem = NULL;
530 DEVINST Device;
531 BOOL bSuccess;
532
533 /* Check if the parent has any child devices */
534 if (GetChildDevice(ParentDevice, &Device) == FALSE)
535 return;
536
537 // Get the cached device node
538 CNode *DeviceNode;
539 DeviceNode = GetDeviceNode(Device);
540 if (DeviceNode == NULL)
541 {
542 ATLASSERT(FALSE);
543 return;
544 }
545
546
547 /* Check if this is a hidden device */
548 if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
549 {
550 /* Add this device to the tree under its parent */
551 hDevItem = InsertIntoTreeView(hParentTreeItem,
552 DeviceNode);
553
554
555 if (hDevItem)
556 {
557 /* Check if this child has any children itself */
558 RecurseChildDevices(Device, hDevItem);
559 }
560 }
561
562
563 for (;;)
564 {
565 /* Check if the parent device has anything at the same level */
566 bSuccess = GetSiblingDevice(Device, &Device);
567 if (bSuccess == FALSE) break;
568
569 DeviceNode = GetDeviceNode(Device);
570 if (DeviceNode == NULL)
571 {
572 ATLASSERT(FALSE);
573 }
574
575 /* Check if this is a hidden device */
576 if (DeviceNode->IsHidden())
577 {
578 if (m_ShowHidden == FALSE)
579 continue;
580 }
581
582 /* Add this device to the tree under its parent */
583 hDevItem = InsertIntoTreeView(hParentTreeItem,
584 DeviceNode);
585 if (hDevItem)
586 {
587 /* Check if this child has any children itself */
588 RecurseChildDevices(Device, hDevItem);
589 }
590 }
591
592 (void)TreeView_SortChildren(m_hTreeView,
593 hParentTreeItem,
594 0);
595
596 }
597
598 bool
599 CDeviceView::GetChildDevice(
600 _In_ DEVINST ParentDevInst,
601 _Out_ PDEVINST DevInst
602 )
603 {
604 CONFIGRET cr;
605 cr = CM_Get_Child(DevInst,
606 ParentDevInst,
607 0);
608 return (cr == CR_SUCCESS);
609 }
610
611 bool
612 CDeviceView::GetSiblingDevice(
613 _In_ DEVINST PrevDevice,
614 _Out_ PDEVINST DevInst
615 )
616 {
617 CONFIGRET cr;
618 cr = CM_Get_Sibling(DevInst,
619 PrevDevice,
620 0);
621 return (cr == CR_SUCCESS);
622 }
623
624 HTREEITEM
625 CDeviceView::InsertIntoTreeView(
626 _In_ HTREEITEM hParent,
627 _In_ CNode *Node
628 )
629 {
630
631 LPWSTR lpLabel;
632 lpLabel = Node->GetDisplayName();
633
634 TV_ITEMW tvi;
635 TV_INSERTSTRUCT tvins;
636 ZeroMemory(&tvi, sizeof(tvi));
637 ZeroMemory(&tvins, sizeof(tvins));
638
639 tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
640 tvi.pszText = lpLabel;
641 tvi.cchTextMax = wcslen(lpLabel);
642 tvi.lParam = (LPARAM)Node;
643 tvi.iImage = Node->GetClassImage();
644 tvi.iSelectedImage = Node->GetClassImage();
645
646 if (Node->GetOverlayImage())
647 {
648 tvi.mask |= TVIF_STATE;
649 tvi.stateMask = TVIS_OVERLAYMASK;
650 tvi.state = INDEXTOOVERLAYMASK(Node->GetOverlayImage());
651 }
652
653 tvins.item = tvi;
654 tvins.hParent = hParent;
655
656 return TreeView_InsertItem(m_hTreeView, &tvins);
657 }
658
659 void
660 CDeviceView::RecurseDeviceView(
661 _In_ HTREEITEM hParentItem
662 )
663 {
664 HTREEITEM hItem;
665 TVITEMW tvItem;
666
667 // Check if this node has any children
668 hItem = TreeView_GetChild(m_hTreeView, hParentItem);
669 if (hItem == NULL) return;
670
671 // The lParam contains the node pointer data
672 tvItem.hItem = hItem;
673 tvItem.mask = TVIF_PARAM;
674 if (TreeView_GetItem(m_hTreeView, &tvItem) &&
675 tvItem.lParam != NULL)
676 {
677 // Delete the node class
678 //delete reinterpret_cast<CNode *>(tvItem.lParam);
679 }
680
681 // This node may have its own children
682 RecurseDeviceView(hItem);
683
684 // Delete all the siblings
685 for (;;)
686 {
687 // Get the next item at this level
688 hItem = TreeView_GetNextSibling(m_hTreeView, hItem);
689 if (hItem == NULL) break;
690
691 // The lParam contains the node pointer data
692 tvItem.hItem = hItem;
693 tvItem.mask = TVIF_PARAM;
694 if (TreeView_GetItem(m_hTreeView, &tvItem))
695 {
696 //if (tvItem.lParam != NULL)
697 // delete reinterpret_cast<CNode *>(tvItem.lParam);
698 }
699
700 /* This node may have its own children */
701 RecurseDeviceView(hItem);
702 }
703 }
704
705
706 void
707 CDeviceView::EmptyDeviceView()
708 {
709 HTREEITEM hItem;
710
711 // Check if there are any items in the tree
712 hItem = TreeView_GetRoot(m_hTreeView);
713 if (hItem == NULL) return;
714
715 // Free all the class nodes
716 //RecurseDeviceView(hItem);
717
718 // Delete all the items
719 (VOID)TreeView_DeleteAllItems(m_hTreeView);
720 }
721
722
723
724
725 CNode*
726 CDeviceView::GetClassNode(_In_ LPGUID ClassGuid)
727 {
728 POSITION Pos;
729 CNode *Node;
730
731 Pos = m_ClassNodeList.GetHeadPosition();
732
733 do
734 {
735 Node = m_ClassNodeList.GetNext(Pos);
736 if (IsEqualGUID(*Node->GetClassGuid(), *ClassGuid))
737 {
738 //ATLASSERT(Node->GetType() == NodeClass);
739 break;
740 }
741
742 Node = NULL;
743
744 } while (Pos != NULL);
745
746 return Node;
747 }
748
749 CNode*
750 CDeviceView::GetDeviceNode(_In_ DEVINST Device)
751 {
752 POSITION Pos;
753 CNode *Node;
754
755 Pos = m_DeviceNodeList.GetHeadPosition();
756
757 do
758 {
759 Node = m_DeviceNodeList.GetNext(Pos);
760 if (Node->GetDeviceInst() == Device)
761 {
762 //ATLASSERT(Node->GetType() == NodeDevice);
763 break;
764 }
765
766 Node = NULL;
767
768 } while (Pos != NULL);
769
770 return Node;
771 }
772
773 CNode* CDeviceView::GetNode(LPTV_ITEMW TvItem)
774 {
775 TvItem->mask = TVIF_PARAM;
776 if (TreeView_GetItem(m_hTreeView, TvItem))
777 {
778 return (CNode *)TvItem->lParam;
779 }
780 }
781
782 CNode* CDeviceView::GetSelectedNode()
783 {
784 TV_ITEM TvItem;
785 TvItem.hItem = TreeView_GetSelection(m_hTreeView);
786 return GetNode(&TvItem);
787 }
788
789 void
790 CDeviceView::EmptyLists()
791 {
792 POSITION Pos;
793 CNode *Node;
794
795 if (!m_ClassNodeList.IsEmpty())
796 {
797 Pos = m_ClassNodeList.GetHeadPosition();
798 do
799 {
800 Node = m_ClassNodeList.GetNext(Pos);
801 delete Node;
802
803 } while (Pos != NULL);
804 }
805
806 if (!m_DeviceNodeList.IsEmpty())
807 {
808 Pos = m_DeviceNodeList.GetHeadPosition();
809 do
810 {
811 Node = m_DeviceNodeList.GetNext(Pos);
812 delete Node;
813
814 } while (Pos != NULL);
815 }
816 }
817
818 bool
819 CDeviceView::RefreshDeviceList()
820 {
821 GUID ClassGuid;
822 CNode *Node;
823 HDEVINFO hDevInfo;
824 SP_DEVINFO_DATA DeviceInfoData;
825 DWORD i;
826 BOOL Success;
827
828 ULONG ClassIndex = 0;
829
830 EmptyLists();
831
832 do
833 {
834 Success = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
835 if (Success)
836 {
837 /* Create a new class node */
838 Node = new CNode(&ClassGuid, &m_ImageListData);
839 if (Node->Setup())
840 {
841 m_ClassNodeList.AddTail(Node);
842 }
843 }
844 ClassIndex++;
845 } while (Success);
846
847
848 hDevInfo = SetupDiGetClassDevsW(NULL,
849 0,
850 0,
851 DIGCF_PRESENT | DIGCF_ALLCLASSES);
852 if (hDevInfo == INVALID_HANDLE_VALUE)
853 {
854 return false;
855 }
856
857
858 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
859 for (i = 0;; i++)
860 {
861 Success = SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData);
862 if (Success == FALSE) break;
863
864
865 Node = new CNode(DeviceInfoData.DevInst, &m_ImageListData);
866 Node->Setup();
867 m_DeviceNodeList.AddTail(Node);
868 }
869
870 SetupDiDestroyDeviceInfoList(hDevInfo);
871
872 return TRUE;
873 }