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