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