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