Reactos
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 "restypes.h"
13#include "devmgmt.h"
14#include "DeviceView.h"
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
23typedef VOID(WINAPI *PADDHARDWAREWIZARD)(HWND hwnd, LPWSTR lpName);
24
25struct RefreshThreadData
26{
27 CDeviceView *This;
28 BOOL ScanForChanges;
29 BOOL UpdateView;
30};
31
32
33// PUBLIC METHODS ************************************/
34
35CDeviceView::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
49CDeviceView::~CDeviceView(void)
50{
51}
52
53bool
54CDeviceView::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
93bool
94CDeviceView::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
107LRESULT
108CDeviceView::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
127LRESULT
128CDeviceView::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
148LRESULT
149CDeviceView::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
168LRESULT
169CDeviceView::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
215void
216CDeviceView::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
242LRESULT
243CDeviceView::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
304void
305CDeviceView::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
318void
319CDeviceView::SetFocus()
320{
321 ::SetFocus(m_hTreeView);
322}
323
324bool
325CDeviceView::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
340CNode*
341CDeviceView::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
352bool
353CDeviceView::AddRootDevice()
354{
355 m_hTreeRoot = InsertIntoTreeView(NULL, m_RootNode);
356 return (m_hTreeRoot != NULL);
357}
358
359bool
360CDeviceView::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
384unsigned 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 (VOID)This->ListResourcesByType();
430 break;
431
432 case ResourcesByConnection:
433 break;
434 }
435
436 This->SelectNode(LastSelectedNode);
437
438 delete ThreadData;
439
440 return 0;
441}
442
443
444bool
445CDeviceView::ListDevicesByType()
446{
447 CClassNode *ClassNode;
448 CDeviceNode *DeviceNode;
449 HDEVINFO hDevInfo;
450 HTREEITEM hTreeItem = NULL;
451 GUID ClassGuid;
452 INT ClassIndex;
453 BOOL bClassSuccess, bSuccess;
454
455 ClassIndex = 0;
456 do
457 {
458 // Loop through all the device classes
459 bClassSuccess = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
460 if (bClassSuccess)
461 {
462 bool AddedParent = false;
463 INT DeviceIndex = 0;
464 bool MoreItems = false;
465
466 // Get the cached class node
467 ClassNode = GetClassNode(&ClassGuid);
468 if (ClassNode == nullptr)
469 {
470 ClassIndex++;
471 continue;
472 }
473
474 // Check if this is a hidden class
475 if (IsEqualGUID(ClassGuid, GUID_DEVCLASS_LEGACYDRIVER) ||
476 IsEqualGUID(ClassGuid, GUID_DEVCLASS_VOLUME))
477 {
478 // Ignore this device if we aren't displaying hidden devices
479 if (m_ShowHidden == FALSE)
480 {
481 ClassIndex++;
482 continue;
483 }
484 }
485
486 do
487 {
488 // Get a handle to all the devices in this class
489 SP_DEVINFO_DATA DeviceInfoData;
490 ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
491 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
492 bSuccess = SetupDiEnumDeviceInfo(hDevInfo,
493 DeviceIndex,
494 &DeviceInfoData);
495 if (bSuccess == FALSE && GetLastError() == ERROR_NO_MORE_ITEMS)
496 MoreItems = false;
497
498 if (bSuccess)
499 {
500 MoreItems = true;
501
502 // Get the cached device node
503 DeviceNode = GetDeviceNode(DeviceInfoData.DevInst);
504 if (DeviceNode == nullptr)
505 {
506 DeviceIndex++;
507 continue;
508 }
509
510 // Check if this is a hidden device
511 if (DeviceNode->IsHidden())
512 {
513 // Ignore this device if we aren't displaying hidden devices
514 if (m_ShowHidden == FALSE)
515 {
516 DeviceIndex++;
517 continue;
518 }
519 }
520
521 // We have a device, we need to add the parent if it hasn't yet been added
522 if (AddedParent == false)
523 {
524 // Insert the new class under the root item
525 hTreeItem = InsertIntoTreeView(m_hTreeRoot,
526 ClassNode);
527 AddedParent = true;
528 }
529
530 // Add the device under the class item node
531 (void)InsertIntoTreeView(hTreeItem, DeviceNode);
532
533 // Expand the class if it has a problem device
534 if (DeviceNode->HasProblem())
535 {
536 (void)TreeView_Expand(m_hTreeView,
537 hTreeItem,
538 TVE_EXPAND);
539 }
540 }
541
542 DeviceIndex++;
543
544 } while (MoreItems);
545
546 // If this class has devices, sort them alphabetically
547 if (AddedParent == true)
548 {
549 (void)TreeView_SortChildren(m_hTreeView,
550 hTreeItem,
551 0);
552 }
553 }
554
555 ClassIndex++;
556
557 } while (bClassSuccess);
558
559 // Sort the classes alphabetically
560 (void)TreeView_SortChildren(m_hTreeView,
561 m_hTreeRoot,
562 0);
563
564 // Expand the root item
565 (void)TreeView_Expand(m_hTreeView,
566 m_hTreeRoot,
567 TVE_EXPAND);
568
569 // Pre-select the root item
570 (VOID)TreeView_SelectItem(m_hTreeView,
571 m_hTreeRoot);
572
573 return 0;
574}
575
576bool
577CDeviceView::ListDevicesByConnection()
578{
579 // Walk the device tree and add all the devices
580 (void)RecurseChildDevices(m_RootNode->GetDeviceInst(), m_hTreeRoot);
581
582 // Expand the root item
583 (void)TreeView_Expand(m_hTreeView,
584 m_hTreeRoot,
585 TVE_EXPAND);
586
587 return true;
588}
589
590bool
591CDeviceView::ListResourcesByType()
592{
593 HTREEITEM hMemoryTreeItem = NULL;
594 HTREEITEM hPortTreeItem = NULL;
595 HTREEITEM hDmaTreeItem = NULL;
596 HTREEITEM hIrqTreeItem = NULL;
597
598 CResourceTypeNode *MemoryNode = new CResourceTypeNode(IDS_TYPE_MEMORY, &m_ImageListData);
599 hMemoryTreeItem = InsertIntoTreeView(m_hTreeRoot,
600 MemoryNode);
601
602 CResourceTypeNode *PortNode = new CResourceTypeNode(IDS_TYPE_PORT, &m_ImageListData);
603 hPortTreeItem = InsertIntoTreeView(m_hTreeRoot,
604 PortNode);
605
606 CResourceTypeNode *DmaNode = new CResourceTypeNode(IDS_TYPE_DMA, &m_ImageListData);
607 hDmaTreeItem = InsertIntoTreeView(m_hTreeRoot,
608 DmaNode);
609
610 CResourceTypeNode *IrqNode = new CResourceTypeNode(IDS_TYPE_IRQ, &m_ImageListData);
611 hIrqTreeItem = InsertIntoTreeView(m_hTreeRoot,
612 IrqNode);
613
614 // Walk the device tree and add all the resources
615 (void)RecurseResources(m_RootNode->GetDeviceInst(),
616 hMemoryTreeItem,
617 hPortTreeItem,
618 hDmaTreeItem,
619 hIrqTreeItem);
620
621 // Sort the resource types alphabetically
622 (void)TreeView_SortChildren(m_hTreeView,
623 m_hTreeRoot,
624 0);
625
626 // Expand the root item
627 (void)TreeView_Expand(m_hTreeView,
628 m_hTreeRoot,
629 TVE_EXPAND);
630
631 return true;
632}
633
634
635bool
636CDeviceView::RecurseResources(
637 _In_ DEVINST ParentDevice,
638 _In_ HTREEITEM hMemoryTreeItem,
639 _In_ HTREEITEM hPortTreeItem,
640 _In_ HTREEITEM hDmaTreeItem,
641 _In_ HTREEITEM hIrqTreeItem
642 )
643{
644 DEVINST Device;
645 bool bSuccess;
646 ULONG Index;
647
648 // Check if the parent has any child devices
649 if (GetChildDevice(ParentDevice, &Device) == FALSE)
650 return true;
651
652 // Get the cached device node
653 CDeviceNode *DeviceNode;
654 DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
655 if (DeviceNode == nullptr)
656 {
657 return false;
658 }
659
660 PCM_RESOURCE_LIST pResourceList = (PCM_RESOURCE_LIST)GetResourceList(DeviceNode->GetDeviceId());
661 if (pResourceList)
662 {
663
664 for (Index = 0; Index < pResourceList->List[0].PartialResourceList.Count; Index++)
665 {
666 PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor = &pResourceList->List[0].PartialResourceList.PartialDescriptors[Index];
667
668 if (Descriptor->Type == CmResourceTypeInterrupt)
669 {
670 CResourceNode *resNode = new CResourceNode(DeviceNode, Descriptor, &m_ImageListData);
671 InsertIntoTreeView(hIrqTreeItem, resNode);
672 }
673 else if (Descriptor->Type == CmResourceTypePort)
674 {
675 CResourceNode *resNode = new CResourceNode(DeviceNode, Descriptor, &m_ImageListData);
676 InsertIntoTreeView(hPortTreeItem, resNode);
677 }
678 else if (Descriptor->Type == CmResourceTypeMemory)
679 {
680 CResourceNode *resNode = new CResourceNode(DeviceNode, Descriptor, &m_ImageListData);
681 InsertIntoTreeView(hMemoryTreeItem, resNode);
682 }
683 else if (Descriptor->Type == CmResourceTypeDma)
684 {
685 CResourceNode *resNode = new CResourceNode(DeviceNode, Descriptor, &m_ImageListData);
686 InsertIntoTreeView(hDmaTreeItem, resNode);
687 }
688 }
689
690 HeapFree(GetProcessHeap(), 0, pResourceList);
691 }
692
693 RecurseResources(Device, hMemoryTreeItem, hPortTreeItem, hDmaTreeItem, hIrqTreeItem);
694
695 // Check for siblings
696 for (;;)
697 {
698 // Check if the parent device has anything at the same level
699 bSuccess = GetSiblingDevice(Device, &Device);
700 if (bSuccess == FALSE)
701 break;
702
703 DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
704 if (DeviceNode == nullptr)
705 {
706 continue;
707 }
708
709 PCM_RESOURCE_LIST pResourceList = (PCM_RESOURCE_LIST)GetResourceList(DeviceNode->GetDeviceId());
710 if (pResourceList)
711 {
712 for (Index = 0; Index < pResourceList->List[0].PartialResourceList.Count; Index++)
713 {
714 PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor = &pResourceList->List[0].PartialResourceList.PartialDescriptors[Index];
715
716 if (Descriptor->Type == CmResourceTypeInterrupt)
717 {
718 CResourceNode *resNode = new CResourceNode(DeviceNode, Descriptor, &m_ImageListData);
719 InsertIntoTreeView(hIrqTreeItem, resNode);
720 }
721 else if (Descriptor->Type == CmResourceTypePort)
722 {
723 CResourceNode *resNode = new CResourceNode(DeviceNode, Descriptor, &m_ImageListData);
724 InsertIntoTreeView(hPortTreeItem, resNode);
725 }
726 else if (Descriptor->Type == CmResourceTypeMemory)
727 {
728 CResourceNode *resNode = new CResourceNode(DeviceNode, Descriptor, &m_ImageListData);
729 InsertIntoTreeView(hMemoryTreeItem, resNode);
730 }
731 else if (Descriptor->Type == CmResourceTypeDma)
732 {
733 CResourceNode *resNode = new CResourceNode(DeviceNode, Descriptor, &m_ImageListData);
734 InsertIntoTreeView(hDmaTreeItem, resNode);
735 }
736 }
737
738 HeapFree(GetProcessHeap(), 0, pResourceList);
739 }
740
741 RecurseResources(Device, hMemoryTreeItem, hPortTreeItem, hDmaTreeItem, hIrqTreeItem);
742 }
743
744 (void)TreeView_SortChildren(m_hTreeView,
745 hMemoryTreeItem,
746 0);
747
748 (void)TreeView_SortChildren(m_hTreeView,
749 hPortTreeItem,
750 0);
751
752 (void)TreeView_SortChildren(m_hTreeView,
753 hIrqTreeItem,
754 0);
755
756 (void)TreeView_SortChildren(m_hTreeView,
757 hDmaTreeItem,
758 0);
759
760 return true;
761}
762
763
764bool
765CDeviceView::RecurseChildDevices(
766 _In_ DEVINST ParentDevice,
767 _In_ HTREEITEM hParentTreeItem
768 )
769{
770 HTREEITEM hDevItem = NULL;
771 DEVINST Device;
772 bool HasProblem = false;
773 bool bSuccess;
774
775 // Check if the parent has any child devices
776 if (GetChildDevice(ParentDevice, &Device) == FALSE)
777 return true;
778
779 // Get the cached device node
780 CDeviceNode *DeviceNode;
781 DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
782 if (DeviceNode == nullptr)
783 {
784 return false;
785 }
786
787 // Don't show hidden devices if not requested
788 if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
789 {
790 // Add this device to the tree under its parent
791 hDevItem = InsertIntoTreeView(hParentTreeItem,
792 DeviceNode);
793 if (hDevItem)
794 {
795 // Check if this child has any children itself
796 if (!RecurseChildDevices(Device, hDevItem))
797 HasProblem = true;
798 }
799
800 if (DeviceNode->HasProblem())
801 {
802 HasProblem = true;
803 }
804 }
805
806
807 // Check for siblings
808 for (;;)
809 {
810 // Check if the parent device has anything at the same level
811 bSuccess = GetSiblingDevice(Device, &Device);
812 if (bSuccess == FALSE)
813 break;
814
815 DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
816 if (DeviceNode == nullptr)
817 {
818 continue;
819 }
820
821 // Don't show hidden devices if not requested
822 if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
823 {
824 if (DeviceNode->HasProblem())
825 {
826 HasProblem = true;
827 }
828
829 // Add this device to the tree under its parent
830 hDevItem = InsertIntoTreeView(hParentTreeItem,
831 DeviceNode);
832 if (hDevItem)
833 {
834 // Check if this child has any children itself
835 if (!RecurseChildDevices(Device, hDevItem))
836 HasProblem = true;
837 }
838 }
839 }
840
841 (void)TreeView_SortChildren(m_hTreeView,
842 hParentTreeItem,
843 0);
844
845 // Expand the class if it has a problem device
846 if (HasProblem == true)
847 {
848 (void)TreeView_Expand(m_hTreeView,
849 hParentTreeItem,
850 TVE_EXPAND);
851 }
852
853 // If there was a problem, expand the ancestors
854 if (HasProblem)
855 return false;
856
857 return true;
858}
859
860bool
861CDeviceView::EnableSelectedDevice(
862 _In_ bool Enable,
863 _Out_ bool &NeedsReboot
864 )
865{
866 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
867 if (Node == nullptr)
868 return false;
869
870 if (Enable == false)
871 {
872 CAtlStringW str;
873 if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_DISABLE))
874 {
875 if (MessageBoxW(m_hMainWnd,
876 str,
877 Node->GetDisplayName(),
878 MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES)
879 {
880 return false;
881 }
882 }
883 }
884
885 return Node->EnableDevice(Enable, NeedsReboot);
886}
887
888bool
889CDeviceView::UpdateSelectedDevice(
890 _Out_ bool &NeedsReboot
891 )
892{
893 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
894 if (Node == nullptr)
895 return false;
896
897 DWORD dwReboot;
898 if (InstallDevInst(m_hMainWnd, Node->GetDeviceId(), TRUE, &dwReboot))
899 {
900 NeedsReboot = false;
901 return true;
902 }
903
904 return false;
905}
906
907bool
908CDeviceView::UninstallSelectedDevice(
909 )
910{
911 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
912 if (Node == nullptr)
913 return false;
914
915 CAtlStringW str;
916 if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_UNINSTALL))
917 {
918 if (MessageBoxW(m_hMainWnd,
919 str,
920 Node->GetDisplayName(),
921 MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES)
922 {
923 return false;
924 }
925 }
926
927 return Node->UninstallDevice();
928}
929
930bool
931CDeviceView::RunAddHardwareWizard()
932{
933 PADDHARDWAREWIZARD pAddHardwareWizard;
934 HMODULE hModule;
935
936 hModule = LoadLibraryW(L"hdwwiz.cpl");
937 if (hModule == NULL)
938 return false;
939
940 pAddHardwareWizard = (PADDHARDWAREWIZARD)GetProcAddress(hModule,
941 "AddHardwareWizard");
942 if (pAddHardwareWizard == NULL)
943 {
944 FreeLibrary(hModule);
945 return false;
946 }
947
948 pAddHardwareWizard(m_hMainWnd, NULL);
949
950 FreeLibrary(hModule);
951 return true;
952}
953
954bool
955CDeviceView::GetChildDevice(
956 _In_ DEVINST ParentDevInst,
957 _Out_ PDEVINST DevInst
958)
959{
960 CONFIGRET cr;
961 cr = CM_Get_Child(DevInst,
962 ParentDevInst,
963 0);
964 return (cr == CR_SUCCESS);
965}
966
967bool
968CDeviceView::GetSiblingDevice(
969 _In_ DEVINST PrevDevice,
970 _Out_ PDEVINST DevInst
971)
972{
973 CONFIGRET cr;
974 cr = CM_Get_Sibling(DevInst,
975 PrevDevice,
976 0);
977 return (cr == CR_SUCCESS);
978}
979
980HTREEITEM
981CDeviceView::InsertIntoTreeView(
982 _In_opt_ HTREEITEM hParent,
983 _In_ CNode *Node
984 )
985{
986 LPWSTR lpLabel;
987 lpLabel = Node->GetDisplayName();
988
989 TV_ITEMW tvi;
990 TV_INSERTSTRUCT tvins;
991 ZeroMemory(&tvi, sizeof(tvi));
992 ZeroMemory(&tvins, sizeof(tvins));
993
994 tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
995 tvi.pszText = lpLabel;
996 tvi.cchTextMax = wcslen(lpLabel);
997 tvi.lParam = (LPARAM)Node;
998 tvi.iImage = Node->GetClassImage();
999 tvi.iSelectedImage = Node->GetClassImage();
1000
1001 // try to cast it to a device node. This will only succeed if it's the correct type
1002 CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node);
1003 if (DeviceNode && DeviceNode->GetOverlayImage())
1004 {
1005 tvi.mask |= TVIF_STATE;
1006 tvi.stateMask = TVIS_OVERLAYMASK;
1007 tvi.state = INDEXTOOVERLAYMASK(DeviceNode->GetOverlayImage());
1008 }
1009
1010 tvins.item = tvi;
1011 tvins.hParent = hParent;
1012
1013 return TreeView_InsertItem(m_hTreeView, &tvins);
1014}
1015
1016void
1017CDeviceView::BuildActionMenuForNode(
1018 _In_ HMENU OwnerMenu,
1019 _In_ CNode *Node,
1020 _In_ bool MainMenu
1021 )
1022{
1023 // Create a separator structure
1024 MENUITEMINFOW MenuSeparator = { 0 };
1025 MenuSeparator.cbSize = sizeof(MENUITEMINFOW);
1026 MenuSeparator.fType = MFT_SEPARATOR;
1027
1028 // Setup the
1029 MENUITEMINFOW MenuItemInfo = { 0 };
1030 MenuItemInfo.cbSize = sizeof(MENUITEMINFOW);
1031 MenuItemInfo.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;
1032 MenuItemInfo.fType = MFT_STRING;
1033
1034 CAtlStringW String;
1035 int i = 0;
1036
1037 // Device nodes have extra data
1038 if (Node->GetNodeType() == DeviceNode)
1039 {
1040 CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node);
1041
1042 if (DeviceNode->CanUpdate())
1043 {
1044 String.LoadStringW(g_hThisInstance, IDS_MENU_UPDATE);
1045 MenuItemInfo.wID = IDM_UPDATE_DRV;
1046 MenuItemInfo.dwTypeData = String.GetBuffer();
1047 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
1048 i++;
1049 }
1050
1051 if (DeviceNode->IsDisabled())
1052 {
1053 String.LoadStringW(g_hThisInstance, IDS_MENU_ENABLE);
1054 MenuItemInfo.wID = IDM_ENABLE_DRV;
1055 MenuItemInfo.dwTypeData = String.GetBuffer();
1056 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
1057 i++;
1058 }
1059
1060 if (DeviceNode->CanDisable() && !DeviceNode->IsDisabled())
1061 {
1062 String.LoadStringW(g_hThisInstance, IDS_MENU_DISABLE);
1063 MenuItemInfo.wID = IDM_DISABLE_DRV;
1064 MenuItemInfo.dwTypeData = String.GetBuffer();
1065 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
1066 i++;
1067 }
1068
1069 if (DeviceNode->CanUninstall())
1070 {
1071 String.LoadStringW(g_hThisInstance, IDS_MENU_UNINSTALL);
1072 MenuItemInfo.wID = IDM_UNINSTALL_DRV;
1073 MenuItemInfo.dwTypeData = String.GetBuffer();
1074 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
1075 i++;
1076 }
1077
1078 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator);
1079 i++;
1080 }
1081
1082 // All nodes have the scan option
1083 String.LoadStringW(g_hThisInstance, IDS_MENU_SCAN);
1084 MenuItemInfo.wID = IDM_SCAN_HARDWARE;
1085 MenuItemInfo.dwTypeData = String.GetBuffer();
1086 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
1087 i++;
1088
1089 if ((Node->GetNodeType() == RootNode) || (MainMenu == true))
1090 {
1091 String.LoadStringW(g_hThisInstance, IDS_MENU_ADD);
1092 MenuItemInfo.wID = IDM_ADD_HARDWARE;
1093 MenuItemInfo.dwTypeData = String.GetBuffer();
1094 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
1095 i++;
1096 }
1097
1098 if (Node->HasProperties())
1099 {
1100 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator);
1101 i++;
1102
1103 String.LoadStringW(g_hThisInstance, IDS_MENU_PROPERTIES);
1104 MenuItemInfo.wID = IDM_PROPERTIES;
1105 MenuItemInfo.dwTypeData = String.GetBuffer();
1106 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
1107 i++;
1108
1109 SetMenuDefaultItem(OwnerMenu, IDC_PROPERTIES, FALSE);
1110 }
1111}
1112
1113HTREEITEM
1114CDeviceView::RecurseFindDevice(
1115 _In_ HTREEITEM hParentItem,
1116 _In_ CNode *Node
1117 )
1118{
1119 HTREEITEM FoundItem;
1120 HTREEITEM hItem;
1121 TVITEMW tvItem;
1122 CNode *FoundNode;
1123
1124 // Check if this node has any children
1125 hItem = TreeView_GetChild(m_hTreeView, hParentItem);
1126 if (hItem == NULL)
1127 return NULL;
1128
1129 // The lParam contains the node pointer data
1130 tvItem.hItem = hItem;
1131 tvItem.mask = TVIF_PARAM;
1132 if (TreeView_GetItem(m_hTreeView, &tvItem) &&
1133 tvItem.lParam != NULL)
1134 {
1135 // check for a matching node
1136 FoundNode = reinterpret_cast<CNode *>(tvItem.lParam);
1137 if ((FoundNode->GetNodeType() == Node->GetNodeType()) &&
1138 (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid())))
1139 {
1140 // check if this is a class node, or a device with matching ID's
1141 if ((FoundNode->GetNodeType() == ClassNode) ||
1142 (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0))
1143 {
1144 return hItem;
1145 }
1146 }
1147 }
1148
1149 // This node may have its own children
1150 FoundItem = RecurseFindDevice(hItem, Node);
1151 if (FoundItem)
1152 return FoundItem;
1153
1154 // Loop all the siblings
1155 for (;;)
1156 {
1157 // Get the next item at this level
1158 hItem = TreeView_GetNextSibling(m_hTreeView, hItem);
1159 if (hItem == NULL)
1160 break;
1161
1162 // The lParam contains the node pointer data
1163 tvItem.hItem = hItem;
1164 tvItem.mask = TVIF_PARAM;
1165 if (TreeView_GetItem(m_hTreeView, &tvItem))
1166 {
1167 // check for a matching class
1168 FoundNode = reinterpret_cast<CNode *>(tvItem.lParam);
1169 if ((FoundNode->GetNodeType() == Node->GetNodeType()) &&
1170 (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid())))
1171 {
1172 // check if this is a class node, or a device with matching ID's
1173 if ((FoundNode->GetNodeType() == ClassNode) ||
1174 (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0))
1175 {
1176 return hItem;
1177 }
1178 }
1179 }
1180
1181 // This node may have its own children
1182 FoundItem = RecurseFindDevice(hItem, Node);
1183 if (FoundItem)
1184 return FoundItem;
1185 }
1186
1187 return hItem;
1188}
1189
1190void
1191CDeviceView::SelectNode(
1192 _In_ CNode *Node
1193 )
1194{
1195 HTREEITEM hRoot, hItem;
1196
1197 // Check if there are any items in the tree
1198 hRoot = TreeView_GetRoot(m_hTreeView);
1199 if (hRoot == NULL)
1200 return;
1201
1202 // If we don't want to set select a node, just select root
1203 if (Node == nullptr || Node->GetNodeType() == RootNode)
1204 {
1205 TreeView_SelectItem(m_hTreeView, hRoot);
1206 return;
1207 }
1208
1209 // Scan the tree looking for the node we want
1210 hItem = RecurseFindDevice(hRoot, Node);
1211 if (hItem)
1212 {
1213 TreeView_SelectItem(m_hTreeView, hItem);
1214 }
1215 else
1216 {
1217 TreeView_SelectItem(m_hTreeView, hRoot);
1218 }
1219}
1220
1221
1222void
1223CDeviceView::EmptyDeviceView()
1224{
1225 (VOID)TreeView_DeleteAllItems(m_hTreeView);
1226}
1227
1228
1229CClassNode*
1230CDeviceView::GetClassNode(
1231 _In_ LPGUID ClassGuid
1232 )
1233{
1234 POSITION Pos;
1235 CClassNode *Node = nullptr;
1236
1237 Pos = m_ClassNodeList.GetHeadPosition();
1238 if (Pos == NULL)
1239 return nullptr;
1240
1241 do
1242 {
1243 Node = m_ClassNodeList.GetNext(Pos);
1244 if (IsEqualGUID(*Node->GetClassGuid(), *ClassGuid))
1245 {
1246 ATLASSERT(Node->GetNodeType() == ClassNode);
1247 break;
1248 }
1249
1250 Node = nullptr;
1251
1252 } while (Pos != NULL);
1253
1254 return Node;
1255}
1256
1257CDeviceNode*
1258CDeviceView::GetDeviceNode(
1259 _In_ DEVINST Device
1260 )
1261{
1262 POSITION Pos;
1263 CDeviceNode *Node = nullptr;
1264
1265 Pos = m_DeviceNodeList.GetHeadPosition();
1266 if (Pos == NULL)
1267 return nullptr;
1268
1269 do
1270 {
1271 Node = m_DeviceNodeList.GetNext(Pos);
1272 if (Node->GetDeviceInst() == Device)
1273 {
1274 ATLASSERT(Node->GetNodeType() == DeviceNode);
1275 break;
1276 }
1277
1278 Node = nullptr;
1279
1280 } while (Pos != NULL);
1281
1282 return Node;
1283}
1284
1285CNode* CDeviceView::GetNode(
1286 _In_ LPTV_ITEMW TvItem
1287 )
1288{
1289 TvItem->mask = TVIF_PARAM;
1290 if (TreeView_GetItem(m_hTreeView, TvItem))
1291 {
1292 return (CNode *)TvItem->lParam;
1293 }
1294 return nullptr;
1295}
1296
1297void
1298CDeviceView::EmptyLists()
1299{
1300 CNode *Node;
1301
1302 while (!m_ClassNodeList.IsEmpty())
1303 {
1304 Node = m_ClassNodeList.RemoveTail();
1305 delete Node;
1306 }
1307
1308 while (!m_DeviceNodeList.IsEmpty())
1309 {
1310 Node = m_DeviceNodeList.RemoveTail();
1311 delete Node;
1312 }
1313}
1314
1315bool
1316CDeviceView::RefreshDeviceList()
1317{
1318 GUID ClassGuid;
1319 CClassNode *ClassNode;
1320 CDeviceNode *DeviceNode;
1321 HDEVINFO hDevInfo;
1322 SP_DEVINFO_DATA DeviceInfoData;
1323 DWORD i;
1324 BOOL Success;
1325
1326 ULONG ClassIndex = 0;
1327
1328 EmptyLists();
1329
1330 if (m_RootNode) delete m_RootNode;
1331 m_RootNode = new CRootNode(&m_ImageListData);
1332 m_RootNode->SetupNode();
1333
1334 // Loop through all the classes
1335 do
1336 {
1337 Success = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
1338 if (Success)
1339 {
1340 // Create a new class node and add it to the list
1341 ClassNode = new CClassNode(&ClassGuid, &m_ImageListData);
1342 if (ClassNode->SetupNode())
1343 {
1344 m_ClassNodeList.AddTail(ClassNode);
1345 }
1346
1347 SetupDiDestroyDeviceInfoList(hDevInfo);
1348 }
1349 ClassIndex++;
1350 } while (Success);
1351
1352 // Get all the devices on the local machine
1353 hDevInfo = SetupDiGetClassDevsW(NULL,
1354 0,
1355 0,
1356 DIGCF_PRESENT | DIGCF_ALLCLASSES);
1357 if (hDevInfo == INVALID_HANDLE_VALUE)
1358 {
1359 return false;
1360 }
1361
1362 // loop though all the devices
1363 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1364 for (i = 0;; i++)
1365 {
1366 // Get the devinst for this device
1367 Success = SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData);
1368 if (Success == FALSE)
1369 break;
1370
1371 // create a new device node and add it to the list
1372 DeviceNode = new CDeviceNode(DeviceInfoData.DevInst, &m_ImageListData);
1373 if (DeviceNode->SetupNode())
1374 {
1375 m_DeviceNodeList.AddTail(DeviceNode);
1376 }
1377 else
1378 {
1379 ATLASSERT(FALSE);
1380 }
1381 }
1382
1383 SetupDiDestroyDeviceInfoList(hDevInfo);
1384
1385 return TRUE;
1386}