1 /*
2  * PROJECT:     ReactOS Device Manager
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/win32/devmgr/devmgmt/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 "precomp.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 #define ROOT_NAME_SIZE      MAX_COMPUTERNAME_LENGTH + 1
21 
22 
23 typedef VOID(WINAPI *PADDHARDWAREWIZARD)(HWND hwnd, LPWSTR lpName);
24 
25 struct RefreshThreadData
26 {
27     CDeviceView *This;
28     BOOL ScanForChanges;
29     BOOL UpdateView;
30 };
31 
32 
33 // PUBLIC METHODS ************************************/
34 
35 CDeviceView::CDeviceView(
36     HWND hMainWnd
37     ) :
38     m_hMainWnd(hMainWnd),
39     m_hTreeView(NULL),
40     m_hPropertyDialog(NULL),
41     m_hMenu(NULL),
42     m_ViewType(DevicesByType),
43     m_ShowHidden(false),
44     m_RootNode(NULL)
45 {
46     ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
47 }
48 
49 CDeviceView::~CDeviceView(void)
50 {
51 }
52 
53 bool
54 CDeviceView::Initialize()
55 {
56     // Get the device image list
57     m_ImageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA);
58     BOOL bSuccess = SetupDiGetClassImageList(&m_ImageListData);
59     if (bSuccess == FALSE)
60         return false;
61 
62     // Create the main treeview
63     m_hTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
64                                   WC_TREEVIEW,
65                                   NULL,
66                                   WS_CHILD | WS_VISIBLE | WS_BORDER | TVS_HASLINES |
67                                   TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_LINESATROOT,
68                                   0, 0, 0, 0,
69                                   m_hMainWnd,
70                                   (HMENU)IDC_TREEVIEW,
71                                   g_hThisInstance,
72                                   NULL);
73     if (m_hTreeView)
74     {
75         // Set the image list against the treeview
76         (void)TreeView_SetImageList(m_hTreeView,
77                                     m_ImageListData.ImageList,
78                                     TVSIL_NORMAL);
79 
80         // Give the treeview arrows instead of +/- boxes (on Win7)
81         SetWindowTheme(m_hTreeView, L"explorer", NULL);
82 
83         // Create the root node
84         m_RootNode = new CRootNode(&m_ImageListData);
85         m_RootNode->SetupNode();
86     }
87 
88 
89 
90     return !!(m_hTreeView);
91 }
92 
93 bool
94 CDeviceView::Uninitialize()
95 {
96     EmptyDeviceView();
97 
98     if (m_ImageListData.ImageList != NULL)
99     {
100         SetupDiDestroyClassImageList(&m_ImageListData);
101         ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
102     }
103 
104     return true;
105 }
106 
107 LRESULT
108 CDeviceView::OnSize(
109     _In_ int x,
110     _In_ int y,
111     _In_ int cx,
112     _In_ int cy
113     )
114 {
115     // Resize the treeview
116     SetWindowPos(m_hTreeView,
117                  NULL,
118                  x,
119                  y,
120                  cx,
121                  cy,
122                  SWP_NOZORDER);
123 
124     return 0;
125 }
126 
127 LRESULT
128 CDeviceView::OnRightClick(
129     _In_ LPNMHDR NmHdr
130     )
131 {
132     TVHITTESTINFO hitInfo;
133     HTREEITEM hItem;
134 
135     GetCursorPos(&hitInfo.pt);
136     ScreenToClient(m_hTreeView, &hitInfo.pt);
137 
138     hItem = TreeView_HitTest(m_hTreeView, &hitInfo);
139     if (hItem != NULL && (hitInfo.flags & (TVHT_ONITEM | TVHT_ONITEMICON)))
140     {
141         TreeView_SelectItem(m_hTreeView, hItem);
142     }
143 
144     return 0;
145 }
146 
147 LRESULT
148 CDeviceView::OnContextMenu(
149     _In_ LPARAM lParam
150     )
151 {
152     HTREEITEM hSelected = TreeView_GetSelection(m_hTreeView);
153 
154     RECT rc;
155     if (TreeView_GetItemRect(m_hTreeView,
156                              hSelected,
157                              &rc,
158                              TRUE))
159     {
160         POINT pt;
161         if (GetCursorPos(&pt) &&
162             ScreenToClient(m_hTreeView, &pt) &&
163             PtInRect(&rc, pt))
164         {
165             CNode *Node = GetSelectedNode();
166             if (Node)
167             {
168                 // Create the context menu
169                 HMENU hContextMenu = CreatePopupMenu();
170 
171                 // Add the actions for this node
172                 BuildActionMenuForNode(hContextMenu, Node, false);
173 
174                 INT xPos = GET_X_LPARAM(lParam);
175                 INT yPos = GET_Y_LPARAM(lParam);
176 
177                 // Display the menu
178                 TrackPopupMenuEx(hContextMenu,
179                                  TPM_RIGHTBUTTON,
180                                  xPos,
181                                  yPos,
182                                  m_hMainWnd,
183                                  NULL);
184 
185                 DestroyMenu(hContextMenu);
186             }
187         }
188     }
189 
190     return 0;
191 }
192 
193 
194 void
195 CDeviceView::Refresh(
196     _In_ ViewType Type,
197     _In_ bool ScanForChanges,
198     _In_ bool UpdateView
199     )
200 {
201     // Enum devices on a separate thread to keep the gui responsive
202 
203     m_ViewType = Type;
204 
205     RefreshThreadData *ThreadData;
206     ThreadData = new RefreshThreadData;
207     ThreadData->This = this;
208     ThreadData->ScanForChanges = ScanForChanges;
209     ThreadData->UpdateView = UpdateView;
210 
211     HANDLE hThread;
212     hThread = (HANDLE)_beginthreadex(NULL,
213                                      0,
214                                      RefreshThread,
215                                      ThreadData,
216                                      0,
217                                      NULL);
218     if (hThread) CloseHandle(hThread);
219 }
220 
221 LRESULT
222 CDeviceView::OnAction(
223     _In_ UINT Action
224 )
225 {
226     switch (Action)
227     {
228         case IDM_PROPERTIES:
229         {
230             DisplayPropertySheet();
231             break;
232         }
233 
234         case IDM_SCAN_HARDWARE:
235         {
236             Refresh(GetCurrentView(),
237                     true,
238                     true);
239             break;
240         }
241 
242         case IDM_ENABLE_DRV:
243         {
244             bool NeedsReboot;
245             if (EnableSelectedDevice(true, NeedsReboot) &&
246                 NeedsReboot)
247             {
248                 MessageBox(m_hMainWnd, L"Rebooting", L"Enable", MB_OK);
249             }
250             break;
251         }
252 
253         case IDM_DISABLE_DRV:
254         {
255             bool NeedsReboot;
256             EnableSelectedDevice(false, NeedsReboot);
257             break;
258         }
259 
260         case IDM_UPDATE_DRV:
261         {
262             bool NeedsReboot;
263             UpdateSelectedDevice(NeedsReboot);
264             break;
265         }
266 
267         case IDM_UNINSTALL_DRV:
268         {
269             UninstallSelectedDevice();
270             break;
271         }
272 
273         case IDM_ADD_HARDWARE:
274         {
275             RunAddHardwareWizard();
276             break;
277         }
278     }
279 
280     return 0;
281 }
282 
283 void
284 CDeviceView::DisplayPropertySheet()
285 {
286     CNode *Node = GetSelectedNode();
287     if (Node && Node->HasProperties())
288     {
289         DevicePropertiesExW(m_hTreeView,
290                             NULL,
291                             Node->GetDeviceId(),
292                             1,//DPF_EXTENDED,
293                             FALSE);
294     }
295 }
296 
297 void
298 CDeviceView::SetFocus()
299 {
300     ::SetFocus(m_hTreeView);
301 }
302 
303 bool
304 CDeviceView::CreateActionMenu(
305     _In_ HMENU OwnerMenu,
306     _In_ bool MainMenu
307     )
308 {
309     CNode *Node = GetSelectedNode();
310     if (Node)
311     {
312         BuildActionMenuForNode(OwnerMenu, Node, MainMenu);
313         return true;
314     }
315 
316     return false;
317 }
318 
319 CNode*
320 CDeviceView::GetSelectedNode()
321 {
322     TV_ITEM TvItem;
323     TvItem.hItem = TreeView_GetSelection(m_hTreeView);
324     return GetNode(&TvItem);
325 }
326 
327 
328 
329 // PRIVATE METHODS *******************************************/
330 
331 bool
332 CDeviceView::AddRootDevice()
333 {
334     m_hTreeRoot = InsertIntoTreeView(NULL, m_RootNode);
335     return (m_hTreeRoot != NULL);
336 }
337 
338 bool
339 CDeviceView::GetNextClass(
340     _In_ ULONG ClassIndex,
341     _Out_ LPGUID ClassGuid,
342     _Out_ HDEVINFO *hDevInfo
343     )
344 {
345     CONFIGRET cr;
346 
347     // Get the next class in the list
348     cr = CM_Enumerate_Classes(ClassIndex,
349                               ClassGuid,
350                               0);
351     if (cr != CR_SUCCESS)
352         return false;
353 
354     // Check if this is the unknown class
355     if (IsEqualGUID(*ClassGuid, GUID_DEVCLASS_UNKNOWN))
356     {
357         // Get device info for all devices
358         *hDevInfo = SetupDiGetClassDevsW(NULL,
359                                          NULL,
360                                          NULL,
361                                          DIGCF_ALLCLASSES);
362     }
363     else
364     {
365         // We only want the devices for this class
366         *hDevInfo = SetupDiGetClassDevsW(ClassGuid,
367                                          NULL,
368                                          NULL,
369                                          DIGCF_PRESENT);
370     }
371 
372     return (hDevInfo != INVALID_HANDLE_VALUE);
373 }
374 
375 unsigned int __stdcall CDeviceView::RefreshThread(void *Param)
376 {
377     RefreshThreadData *ThreadData = (RefreshThreadData *)Param;
378     CDeviceView *This = ThreadData->This;
379 
380     // Get a copy of the currently selected node
381     CNode *LastSelectedNode = This->GetSelectedNode();
382     if (LastSelectedNode == nullptr || (LastSelectedNode->GetNodeType() == RootNode))
383     {
384         LastSelectedNode = new CRootNode(*This->m_RootNode);
385     }
386     else if (LastSelectedNode->GetNodeType() == ClassNode)
387     {
388         LastSelectedNode = new CClassNode(*dynamic_cast<CClassNode *>(LastSelectedNode));
389     }
390     else if (LastSelectedNode->GetNodeType() == DeviceNode)
391     {
392         LastSelectedNode = new CDeviceNode(*dynamic_cast<CDeviceNode *>(LastSelectedNode));
393     }
394 
395     // Empty the treeview
396     This->EmptyDeviceView();
397 
398     // Re-add the root node to the tree
399     if (This->AddRootDevice() == false)
400         return 0;
401 
402     // Refresh the devices only if requested
403     if (ThreadData->ScanForChanges)
404     {
405         This->RefreshDeviceList();
406     }
407 
408     // display the type of view the user wants
409     switch (This->m_ViewType)
410     {
411         case DevicesByType:
412             (void)This->ListDevicesByType();
413             break;
414 
415         case DevicesByConnection:
416             (VOID)This->ListDevicesByConnection();
417             break;
418 
419         case ResourcesByType:
420             break;
421 
422         case ResourcesByConnection:
423             break;
424     }
425 
426     This->SelectNode(LastSelectedNode);
427 
428     delete ThreadData;
429 
430     return 0;
431 }
432 
433 
434 bool
435 CDeviceView::ListDevicesByType()
436 {
437     CClassNode *ClassNode;
438     CDeviceNode *DeviceNode;
439     HDEVINFO hDevInfo;
440     HTREEITEM hTreeItem = NULL;
441     GUID ClassGuid;
442     INT ClassIndex;
443     BOOL bClassSuccess, bSuccess;
444 
445     ClassIndex = 0;
446     do
447     {
448         // Loop through all the device classes
449         bClassSuccess = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
450         if (bClassSuccess)
451         {
452             bool bClassUnknown = false;
453             bool AddedParent = false;
454             INT DeviceIndex = 0;
455             bool MoreItems = false;
456 
457             // Get the cached class node
458             ClassNode = GetClassNode(&ClassGuid);
459             if (ClassNode == nullptr)
460             {
461                 ClassIndex++;
462                 continue;
463             }
464 
465             // Set a flag is this is the (special case) unknown class
466             if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_UNKNOWN))
467                 bClassUnknown = true;
468 
469             // Check if this is a hidden class
470             if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_LEGACYDRIVER) ||
471                 IsEqualGUID(ClassGuid, GUID_DEVCLASS_VOLUME))
472             {
473                 // Ignore this device if we aren't displaying hidden devices
474                 if (m_ShowHidden == FALSE)
475                 {
476                     ClassIndex++;
477                     continue;
478                 }
479             }
480 
481             do
482             {
483                 // Get a handle to all the devices in this class
484                 SP_DEVINFO_DATA DeviceInfoData;
485                 ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
486                 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
487                 bSuccess = SetupDiEnumDeviceInfo(hDevInfo,
488                                                  DeviceIndex,
489                                                  &DeviceInfoData);
490                 if (bSuccess == FALSE && GetLastError() == ERROR_NO_MORE_ITEMS)
491                     MoreItems = false;
492 
493                 if (bSuccess)
494                 {
495                     MoreItems = true;
496 
497                     // The unknown class handle contains all devices on the system,
498                     // and we're just looking for the ones with a null GUID
499                     if (bClassUnknown)
500                     {
501                         if (IsEqualGUID(DeviceInfoData.ClassGuid, GUID_NULL) == FALSE)
502                         {
503                             // This is a known device, we aren't interested in it
504                             DeviceIndex++;
505                             continue;
506                         }
507                     }
508 
509                     // Get the cached device node
510                     DeviceNode = GetDeviceNode(DeviceInfoData.DevInst);
511                     if (DeviceNode == nullptr)
512                     {
513                         DeviceIndex++;
514                         continue;
515                     }
516 
517                     // Check if this is a hidden device
518                     if (DeviceNode->IsHidden())
519                     {
520                         // Ignore this device if we aren't displaying hidden devices
521                         if (m_ShowHidden == FALSE)
522                         {
523                             DeviceIndex++;
524                             continue;
525                         }
526                     }
527 
528                     // We have a device, we need to add the parent if it hasn't yet been added
529                     if (AddedParent == false)
530                     {
531                         // Insert the new class under the root item
532                         hTreeItem = InsertIntoTreeView(m_hTreeRoot,
533                                                        ClassNode);
534                         AddedParent = true;
535                     }
536 
537                     // Add the device under the class item node
538                     (void)InsertIntoTreeView(hTreeItem, DeviceNode);
539 
540                     // Expand the class if it has a problem device
541                     if (DeviceNode->HasProblem())
542                     {
543                         (void)TreeView_Expand(m_hTreeView,
544                                               hTreeItem,
545                                               TVE_EXPAND);
546                     }
547                 }
548 
549                 DeviceIndex++;
550 
551             } while (MoreItems);
552 
553             // If this class has devices, sort them alphabetically
554             if (AddedParent == true)
555             {
556                 (void)TreeView_SortChildren(m_hTreeView,
557                                             hTreeItem,
558                                             0);
559             }
560         }
561 
562         ClassIndex++;
563 
564     } while (bClassSuccess);
565 
566     // Sort the classes alphabetically
567     (void)TreeView_SortChildren(m_hTreeView,
568                                 m_hTreeRoot,
569                                 0);
570 
571     // Expand the root item
572     (void)TreeView_Expand(m_hTreeView,
573                           m_hTreeRoot,
574                           TVE_EXPAND);
575 
576     // Pre-select the root item
577     (VOID)TreeView_SelectItem(m_hTreeView,
578                               m_hTreeRoot);
579 
580     return 0;
581 }
582 
583 bool
584 CDeviceView::ListDevicesByConnection()
585 {
586     // Walk the device tree and add all the devices
587     (void)RecurseChildDevices(m_RootNode->GetDeviceInst(), m_hTreeRoot);
588 
589     // Expand the root item
590     (void)TreeView_Expand(m_hTreeView,
591                           m_hTreeRoot,
592                           TVE_EXPAND);
593 
594     return true;
595 }
596 
597 bool
598 CDeviceView::RecurseChildDevices(
599     _In_ DEVINST ParentDevice,
600     _In_ HTREEITEM hParentTreeItem
601     )
602 {
603     HTREEITEM hDevItem = NULL;
604     DEVINST Device;
605     bool HasProblem = false;
606     bool bSuccess;
607 
608     // Check if the parent has any child devices
609     if (GetChildDevice(ParentDevice, &Device) == FALSE)
610         return true;
611 
612     // Get the cached device node
613     CDeviceNode *DeviceNode;
614     DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
615     if (DeviceNode == nullptr)
616     {
617         return false;
618     }
619 
620     // Don't show hidden devices if not requested
621     if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
622     {
623         // Add this device to the tree under its parent
624         hDevItem = InsertIntoTreeView(hParentTreeItem,
625                                       DeviceNode);
626         if (hDevItem)
627         {
628             // Check if this child has any children itself
629             if (!RecurseChildDevices(Device, hDevItem))
630                 HasProblem = true;
631         }
632 
633         if (DeviceNode->HasProblem())
634         {
635             HasProblem = true;
636         }
637     }
638 
639 
640     // Check for siblings
641     for (;;)
642     {
643         // Check if the parent device has anything at the same level
644         bSuccess = GetSiblingDevice(Device, &Device);
645         if (bSuccess == FALSE)
646             break;
647 
648         DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
649         if (DeviceNode == nullptr)
650         {
651             continue;
652         }
653 
654         // Don't show hidden devices if not requested
655         if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
656         {
657             if (DeviceNode->HasProblem())
658             {
659                 HasProblem = true;
660             }
661 
662             // Add this device to the tree under its parent
663             hDevItem = InsertIntoTreeView(hParentTreeItem,
664                                           DeviceNode);
665             if (hDevItem)
666             {
667                 // Check if this child has any children itself
668                 if (!RecurseChildDevices(Device, hDevItem))
669                     HasProblem = true;
670             }
671         }
672     }
673 
674     (void)TreeView_SortChildren(m_hTreeView,
675                                 hParentTreeItem,
676                                 0);
677 
678     // Expand the class if it has a problem device
679     if (HasProblem == true)
680     {
681         (void)TreeView_Expand(m_hTreeView,
682                               hParentTreeItem,
683                               TVE_EXPAND);
684     }
685 
686     // If there was a problem, expand the ancestors
687     if (HasProblem)
688         return false;
689 
690     return true;
691 }
692 
693 bool
694 CDeviceView::EnableSelectedDevice(
695     _In_ bool Enable,
696     _Out_ bool &NeedsReboot
697     )
698 {
699     CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
700     if (Node == nullptr)
701         return false;
702 
703     if (Enable == false)
704     {
705         CAtlStringW str;
706         if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_DISABLE))
707         {
708             if (MessageBoxW(m_hMainWnd,
709                             str,
710                             Node->GetDisplayName(),
711                             MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES)
712             {
713                 return false;
714             }
715         }
716     }
717 
718     return Node->EnableDevice(Enable, NeedsReboot);
719 }
720 
721 bool
722 CDeviceView::UpdateSelectedDevice(
723     _Out_ bool &NeedsReboot
724     )
725 {
726     CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
727     if (Node == nullptr)
728         return false;
729 
730     DWORD dwReboot;
731     if (InstallDevInst(m_hMainWnd, Node->GetDeviceId(), TRUE, &dwReboot))
732     {
733         NeedsReboot = false;
734         return true;
735     }
736 
737     return false;
738 }
739 
740 bool
741 CDeviceView::UninstallSelectedDevice(
742     )
743 {
744     CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
745     if (Node == nullptr)
746         return false;
747 
748     CAtlStringW str;
749     if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_UNINSTALL))
750     {
751         if (MessageBoxW(m_hMainWnd,
752                         str,
753                         Node->GetDisplayName(),
754                         MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES)
755         {
756             return false;
757         }
758     }
759 
760     return Node->UninstallDevice();
761 }
762 
763 bool
764 CDeviceView::RunAddHardwareWizard()
765 {
766     PADDHARDWAREWIZARD pAddHardwareWizard;
767     HMODULE hModule;
768 
769     hModule = LoadLibraryW(L"hdwwiz.cpl");
770     if (hModule == NULL)
771         return false;
772 
773     pAddHardwareWizard = (PADDHARDWAREWIZARD)GetProcAddress(hModule,
774                                                             "AddHardwareWizard");
775     if (pAddHardwareWizard == NULL)
776     {
777         FreeLibrary(hModule);
778         return false;
779     }
780 
781     pAddHardwareWizard(m_hMainWnd, NULL);
782 
783     FreeLibrary(hModule);
784     return true;
785 }
786 
787 bool
788 CDeviceView::GetChildDevice(
789     _In_ DEVINST ParentDevInst,
790     _Out_ PDEVINST DevInst
791 )
792 {
793     CONFIGRET cr;
794     cr = CM_Get_Child(DevInst,
795                       ParentDevInst,
796                       0);
797     return (cr == CR_SUCCESS);
798 }
799 
800 bool
801 CDeviceView::GetSiblingDevice(
802     _In_ DEVINST PrevDevice,
803     _Out_ PDEVINST DevInst
804 )
805 {
806     CONFIGRET cr;
807     cr = CM_Get_Sibling(DevInst,
808                         PrevDevice,
809                         0);
810     return (cr == CR_SUCCESS);
811 }
812 
813 HTREEITEM
814 CDeviceView::InsertIntoTreeView(
815     _In_opt_ HTREEITEM hParent,
816     _In_ CNode *Node
817     )
818 {
819     LPWSTR lpLabel;
820     lpLabel = Node->GetDisplayName();
821 
822     TV_ITEMW tvi;
823     TV_INSERTSTRUCT tvins;
824     ZeroMemory(&tvi, sizeof(tvi));
825     ZeroMemory(&tvins, sizeof(tvins));
826 
827     tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
828     tvi.pszText = lpLabel;
829     tvi.cchTextMax = wcslen(lpLabel);
830     tvi.lParam = (LPARAM)Node;
831     tvi.iImage = Node->GetClassImage();
832     tvi.iSelectedImage = Node->GetClassImage();
833 
834     // try to cast it to a device node. This will only succeed if it's the correct type
835     CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node);
836     if (DeviceNode && DeviceNode->GetOverlayImage())
837     {
838         tvi.mask |= TVIF_STATE;
839         tvi.stateMask = TVIS_OVERLAYMASK;
840         tvi.state = INDEXTOOVERLAYMASK(DeviceNode->GetOverlayImage());
841     }
842 
843     tvins.item = tvi;
844     tvins.hParent = hParent;
845 
846     return TreeView_InsertItem(m_hTreeView, &tvins);
847 }
848 
849 void
850 CDeviceView::BuildActionMenuForNode(
851     _In_ HMENU OwnerMenu,
852     _In_ CNode *Node,
853     _In_ bool MainMenu
854     )
855 {
856     // Create a separator structure
857     MENUITEMINFOW MenuSeparator = { 0 };
858     MenuSeparator.cbSize = sizeof(MENUITEMINFOW);
859     MenuSeparator.fType = MFT_SEPARATOR;
860 
861     // Setup the
862     MENUITEMINFOW MenuItemInfo = { 0 };
863     MenuItemInfo.cbSize = sizeof(MENUITEMINFOW);
864     MenuItemInfo.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;
865     MenuItemInfo.fType = MFT_STRING;
866 
867     CAtlStringW String;
868     int i = 0;
869 
870     // Device nodes have extra data
871     if (Node->GetNodeType() == DeviceNode)
872     {
873         CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node);
874 
875         if (DeviceNode->CanUpdate())
876         {
877             String.LoadStringW(g_hThisInstance, IDS_MENU_UPDATE);
878             MenuItemInfo.wID = IDM_UPDATE_DRV;
879             MenuItemInfo.dwTypeData = String.GetBuffer();
880             InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
881             i++;
882         }
883 
884         if (DeviceNode->IsDisabled())
885         {
886             String.LoadStringW(g_hThisInstance, IDS_MENU_ENABLE);
887             MenuItemInfo.wID = IDM_ENABLE_DRV;
888             MenuItemInfo.dwTypeData = String.GetBuffer();
889             InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
890             i++;
891         }
892 
893         if (DeviceNode->CanDisable() && !DeviceNode->IsDisabled())
894         {
895             String.LoadStringW(g_hThisInstance, IDS_MENU_DISABLE);
896             MenuItemInfo.wID = IDM_DISABLE_DRV;
897             MenuItemInfo.dwTypeData = String.GetBuffer();
898             InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
899             i++;
900         }
901 
902         if (DeviceNode->CanUninstall())
903         {
904             String.LoadStringW(g_hThisInstance, IDS_MENU_UNINSTALL);
905             MenuItemInfo.wID = IDM_UNINSTALL_DRV;
906             MenuItemInfo.dwTypeData = String.GetBuffer();
907             InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
908             i++;
909         }
910 
911         InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator);
912         i++;
913     }
914 
915     // All nodes have the scan option
916     String.LoadStringW(g_hThisInstance, IDS_MENU_SCAN);
917     MenuItemInfo.wID = IDM_SCAN_HARDWARE;
918     MenuItemInfo.dwTypeData = String.GetBuffer();
919     InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
920     i++;
921 
922     if ((Node->GetNodeType() == RootNode) || (MainMenu == true))
923     {
924         String.LoadStringW(g_hThisInstance, IDS_MENU_ADD);
925         MenuItemInfo.wID = IDM_ADD_HARDWARE;
926         MenuItemInfo.dwTypeData = String.GetBuffer();
927         InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
928         i++;
929     }
930 
931     if (Node->HasProperties())
932     {
933         InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator);
934         i++;
935 
936         String.LoadStringW(g_hThisInstance, IDS_MENU_PROPERTIES);
937         MenuItemInfo.wID = IDM_PROPERTIES;
938         MenuItemInfo.dwTypeData = String.GetBuffer();
939         InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
940         i++;
941 
942         SetMenuDefaultItem(OwnerMenu, IDC_PROPERTIES, FALSE);
943     }
944 }
945 
946 HTREEITEM
947 CDeviceView::RecurseFindDevice(
948     _In_ HTREEITEM hParentItem,
949     _In_ CNode *Node
950     )
951 {
952     HTREEITEM FoundItem;
953     HTREEITEM hItem;
954     TVITEMW tvItem;
955     CNode *FoundNode;
956 
957     // Check if this node has any children
958     hItem = TreeView_GetChild(m_hTreeView, hParentItem);
959     if (hItem == NULL)
960         return NULL;
961 
962     // The lParam contains the node pointer data
963     tvItem.hItem = hItem;
964     tvItem.mask = TVIF_PARAM;
965     if (TreeView_GetItem(m_hTreeView, &tvItem) &&
966         tvItem.lParam != NULL)
967     {
968         // check for a matching node
969         FoundNode = reinterpret_cast<CNode *>(tvItem.lParam);
970         if ((FoundNode->GetNodeType() == Node->GetNodeType()) &&
971             (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid())))
972         {
973             // check if this is a class node, or a device with matching ID's
974             if ((FoundNode->GetNodeType() == ClassNode) ||
975                 (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0))
976             {
977                 return hItem;
978             }
979         }
980     }
981 
982     // This node may have its own children
983     FoundItem = RecurseFindDevice(hItem, Node);
984     if (FoundItem)
985         return FoundItem;
986 
987     // Loop all the siblings
988     for (;;)
989     {
990         // Get the next item at this level
991         hItem = TreeView_GetNextSibling(m_hTreeView, hItem);
992         if (hItem == NULL)
993             break;
994 
995         // The lParam contains the node pointer data
996         tvItem.hItem = hItem;
997         tvItem.mask = TVIF_PARAM;
998         if (TreeView_GetItem(m_hTreeView, &tvItem))
999         {
1000             // check for a matching class
1001             FoundNode = reinterpret_cast<CNode *>(tvItem.lParam);
1002             if ((FoundNode->GetNodeType() == Node->GetNodeType()) &&
1003                 (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid())))
1004             {
1005                 // check if this is a class node, or a device with matching ID's
1006                 if ((FoundNode->GetNodeType() == ClassNode) ||
1007                     (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0))
1008                 {
1009                     return hItem;
1010                 }
1011             }
1012         }
1013 
1014         // This node may have its own children
1015         FoundItem = RecurseFindDevice(hItem, Node);
1016         if (FoundItem)
1017             return FoundItem;
1018     }
1019 
1020     return hItem;
1021 }
1022 
1023 void
1024 CDeviceView::SelectNode(
1025     _In_ CNode *Node
1026     )
1027 {
1028     HTREEITEM hRoot, hItem;
1029 
1030     // Check if there are any items in the tree
1031     hRoot = TreeView_GetRoot(m_hTreeView);
1032     if (hRoot == NULL)
1033         return;
1034 
1035     // If we don't want to set select a node, just select root
1036     if (Node == nullptr || Node->GetNodeType() == RootNode)
1037     {
1038         TreeView_SelectItem(m_hTreeView, hRoot);
1039         return;
1040     }
1041 
1042     // Scan the tree looking for the node we want
1043     hItem = RecurseFindDevice(hRoot, Node);
1044     if (hItem)
1045     {
1046         TreeView_SelectItem(m_hTreeView, hItem);
1047     }
1048     else
1049     {
1050         TreeView_SelectItem(m_hTreeView, hRoot);
1051     }
1052 }
1053 
1054 
1055 void
1056 CDeviceView::EmptyDeviceView()
1057 {
1058     (VOID)TreeView_DeleteAllItems(m_hTreeView);
1059 }
1060 
1061 
1062 CClassNode*
1063 CDeviceView::GetClassNode(
1064     _In_ LPGUID ClassGuid
1065     )
1066 {
1067     POSITION Pos;
1068     CClassNode *Node = nullptr;
1069 
1070     Pos = m_ClassNodeList.GetHeadPosition();
1071     if (Pos == NULL)
1072         return nullptr;
1073 
1074     do
1075     {
1076         Node = m_ClassNodeList.GetNext(Pos);
1077         if (IsEqualGUID(*Node->GetClassGuid(), *ClassGuid))
1078         {
1079             ATLASSERT(Node->GetNodeType() == ClassNode);
1080             break;
1081         }
1082 
1083         Node = nullptr;
1084 
1085     } while (Pos != NULL);
1086 
1087     return Node;
1088 }
1089 
1090 CDeviceNode*
1091 CDeviceView::GetDeviceNode(
1092     _In_ DEVINST Device
1093     )
1094 {
1095     POSITION Pos;
1096     CDeviceNode *Node = nullptr;
1097 
1098     Pos = m_DeviceNodeList.GetHeadPosition();
1099     if (Pos == NULL)
1100         return nullptr;
1101 
1102     do
1103     {
1104         Node = m_DeviceNodeList.GetNext(Pos);
1105         if (Node->GetDeviceInst() == Device)
1106         {
1107             ATLASSERT(Node->GetNodeType() == DeviceNode);
1108             break;
1109         }
1110 
1111         Node = nullptr;
1112 
1113     } while (Pos != NULL);
1114 
1115     return Node;
1116 }
1117 
1118 CNode* CDeviceView::GetNode(
1119     _In_ LPTV_ITEMW TvItem
1120     )
1121 {
1122     TvItem->mask = TVIF_PARAM;
1123     if (TreeView_GetItem(m_hTreeView, TvItem))
1124     {
1125         return (CNode *)TvItem->lParam;
1126     }
1127     return nullptr;
1128 }
1129 
1130 void
1131 CDeviceView::EmptyLists()
1132 {
1133     CNode *Node;
1134 
1135     while (!m_ClassNodeList.IsEmpty())
1136     {
1137         Node = m_ClassNodeList.RemoveTail();
1138         delete Node;
1139     }
1140 
1141     while (!m_DeviceNodeList.IsEmpty())
1142     {
1143         Node = m_DeviceNodeList.RemoveTail();
1144         delete Node;
1145     }
1146 }
1147 
1148 bool
1149 CDeviceView::RefreshDeviceList()
1150 {
1151     GUID ClassGuid;
1152     CClassNode *ClassNode;
1153     CDeviceNode *DeviceNode;
1154     HDEVINFO hDevInfo;
1155     SP_DEVINFO_DATA DeviceInfoData;
1156     DWORD i;
1157     BOOL Success;
1158 
1159     ULONG ClassIndex = 0;
1160 
1161     EmptyLists();
1162 
1163     if (m_RootNode) delete m_RootNode;
1164     m_RootNode = new CRootNode(&m_ImageListData);
1165     m_RootNode->SetupNode();
1166 
1167     // Loop through all the classes
1168     do
1169     {
1170         Success = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
1171         if (Success)
1172         {
1173             // Create a new class node and add it to the list
1174             ClassNode = new CClassNode(&ClassGuid, &m_ImageListData);
1175             if (ClassNode->SetupNode())
1176             {
1177                 m_ClassNodeList.AddTail(ClassNode);
1178             }
1179 
1180             SetupDiDestroyDeviceInfoList(hDevInfo);
1181         }
1182         ClassIndex++;
1183     } while (Success);
1184 
1185     // Get all the devices on the local machine
1186     hDevInfo = SetupDiGetClassDevsW(NULL,
1187                                     0,
1188                                     0,
1189                                     DIGCF_PRESENT | DIGCF_ALLCLASSES);
1190     if (hDevInfo == INVALID_HANDLE_VALUE)
1191     {
1192         return false;
1193     }
1194 
1195     // loop though all the devices
1196     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1197     for (i = 0;; i++)
1198     {
1199         // Get the devinst for this device
1200         Success = SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData);
1201         if (Success == FALSE)
1202             break;
1203 
1204         // create a new device node and add it to the list
1205         DeviceNode = new CDeviceNode(DeviceInfoData.DevInst, &m_ImageListData);
1206         /* FIXME: Start of Hack for CORE-5643 */
1207         if (!DeviceNode->IsInstalled())
1208             continue;
1209         /* FIXME: End of Hack for CORE-5643 */
1210 
1211         if (DeviceNode->SetupNode())
1212         {
1213             m_DeviceNodeList.AddTail(DeviceNode);
1214         }
1215         else
1216         {
1217             ATLASSERT(FALSE);
1218         }
1219     }
1220 
1221     SetupDiDestroyDeviceInfoList(hDevInfo);
1222 
1223     return TRUE;
1224 }