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