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 IDC_PROPERTIES:
229         {
230             DisplayPropertySheet();
231             break;
232         }
233 
234         case IDC_SCAN_HARDWARE:
235         {
236             Refresh(GetCurrentView(),
237                     true,
238                     true);
239             break;
240         }
241 
242         case IDC_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 IDC_DISABLE_DRV:
254         {
255             bool NeedsReboot;
256             EnableSelectedDevice(false, NeedsReboot);
257             break;
258         }
259 
260         case IDC_UPDATE_DRV:
261         {
262             bool NeedsReboot;
263             UpdateSelectedDevice(NeedsReboot);
264             break;
265         }
266 
267         case IDC_UNINSTALL_DRV:
268         {
269             UninstallSelectedDevice();
270             break;
271         }
272 
273         case IDC_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                 ATLASSERT(FALSE);
462                 ClassIndex++;
463                 continue;
464             }
465 
466             // Set a flag is this is the (special case) unknown class
467             if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_UNKNOWN))
468                 bClassUnknown = true;
469 
470             // Check if this is a hidden class
471             if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_LEGACYDRIVER) ||
472                 IsEqualGUID(ClassGuid, GUID_DEVCLASS_VOLUME))
473             {
474                 // Ignore this device if we aren't displaying hidden devices
475                 if (m_ShowHidden == FALSE)
476                 {
477                     ClassIndex++;
478                     continue;
479                 }
480             }
481 
482             do
483             {
484                 // Get a handle to all the devices in this class
485                 SP_DEVINFO_DATA DeviceInfoData;
486                 ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
487                 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
488                 bSuccess = SetupDiEnumDeviceInfo(hDevInfo,
489                                                  DeviceIndex,
490                                                  &DeviceInfoData);
491                 if (bSuccess == FALSE && GetLastError() == ERROR_NO_MORE_ITEMS)
492                     MoreItems = false;
493 
494                 if (bSuccess)
495                 {
496                     MoreItems = true;
497 
498                     // The unknown class handle contains all devices on the system,
499                     // and we're just looking for the ones with a null GUID
500                     if (bClassUnknown)
501                     {
502                         if (IsEqualGUID(DeviceInfoData.ClassGuid, GUID_NULL) == FALSE)
503                         {
504                             // This is a known device, we aren't interested in it
505                             DeviceIndex++;
506                             continue;
507                         }
508                     }
509 
510                     // Get the cached device node
511                     DeviceNode = GetDeviceNode(DeviceInfoData.DevInst);
512                     if (DeviceNode == nullptr)
513                     {
514                         ATLASSERT(bClassUnknown == true);
515                         DeviceIndex++;
516                         continue;
517                     }
518 
519                     // Check if this is a hidden device
520                     if (DeviceNode->IsHidden())
521                     {
522                         // Ignore this device if we aren't displaying hidden devices
523                         if (m_ShowHidden == FALSE)
524                         {
525                             DeviceIndex++;
526                             continue;
527                         }
528                     }
529 
530                     // We have a device, we need to add the parent if it hasn't yet been added
531                     if (AddedParent == false)
532                     {
533                         // Insert the new class under the root item
534                         hTreeItem = InsertIntoTreeView(m_hTreeRoot,
535                                                        ClassNode);
536                         AddedParent = true;
537                     }
538 
539                     // Add the device under the class item node
540                     (void)InsertIntoTreeView(hTreeItem, DeviceNode);
541 
542                     // Expand the class if it has a problem device
543                     if (DeviceNode->HasProblem())
544                     {
545                         (void)TreeView_Expand(m_hTreeView,
546                                               hTreeItem,
547                                               TVE_EXPAND);
548                     }
549                 }
550 
551                 DeviceIndex++;
552 
553             } while (MoreItems);
554 
555             // If this class has devices, sort them alphabetically
556             if (AddedParent == true)
557             {
558                 (void)TreeView_SortChildren(m_hTreeView,
559                                             hTreeItem,
560                                             0);
561             }
562         }
563 
564         ClassIndex++;
565 
566     } while (bClassSuccess);
567 
568     // Sort the classes alphabetically
569     (void)TreeView_SortChildren(m_hTreeView,
570                                 m_hTreeRoot,
571                                 0);
572 
573     // Expand the root item
574     (void)TreeView_Expand(m_hTreeView,
575                           m_hTreeRoot,
576                           TVE_EXPAND);
577 
578     // Pre-select the root item
579     (VOID)TreeView_SelectItem(m_hTreeView,
580                               m_hTreeRoot);
581 
582     return 0;
583 }
584 
585 bool
586 CDeviceView::ListDevicesByConnection()
587 {
588     // Walk the device tree and add all the devices
589     (void)RecurseChildDevices(m_RootNode->GetDeviceInst(), m_hTreeRoot);
590 
591     // Expand the root item
592     (void)TreeView_Expand(m_hTreeView,
593                           m_hTreeRoot,
594                           TVE_EXPAND);
595 
596     return true;
597 }
598 
599 bool
600 CDeviceView::RecurseChildDevices(
601     _In_ DEVINST ParentDevice,
602     _In_ HTREEITEM hParentTreeItem
603     )
604 {
605     HTREEITEM hDevItem = NULL;
606     DEVINST Device;
607     bool HasProblem = false;
608     bool bSuccess;
609 
610     // Check if the parent has any child devices
611     if (GetChildDevice(ParentDevice, &Device) == FALSE)
612         return true;
613 
614     // Get the cached device node
615     CDeviceNode *DeviceNode;
616     DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
617     if (DeviceNode == nullptr)
618     {
619         ATLASSERT(FALSE);
620         return false;
621     }
622 
623     // Don't show hidden devices if not requested
624     if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
625     {
626         // Add this device to the tree under its parent
627         hDevItem = InsertIntoTreeView(hParentTreeItem,
628                                       DeviceNode);
629         if (hDevItem)
630         {
631             // Check if this child has any children itself
632             if (!RecurseChildDevices(Device, hDevItem))
633                 HasProblem = true;
634         }
635 
636         if (DeviceNode->HasProblem())
637         {
638             HasProblem = true;
639         }
640     }
641 
642 
643     // Check for siblings
644     for (;;)
645     {
646         // Check if the parent device has anything at the same level
647         bSuccess = GetSiblingDevice(Device, &Device);
648         if (bSuccess == FALSE)
649             break;
650 
651         DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
652         if (DeviceNode == nullptr)
653         {
654             ATLASSERT(FALSE);
655         }
656 
657         // Don't show hidden devices if not requested
658         if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
659         {
660             if (DeviceNode->HasProblem())
661             {
662                 HasProblem = true;
663             }
664 
665             // Add this device to the tree under its parent
666             hDevItem = InsertIntoTreeView(hParentTreeItem,
667                                           DeviceNode);
668             if (hDevItem)
669             {
670                 // Check if this child has any children itself
671                 if (!RecurseChildDevices(Device, hDevItem))
672                     HasProblem = true;
673             }
674         }
675     }
676 
677     (void)TreeView_SortChildren(m_hTreeView,
678                                 hParentTreeItem,
679                                 0);
680 
681     // Expand the class if it has a problem device
682     if (HasProblem == true)
683     {
684         (void)TreeView_Expand(m_hTreeView,
685                               hParentTreeItem,
686                               TVE_EXPAND);
687     }
688 
689     // If there was a problem, expand the ancestors
690     if (HasProblem)
691         return false;
692 
693     return true;
694 }
695 
696 bool
697 CDeviceView::EnableSelectedDevice(
698     _In_ bool Enable,
699     _Out_ bool &NeedsReboot
700     )
701 {
702     CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
703     if (Node == nullptr)
704         return false;
705 
706     if (Enable == false)
707     {
708         CAtlStringW str;
709         if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_DISABLE))
710         {
711             if (MessageBoxW(m_hMainWnd,
712                             str,
713                             Node->GetDisplayName(),
714                             MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES)
715             {
716                 return false;
717             }
718         }
719     }
720 
721     return Node->EnableDevice(Enable, NeedsReboot);
722 }
723 
724 bool
725 CDeviceView::UpdateSelectedDevice(
726     _Out_ bool &NeedsReboot
727     )
728 {
729     CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
730     if (Node == nullptr)
731         return false;
732 
733     DWORD dwReboot;
734     if (InstallDevInst(m_hMainWnd, Node->GetDeviceId(), TRUE, &dwReboot))
735     {
736         NeedsReboot = false;
737         return true;
738     }
739 
740     return false;
741 }
742 
743 bool
744 CDeviceView::UninstallSelectedDevice(
745     )
746 {
747     CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
748     if (Node == nullptr)
749         return false;
750 
751     CAtlStringW str;
752     if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_UNINSTALL))
753     {
754         if (MessageBoxW(m_hMainWnd,
755                         str,
756                         Node->GetDisplayName(),
757                         MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES)
758         {
759             return false;
760         }
761     }
762 
763     return Node->UninstallDevice();
764 }
765 
766 bool
767 CDeviceView::RunAddHardwareWizard()
768 {
769     PADDHARDWAREWIZARD pAddHardwareWizard;
770     HMODULE hModule;
771 
772     hModule = LoadLibraryW(L"hdwwiz.cpl");
773     if (hModule == NULL)
774         return false;
775 
776     pAddHardwareWizard = (PADDHARDWAREWIZARD)GetProcAddress(hModule,
777                                                             "AddHardwareWizard");
778     if (pAddHardwareWizard == NULL)
779     {
780         FreeLibrary(hModule);
781         return false;
782     }
783 
784     pAddHardwareWizard(m_hMainWnd, NULL);
785 
786     FreeLibrary(hModule);
787     return true;
788 }
789 
790 bool
791 CDeviceView::GetChildDevice(
792     _In_ DEVINST ParentDevInst,
793     _Out_ PDEVINST DevInst
794 )
795 {
796     CONFIGRET cr;
797     cr = CM_Get_Child(DevInst,
798                       ParentDevInst,
799                       0);
800     return (cr == CR_SUCCESS);
801 }
802 
803 bool
804 CDeviceView::GetSiblingDevice(
805     _In_ DEVINST PrevDevice,
806     _Out_ PDEVINST DevInst
807 )
808 {
809     CONFIGRET cr;
810     cr = CM_Get_Sibling(DevInst,
811                         PrevDevice,
812                         0);
813     return (cr == CR_SUCCESS);
814 }
815 
816 HTREEITEM
817 CDeviceView::InsertIntoTreeView(
818     _In_opt_ HTREEITEM hParent,
819     _In_ CNode *Node
820     )
821 {
822     LPWSTR lpLabel;
823     lpLabel = Node->GetDisplayName();
824 
825     TV_ITEMW tvi;
826     TV_INSERTSTRUCT tvins;
827     ZeroMemory(&tvi, sizeof(tvi));
828     ZeroMemory(&tvins, sizeof(tvins));
829 
830     tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
831     tvi.pszText = lpLabel;
832     tvi.cchTextMax = wcslen(lpLabel);
833     tvi.lParam = (LPARAM)Node;
834     tvi.iImage = Node->GetClassImage();
835     tvi.iSelectedImage = Node->GetClassImage();
836 
837     // try to cast it to a device node. This will only succeed if it's the correct type
838     CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node);
839     if (DeviceNode && DeviceNode->GetOverlayImage())
840     {
841         tvi.mask |= TVIF_STATE;
842         tvi.stateMask = TVIS_OVERLAYMASK;
843         tvi.state = INDEXTOOVERLAYMASK(DeviceNode->GetOverlayImage());
844     }
845 
846     tvins.item = tvi;
847     tvins.hParent = hParent;
848 
849     return TreeView_InsertItem(m_hTreeView, &tvins);
850 }
851 
852 void
853 CDeviceView::BuildActionMenuForNode(
854     _In_ HMENU OwnerMenu,
855     _In_ CNode *Node,
856     _In_ bool MainMenu
857     )
858 {
859     // Create a separator structure
860     MENUITEMINFOW MenuSeparator = { 0 };
861     MenuSeparator.cbSize = sizeof(MENUITEMINFOW);
862     MenuSeparator.fType = MFT_SEPARATOR;
863 
864     // Setup the
865     MENUITEMINFOW MenuItemInfo = { 0 };
866     MenuItemInfo.cbSize = sizeof(MENUITEMINFOW);
867     MenuItemInfo.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;
868     MenuItemInfo.fType = MFT_STRING;
869 
870     CAtlStringW String;
871     int i = 0;
872 
873     // Device nodes have extra data
874     if (Node->GetNodeType() == DeviceNode)
875     {
876         CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node);
877 
878         if (DeviceNode->CanUpdate())
879         {
880             String.LoadStringW(g_hThisInstance, IDS_MENU_UPDATE);
881             MenuItemInfo.wID = IDC_UPDATE_DRV;
882             MenuItemInfo.dwTypeData = String.GetBuffer();
883             InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
884             i++;
885         }
886 
887         if (DeviceNode->IsDisabled())
888         {
889             String.LoadStringW(g_hThisInstance, IDS_MENU_ENABLE);
890             MenuItemInfo.wID = IDC_ENABLE_DRV;
891             MenuItemInfo.dwTypeData = String.GetBuffer();
892             InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
893             i++;
894         }
895 
896         if (DeviceNode->CanDisable() && !DeviceNode->IsDisabled())
897         {
898             String.LoadStringW(g_hThisInstance, IDS_MENU_DISABLE);
899             MenuItemInfo.wID = IDC_DISABLE_DRV;
900             MenuItemInfo.dwTypeData = String.GetBuffer();
901             InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
902             i++;
903         }
904 
905         if (DeviceNode->CanUninstall())
906         {
907             String.LoadStringW(g_hThisInstance, IDS_MENU_UNINSTALL);
908             MenuItemInfo.wID = IDC_UNINSTALL_DRV;
909             MenuItemInfo.dwTypeData = String.GetBuffer();
910             InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
911             i++;
912         }
913 
914         InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator);
915         i++;
916     }
917 
918     // All nodes have the scan option
919     String.LoadStringW(g_hThisInstance, IDS_MENU_SCAN);
920     MenuItemInfo.wID = IDC_SCAN_HARDWARE;
921     MenuItemInfo.dwTypeData = String.GetBuffer();
922     InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
923     i++;
924 
925     if ((Node->GetNodeType() == RootNode) || (MainMenu == true))
926     {
927         String.LoadStringW(g_hThisInstance, IDS_MENU_ADD);
928         MenuItemInfo.wID = IDC_ADD_HARDWARE;
929         MenuItemInfo.dwTypeData = String.GetBuffer();
930         InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
931         i++;
932     }
933 
934     if (Node->HasProperties())
935     {
936         InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator);
937         i++;
938 
939         String.LoadStringW(g_hThisInstance, IDS_MENU_PROPERTIES);
940         MenuItemInfo.wID = IDC_PROPERTIES;
941         MenuItemInfo.dwTypeData = String.GetBuffer();
942         InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
943         i++;
944 
945         SetMenuDefaultItem(OwnerMenu, IDC_PROPERTIES, FALSE);
946     }
947 }
948 
949 HTREEITEM
950 CDeviceView::RecurseFindDevice(
951     _In_ HTREEITEM hParentItem,
952     _In_ CNode *Node
953     )
954 {
955     HTREEITEM FoundItem;
956     HTREEITEM hItem;
957     TVITEMW tvItem;
958     CNode *FoundNode;
959 
960     // Check if this node has any children
961     hItem = TreeView_GetChild(m_hTreeView, hParentItem);
962     if (hItem == NULL)
963         return NULL;
964 
965     // The lParam contains the node pointer data
966     tvItem.hItem = hItem;
967     tvItem.mask = TVIF_PARAM;
968     if (TreeView_GetItem(m_hTreeView, &tvItem) &&
969         tvItem.lParam != NULL)
970     {
971         // check for a matching node
972         FoundNode = reinterpret_cast<CNode *>(tvItem.lParam);
973         if ((FoundNode->GetNodeType() == Node->GetNodeType()) &&
974             (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid())))
975         {
976             // check if this is a class node, or a device with matching ID's
977             if ((FoundNode->GetNodeType() == ClassNode) ||
978                 (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0))
979             {
980                 return hItem;
981             }
982         }
983     }
984 
985     // This node may have its own children
986     FoundItem = RecurseFindDevice(hItem, Node);
987     if (FoundItem)
988         return FoundItem;
989 
990     // Loop all the siblings
991     for (;;)
992     {
993         // Get the next item at this level
994         hItem = TreeView_GetNextSibling(m_hTreeView, hItem);
995         if (hItem == NULL)
996             break;
997 
998         // The lParam contains the node pointer data
999         tvItem.hItem = hItem;
1000         tvItem.mask = TVIF_PARAM;
1001         if (TreeView_GetItem(m_hTreeView, &tvItem))
1002         {
1003             // check for a matching class
1004             FoundNode = reinterpret_cast<CNode *>(tvItem.lParam);
1005             if ((FoundNode->GetNodeType() == Node->GetNodeType()) &&
1006                 (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid())))
1007             {
1008                 // check if this is a class node, or a device with matching ID's
1009                 if ((FoundNode->GetNodeType() == ClassNode) ||
1010                     (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0))
1011                 {
1012                     return hItem;
1013                 }
1014             }
1015         }
1016 
1017         // This node may have its own children
1018         FoundItem = RecurseFindDevice(hItem, Node);
1019         if (FoundItem)
1020             return FoundItem;
1021     }
1022 
1023     return hItem;
1024 }
1025 
1026 void
1027 CDeviceView::SelectNode(
1028     _In_ CNode *Node
1029     )
1030 {
1031     HTREEITEM hRoot, hItem;
1032 
1033     // Check if there are any items in the tree
1034     hRoot = TreeView_GetRoot(m_hTreeView);
1035     if (hRoot == NULL)
1036         return;
1037 
1038     // If we don't want to set select a node, just select root
1039     if (Node == nullptr || Node->GetNodeType() == RootNode)
1040     {
1041         TreeView_SelectItem(m_hTreeView, hRoot);
1042         return;
1043     }
1044 
1045     // Scan the tree looking for the node we want
1046     hItem = RecurseFindDevice(hRoot, Node);
1047     if (hItem)
1048     {
1049         TreeView_SelectItem(m_hTreeView, hItem);
1050     }
1051     else
1052     {
1053         TreeView_SelectItem(m_hTreeView, hRoot);
1054     }
1055 }
1056 
1057 
1058 void
1059 CDeviceView::EmptyDeviceView()
1060 {
1061     (VOID)TreeView_DeleteAllItems(m_hTreeView);
1062 }
1063 
1064 
1065 CClassNode*
1066 CDeviceView::GetClassNode(
1067     _In_ LPGUID ClassGuid
1068     )
1069 {
1070     POSITION Pos;
1071     CClassNode *Node = nullptr;
1072 
1073     Pos = m_ClassNodeList.GetHeadPosition();
1074     if (Pos == NULL)
1075         return nullptr;
1076 
1077     do
1078     {
1079         Node = m_ClassNodeList.GetNext(Pos);
1080         if (IsEqualGUID(*Node->GetClassGuid(), *ClassGuid))
1081         {
1082             ATLASSERT(Node->GetNodeType() == ClassNode);
1083             break;
1084         }
1085 
1086         Node = nullptr;
1087 
1088     } while (Pos != NULL);
1089 
1090     return Node;
1091 }
1092 
1093 CDeviceNode*
1094 CDeviceView::GetDeviceNode(
1095     _In_ DEVINST Device
1096     )
1097 {
1098     POSITION Pos;
1099     CDeviceNode *Node = nullptr;
1100 
1101     Pos = m_DeviceNodeList.GetHeadPosition();
1102     if (Pos == NULL)
1103         return nullptr;
1104 
1105     do
1106     {
1107         Node = m_DeviceNodeList.GetNext(Pos);
1108         if (Node->GetDeviceInst() == Device)
1109         {
1110             ATLASSERT(Node->GetNodeType() == DeviceNode);
1111             break;
1112         }
1113 
1114         Node = nullptr;
1115 
1116     } while (Pos != NULL);
1117 
1118     return Node;
1119 }
1120 
1121 CNode* CDeviceView::GetNode(
1122     _In_ LPTV_ITEMW TvItem
1123     )
1124 {
1125     TvItem->mask = TVIF_PARAM;
1126     if (TreeView_GetItem(m_hTreeView, TvItem))
1127     {
1128         return (CNode *)TvItem->lParam;
1129     }
1130     return nullptr;
1131 }
1132 
1133 void
1134 CDeviceView::EmptyLists()
1135 {
1136     CNode *Node;
1137 
1138     while (!m_ClassNodeList.IsEmpty())
1139     {
1140         Node = m_ClassNodeList.RemoveTail();
1141         delete Node;
1142     }
1143 
1144     while (!m_DeviceNodeList.IsEmpty())
1145     {
1146         Node = m_DeviceNodeList.RemoveTail();
1147         delete Node;
1148     }
1149 }
1150 
1151 bool
1152 CDeviceView::RefreshDeviceList()
1153 {
1154     GUID ClassGuid;
1155     CClassNode *ClassNode;
1156     CDeviceNode *DeviceNode;
1157     HDEVINFO hDevInfo;
1158     SP_DEVINFO_DATA DeviceInfoData;
1159     DWORD i;
1160     BOOL Success;
1161 
1162     ULONG ClassIndex = 0;
1163 
1164     EmptyLists();
1165 
1166     if (m_RootNode) delete m_RootNode;
1167     m_RootNode = new CRootNode(&m_ImageListData);
1168     m_RootNode->SetupNode();
1169 
1170     // Loop through all the classes
1171     do
1172     {
1173         Success = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
1174         if (Success)
1175         {
1176             // Create a new class node and add it to the list
1177             ClassNode = new CClassNode(&ClassGuid, &m_ImageListData);
1178             if (ClassNode->SetupNode())
1179             {
1180                 m_ClassNodeList.AddTail(ClassNode);
1181             }
1182 
1183             SetupDiDestroyDeviceInfoList(hDevInfo);
1184         }
1185         ClassIndex++;
1186     } while (Success);
1187 
1188     // Get all the devices on the local machine
1189     hDevInfo = SetupDiGetClassDevsW(NULL,
1190                                     0,
1191                                     0,
1192                                     DIGCF_PRESENT | DIGCF_ALLCLASSES);
1193     if (hDevInfo == INVALID_HANDLE_VALUE)
1194     {
1195         return false;
1196     }
1197 
1198     // loop though all the devices
1199     DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1200     for (i = 0;; i++)
1201     {
1202         // Get the devinst for this device
1203         Success = SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData);
1204         if (Success == FALSE)
1205             break;
1206 
1207         // create a new device node and add it to the list
1208         DeviceNode = new CDeviceNode(DeviceInfoData.DevInst, &m_ImageListData);
1209         /* FIXME: Start of Hack for CORE-5643 */
1210         if (!DeviceNode->IsInstalled())
1211             continue;
1212         /* FIXME: End of Hack for CORE-5643 */
1213 
1214         if (DeviceNode->SetupNode())
1215         {
1216             m_DeviceNodeList.AddTail(DeviceNode);
1217         }
1218         else
1219         {
1220             ATLASSERT(FALSE);
1221         }
1222     }
1223 
1224     SetupDiDestroyDeviceInfoList(hDevInfo);
1225 
1226     return TRUE;
1227 }