Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / 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)
60 return false;
61
62 // Create the main treeview
63 m_hTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
64 WC_TREEVIEW,
65 NULL,
66 WS_CHILD | WS_VISIBLE | WS_BORDER | TVS_HASLINES |
67 TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_LINESATROOT,
68 0, 0, 0, 0,
69 m_hMainWnd,
70 (HMENU)IDC_TREEVIEW,
71 g_hThisInstance,
72 NULL);
73 if (m_hTreeView)
74 {
75 // Set the image list against the treeview
76 (void)TreeView_SetImageList(m_hTreeView,
77 m_ImageListData.ImageList,
78 TVSIL_NORMAL);
79
80 // Give the treeview arrows instead of +/- boxes (on Win7)
81 SetWindowTheme(m_hTreeView, L"explorer", NULL);
82
83 // Create the root node
84 m_RootNode = new CRootNode(&m_ImageListData);
85 m_RootNode->SetupNode();
86 }
87
88
89
90 return !!(m_hTreeView);
91 }
92
93 bool
94 CDeviceView::Uninitialize()
95 {
96 EmptyDeviceView();
97
98 if (m_ImageListData.ImageList != NULL)
99 {
100 SetupDiDestroyClassImageList(&m_ImageListData);
101 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
102 }
103
104 return true;
105 }
106
107 LRESULT
108 CDeviceView::OnSize(
109 _In_ int x,
110 _In_ int y,
111 _In_ int cx,
112 _In_ int cy
113 )
114 {
115 // Resize the treeview
116 SetWindowPos(m_hTreeView,
117 NULL,
118 x,
119 y,
120 cx,
121 cy,
122 SWP_NOZORDER);
123
124 return 0;
125 }
126
127 LRESULT
128 CDeviceView::OnRightClick(
129 _In_ LPNMHDR NmHdr
130 )
131 {
132 TVHITTESTINFO hitInfo;
133 HTREEITEM hItem;
134
135 GetCursorPos(&hitInfo.pt);
136 ScreenToClient(m_hTreeView, &hitInfo.pt);
137
138 hItem = TreeView_HitTest(m_hTreeView, &hitInfo);
139 if (hItem != NULL && (hitInfo.flags & (TVHT_ONITEM | TVHT_ONITEMICON)))
140 {
141 TreeView_SelectItem(m_hTreeView, hItem);
142 }
143
144 return 0;
145 }
146
147 LRESULT
148 CDeviceView::OnContextMenu(
149 _In_ LPARAM lParam
150 )
151 {
152 HTREEITEM hSelected = TreeView_GetSelection(m_hTreeView);
153
154 RECT rc;
155 if (TreeView_GetItemRect(m_hTreeView,
156 hSelected,
157 &rc,
158 TRUE))
159 {
160 POINT pt;
161 if (GetCursorPos(&pt) &&
162 ScreenToClient(m_hTreeView, &pt) &&
163 PtInRect(&rc, pt))
164 {
165 CNode *Node = GetSelectedNode();
166 if (Node)
167 {
168 // Create the context menu
169 HMENU hContextMenu = CreatePopupMenu();
170
171 // Add the actions for this node
172 BuildActionMenuForNode(hContextMenu, Node, false);
173
174 INT xPos = GET_X_LPARAM(lParam);
175 INT yPos = GET_Y_LPARAM(lParam);
176
177 // Display the menu
178 TrackPopupMenuEx(hContextMenu,
179 TPM_RIGHTBUTTON,
180 xPos,
181 yPos,
182 m_hMainWnd,
183 NULL);
184
185 DestroyMenu(hContextMenu);
186 }
187 }
188 }
189
190 return 0;
191 }
192
193
194 void
195 CDeviceView::Refresh(
196 _In_ ViewType Type,
197 _In_ bool ScanForChanges,
198 _In_ bool UpdateView
199 )
200 {
201 // Enum devices on a separate thread to keep the gui responsive
202
203 m_ViewType = Type;
204
205 RefreshThreadData *ThreadData;
206 ThreadData = new RefreshThreadData;
207 ThreadData->This = this;
208 ThreadData->ScanForChanges = ScanForChanges;
209 ThreadData->UpdateView = UpdateView;
210
211 HANDLE hThread;
212 hThread = (HANDLE)_beginthreadex(NULL,
213 0,
214 RefreshThread,
215 ThreadData,
216 0,
217 NULL);
218 if (hThread) CloseHandle(hThread);
219 }
220
221 LRESULT
222 CDeviceView::OnAction(
223 _In_ UINT Action
224 )
225 {
226 switch (Action)
227 {
228 case 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)
352 return false;
353
354 // Check if this is the unknown class
355 if (IsEqualGUID(*ClassGuid, GUID_DEVCLASS_UNKNOWN))
356 {
357 // Get device info for all devices
358 *hDevInfo = SetupDiGetClassDevsW(NULL,
359 NULL,
360 NULL,
361 DIGCF_ALLCLASSES);
362 }
363 else
364 {
365 // We only want the devices for this class
366 *hDevInfo = SetupDiGetClassDevsW(ClassGuid,
367 NULL,
368 NULL,
369 DIGCF_PRESENT);
370 }
371
372 return (hDevInfo != INVALID_HANDLE_VALUE);
373 }
374
375 unsigned int __stdcall CDeviceView::RefreshThread(void *Param)
376 {
377 RefreshThreadData *ThreadData = (RefreshThreadData *)Param;
378 CDeviceView *This = ThreadData->This;
379
380 // Get a copy of the currently selected node
381 CNode *LastSelectedNode = This->GetSelectedNode();
382 if (LastSelectedNode == nullptr || (LastSelectedNode->GetNodeType() == RootNode))
383 {
384 LastSelectedNode = new CRootNode(*This->m_RootNode);
385 }
386 else if (LastSelectedNode->GetNodeType() == ClassNode)
387 {
388 LastSelectedNode = new CClassNode(*dynamic_cast<CClassNode *>(LastSelectedNode));
389 }
390 else if (LastSelectedNode->GetNodeType() == DeviceNode)
391 {
392 LastSelectedNode = new CDeviceNode(*dynamic_cast<CDeviceNode *>(LastSelectedNode));
393 }
394
395 // Empty the treeview
396 This->EmptyDeviceView();
397
398 // Re-add the root node to the tree
399 if (This->AddRootDevice() == false)
400 return 0;
401
402 // Refresh the devices only if requested
403 if (ThreadData->ScanForChanges)
404 {
405 This->RefreshDeviceList();
406 }
407
408 // display the type of view the user wants
409 switch (This->m_ViewType)
410 {
411 case DevicesByType:
412 (void)This->ListDevicesByType();
413 break;
414
415 case DevicesByConnection:
416 (VOID)This->ListDevicesByConnection();
417 break;
418
419 case ResourcesByType:
420 break;
421
422 case ResourcesByConnection:
423 break;
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)
649 break;
650
651 DeviceNode = dynamic_cast<CDeviceNode *>(GetDeviceNode(Device));
652 if (DeviceNode == nullptr)
653 {
654 ATLASSERT(FALSE);
655 }
656
657 // Don't show hidden devices if not requested
658 if ((m_ShowHidden == TRUE) || (!(DeviceNode->IsHidden())))
659 {
660 if (DeviceNode->HasProblem())
661 {
662 HasProblem = true;
663 }
664
665 // Add this device to the tree under its parent
666 hDevItem = InsertIntoTreeView(hParentTreeItem,
667 DeviceNode);
668 if (hDevItem)
669 {
670 // Check if this child has any children itself
671 if (!RecurseChildDevices(Device, hDevItem))
672 HasProblem = true;
673 }
674 }
675 }
676
677 (void)TreeView_SortChildren(m_hTreeView,
678 hParentTreeItem,
679 0);
680
681 // Expand the class if it has a problem device
682 if (HasProblem == true)
683 {
684 (void)TreeView_Expand(m_hTreeView,
685 hParentTreeItem,
686 TVE_EXPAND);
687 }
688
689 // If there was a problem, expand the ancestors
690 if (HasProblem)
691 return false;
692
693 return true;
694 }
695
696 bool
697 CDeviceView::EnableSelectedDevice(
698 _In_ bool Enable,
699 _Out_ bool &NeedsReboot
700 )
701 {
702 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
703 if (Node == nullptr)
704 return false;
705
706 if (Enable == false)
707 {
708 CAtlStringW str;
709 if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_DISABLE))
710 {
711 if (MessageBoxW(m_hMainWnd,
712 str,
713 Node->GetDisplayName(),
714 MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES)
715 {
716 return false;
717 }
718 }
719 }
720
721 return Node->EnableDevice(Enable, NeedsReboot);
722 }
723
724 bool
725 CDeviceView::UpdateSelectedDevice(
726 _Out_ bool &NeedsReboot
727 )
728 {
729 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
730 if (Node == nullptr)
731 return false;
732
733 DWORD dwReboot;
734 if (InstallDevInst(m_hMainWnd, Node->GetDeviceId(), TRUE, &dwReboot))
735 {
736 NeedsReboot = false;
737 return true;
738 }
739
740 return false;
741 }
742
743 bool
744 CDeviceView::UninstallSelectedDevice(
745 )
746 {
747 CDeviceNode *Node = dynamic_cast<CDeviceNode *>(GetSelectedNode());
748 if (Node == nullptr)
749 return false;
750
751 CAtlStringW str;
752 if (str.LoadStringW(g_hThisInstance, IDS_CONFIRM_UNINSTALL))
753 {
754 if (MessageBoxW(m_hMainWnd,
755 str,
756 Node->GetDisplayName(),
757 MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES)
758 {
759 return false;
760 }
761 }
762
763 return Node->UninstallDevice();
764 }
765
766 bool
767 CDeviceView::RunAddHardwareWizard()
768 {
769 PADDHARDWAREWIZARD pAddHardwareWizard;
770 HMODULE hModule;
771
772 hModule = LoadLibraryW(L"hdwwiz.cpl");
773 if (hModule == NULL)
774 return false;
775
776 pAddHardwareWizard = (PADDHARDWAREWIZARD)GetProcAddress(hModule,
777 "AddHardwareWizard");
778 if (pAddHardwareWizard == NULL)
779 {
780 FreeLibrary(hModule);
781 return false;
782 }
783
784 pAddHardwareWizard(m_hMainWnd, NULL);
785
786 FreeLibrary(hModule);
787 return true;
788 }
789
790 bool
791 CDeviceView::GetChildDevice(
792 _In_ DEVINST ParentDevInst,
793 _Out_ PDEVINST DevInst
794 )
795 {
796 CONFIGRET cr;
797 cr = CM_Get_Child(DevInst,
798 ParentDevInst,
799 0);
800 return (cr == CR_SUCCESS);
801 }
802
803 bool
804 CDeviceView::GetSiblingDevice(
805 _In_ DEVINST PrevDevice,
806 _Out_ PDEVINST DevInst
807 )
808 {
809 CONFIGRET cr;
810 cr = CM_Get_Sibling(DevInst,
811 PrevDevice,
812 0);
813 return (cr == CR_SUCCESS);
814 }
815
816 HTREEITEM
817 CDeviceView::InsertIntoTreeView(
818 _In_opt_ HTREEITEM hParent,
819 _In_ CNode *Node
820 )
821 {
822 LPWSTR lpLabel;
823 lpLabel = Node->GetDisplayName();
824
825 TV_ITEMW tvi;
826 TV_INSERTSTRUCT tvins;
827 ZeroMemory(&tvi, sizeof(tvi));
828 ZeroMemory(&tvins, sizeof(tvins));
829
830 tvi.mask = TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
831 tvi.pszText = lpLabel;
832 tvi.cchTextMax = wcslen(lpLabel);
833 tvi.lParam = (LPARAM)Node;
834 tvi.iImage = Node->GetClassImage();
835 tvi.iSelectedImage = Node->GetClassImage();
836
837 // try to cast it to a device node. This will only succeed if it's the correct type
838 CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node);
839 if (DeviceNode && DeviceNode->GetOverlayImage())
840 {
841 tvi.mask |= TVIF_STATE;
842 tvi.stateMask = TVIS_OVERLAYMASK;
843 tvi.state = INDEXTOOVERLAYMASK(DeviceNode->GetOverlayImage());
844 }
845
846 tvins.item = tvi;
847 tvins.hParent = hParent;
848
849 return TreeView_InsertItem(m_hTreeView, &tvins);
850 }
851
852 void
853 CDeviceView::BuildActionMenuForNode(
854 _In_ HMENU OwnerMenu,
855 _In_ CNode *Node,
856 _In_ bool MainMenu
857 )
858 {
859 // Create a separator structure
860 MENUITEMINFOW MenuSeparator = { 0 };
861 MenuSeparator.cbSize = sizeof(MENUITEMINFOW);
862 MenuSeparator.fType = MFT_SEPARATOR;
863
864 // Setup the
865 MENUITEMINFOW MenuItemInfo = { 0 };
866 MenuItemInfo.cbSize = sizeof(MENUITEMINFOW);
867 MenuItemInfo.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;
868 MenuItemInfo.fType = MFT_STRING;
869
870 CAtlStringW String;
871 int i = 0;
872
873 // Device nodes have extra data
874 if (Node->GetNodeType() == DeviceNode)
875 {
876 CDeviceNode *DeviceNode = dynamic_cast<CDeviceNode *>(Node);
877
878 if (DeviceNode->CanUpdate())
879 {
880 String.LoadStringW(g_hThisInstance, IDS_MENU_UPDATE);
881 MenuItemInfo.wID = IDC_UPDATE_DRV;
882 MenuItemInfo.dwTypeData = String.GetBuffer();
883 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
884 i++;
885 }
886
887 if (DeviceNode->IsDisabled())
888 {
889 String.LoadStringW(g_hThisInstance, IDS_MENU_ENABLE);
890 MenuItemInfo.wID = IDC_ENABLE_DRV;
891 MenuItemInfo.dwTypeData = String.GetBuffer();
892 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
893 i++;
894 }
895
896 if (DeviceNode->CanDisable() && !DeviceNode->IsDisabled())
897 {
898 String.LoadStringW(g_hThisInstance, IDS_MENU_DISABLE);
899 MenuItemInfo.wID = IDC_DISABLE_DRV;
900 MenuItemInfo.dwTypeData = String.GetBuffer();
901 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
902 i++;
903 }
904
905 if (DeviceNode->CanUninstall())
906 {
907 String.LoadStringW(g_hThisInstance, IDS_MENU_UNINSTALL);
908 MenuItemInfo.wID = IDC_UNINSTALL_DRV;
909 MenuItemInfo.dwTypeData = String.GetBuffer();
910 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
911 i++;
912 }
913
914 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator);
915 i++;
916 }
917
918 // All nodes have the scan option
919 String.LoadStringW(g_hThisInstance, IDS_MENU_SCAN);
920 MenuItemInfo.wID = IDC_SCAN_HARDWARE;
921 MenuItemInfo.dwTypeData = String.GetBuffer();
922 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
923 i++;
924
925 if ((Node->GetNodeType() == RootNode) || (MainMenu == true))
926 {
927 String.LoadStringW(g_hThisInstance, IDS_MENU_ADD);
928 MenuItemInfo.wID = IDC_ADD_HARDWARE;
929 MenuItemInfo.dwTypeData = String.GetBuffer();
930 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
931 i++;
932 }
933
934 if (Node->HasProperties())
935 {
936 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuSeparator);
937 i++;
938
939 String.LoadStringW(g_hThisInstance, IDS_MENU_PROPERTIES);
940 MenuItemInfo.wID = IDC_PROPERTIES;
941 MenuItemInfo.dwTypeData = String.GetBuffer();
942 InsertMenuItemW(OwnerMenu, i, TRUE, &MenuItemInfo);
943 i++;
944
945 SetMenuDefaultItem(OwnerMenu, IDC_PROPERTIES, FALSE);
946 }
947 }
948
949 HTREEITEM
950 CDeviceView::RecurseFindDevice(
951 _In_ HTREEITEM hParentItem,
952 _In_ CNode *Node
953 )
954 {
955 HTREEITEM FoundItem;
956 HTREEITEM hItem;
957 TVITEMW tvItem;
958 CNode *FoundNode;
959
960 // Check if this node has any children
961 hItem = TreeView_GetChild(m_hTreeView, hParentItem);
962 if (hItem == NULL)
963 return NULL;
964
965 // The lParam contains the node pointer data
966 tvItem.hItem = hItem;
967 tvItem.mask = TVIF_PARAM;
968 if (TreeView_GetItem(m_hTreeView, &tvItem) &&
969 tvItem.lParam != NULL)
970 {
971 // check for a matching node
972 FoundNode = reinterpret_cast<CNode *>(tvItem.lParam);
973 if ((FoundNode->GetNodeType() == Node->GetNodeType()) &&
974 (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid())))
975 {
976 // check if this is a class node, or a device with matching ID's
977 if ((FoundNode->GetNodeType() == ClassNode) ||
978 (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0))
979 {
980 return hItem;
981 }
982 }
983 }
984
985 // This node may have its own children
986 FoundItem = RecurseFindDevice(hItem, Node);
987 if (FoundItem)
988 return FoundItem;
989
990 // Loop all the siblings
991 for (;;)
992 {
993 // Get the next item at this level
994 hItem = TreeView_GetNextSibling(m_hTreeView, hItem);
995 if (hItem == NULL)
996 break;
997
998 // The lParam contains the node pointer data
999 tvItem.hItem = hItem;
1000 tvItem.mask = TVIF_PARAM;
1001 if (TreeView_GetItem(m_hTreeView, &tvItem))
1002 {
1003 // check for a matching class
1004 FoundNode = reinterpret_cast<CNode *>(tvItem.lParam);
1005 if ((FoundNode->GetNodeType() == Node->GetNodeType()) &&
1006 (IsEqualGUID(*FoundNode->GetClassGuid(), *Node->GetClassGuid())))
1007 {
1008 // check if this is a class node, or a device with matching ID's
1009 if ((FoundNode->GetNodeType() == ClassNode) ||
1010 (wcscmp(FoundNode->GetDeviceId(), Node->GetDeviceId()) == 0))
1011 {
1012 return hItem;
1013 }
1014 }
1015 }
1016
1017 // This node may have its own children
1018 FoundItem = RecurseFindDevice(hItem, Node);
1019 if (FoundItem)
1020 return FoundItem;
1021 }
1022
1023 return hItem;
1024 }
1025
1026 void
1027 CDeviceView::SelectNode(
1028 _In_ CNode *Node
1029 )
1030 {
1031 HTREEITEM hRoot, hItem;
1032
1033 // Check if there are any items in the tree
1034 hRoot = TreeView_GetRoot(m_hTreeView);
1035 if (hRoot == NULL)
1036 return;
1037
1038 // If we don't want to set select a node, just select root
1039 if (Node == nullptr || Node->GetNodeType() == RootNode)
1040 {
1041 TreeView_SelectItem(m_hTreeView, hRoot);
1042 return;
1043 }
1044
1045 // Scan the tree looking for the node we want
1046 hItem = RecurseFindDevice(hRoot, Node);
1047 if (hItem)
1048 {
1049 TreeView_SelectItem(m_hTreeView, hItem);
1050 }
1051 else
1052 {
1053 TreeView_SelectItem(m_hTreeView, hRoot);
1054 }
1055 }
1056
1057
1058 void
1059 CDeviceView::EmptyDeviceView()
1060 {
1061 (VOID)TreeView_DeleteAllItems(m_hTreeView);
1062 }
1063
1064
1065 CClassNode*
1066 CDeviceView::GetClassNode(
1067 _In_ LPGUID ClassGuid
1068 )
1069 {
1070 POSITION Pos;
1071 CClassNode *Node = nullptr;
1072
1073 Pos = m_ClassNodeList.GetHeadPosition();
1074 if (Pos == NULL)
1075 return nullptr;
1076
1077 do
1078 {
1079 Node = m_ClassNodeList.GetNext(Pos);
1080 if (IsEqualGUID(*Node->GetClassGuid(), *ClassGuid))
1081 {
1082 ATLASSERT(Node->GetNodeType() == ClassNode);
1083 break;
1084 }
1085
1086 Node = nullptr;
1087
1088 } while (Pos != NULL);
1089
1090 return Node;
1091 }
1092
1093 CDeviceNode*
1094 CDeviceView::GetDeviceNode(
1095 _In_ DEVINST Device
1096 )
1097 {
1098 POSITION Pos;
1099 CDeviceNode *Node = nullptr;
1100
1101 Pos = m_DeviceNodeList.GetHeadPosition();
1102 if (Pos == NULL)
1103 return nullptr;
1104
1105 do
1106 {
1107 Node = m_DeviceNodeList.GetNext(Pos);
1108 if (Node->GetDeviceInst() == Device)
1109 {
1110 ATLASSERT(Node->GetNodeType() == DeviceNode);
1111 break;
1112 }
1113
1114 Node = nullptr;
1115
1116 } while (Pos != NULL);
1117
1118 return Node;
1119 }
1120
1121 CNode* CDeviceView::GetNode(
1122 _In_ LPTV_ITEMW TvItem
1123 )
1124 {
1125 TvItem->mask = TVIF_PARAM;
1126 if (TreeView_GetItem(m_hTreeView, TvItem))
1127 {
1128 return (CNode *)TvItem->lParam;
1129 }
1130 return nullptr;
1131 }
1132
1133 void
1134 CDeviceView::EmptyLists()
1135 {
1136 CNode *Node;
1137
1138 while (!m_ClassNodeList.IsEmpty())
1139 {
1140 Node = m_ClassNodeList.RemoveTail();
1141 delete Node;
1142 }
1143
1144 while (!m_DeviceNodeList.IsEmpty())
1145 {
1146 Node = m_DeviceNodeList.RemoveTail();
1147 delete Node;
1148 }
1149 }
1150
1151 bool
1152 CDeviceView::RefreshDeviceList()
1153 {
1154 GUID ClassGuid;
1155 CClassNode *ClassNode;
1156 CDeviceNode *DeviceNode;
1157 HDEVINFO hDevInfo;
1158 SP_DEVINFO_DATA DeviceInfoData;
1159 DWORD i;
1160 BOOL Success;
1161
1162 ULONG ClassIndex = 0;
1163
1164 EmptyLists();
1165
1166 if (m_RootNode) delete m_RootNode;
1167 m_RootNode = new CRootNode(&m_ImageListData);
1168 m_RootNode->SetupNode();
1169
1170 // Loop through all the classes
1171 do
1172 {
1173 Success = GetNextClass(ClassIndex, &ClassGuid, &hDevInfo);
1174 if (Success)
1175 {
1176 // Create a new class node and add it to the list
1177 ClassNode = new CClassNode(&ClassGuid, &m_ImageListData);
1178 if (ClassNode->SetupNode())
1179 {
1180 m_ClassNodeList.AddTail(ClassNode);
1181 }
1182
1183 SetupDiDestroyDeviceInfoList(hDevInfo);
1184 }
1185 ClassIndex++;
1186 } while (Success);
1187
1188 // Get all the devices on the local machine
1189 hDevInfo = SetupDiGetClassDevsW(NULL,
1190 0,
1191 0,
1192 DIGCF_PRESENT | DIGCF_ALLCLASSES);
1193 if (hDevInfo == INVALID_HANDLE_VALUE)
1194 {
1195 return false;
1196 }
1197
1198 // loop though all the devices
1199 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1200 for (i = 0;; i++)
1201 {
1202 // Get the devinst for this device
1203 Success = SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData);
1204 if (Success == FALSE)
1205 break;
1206
1207 // create a new device node and add it to the list
1208 DeviceNode = new CDeviceNode(DeviceInfoData.DevInst, &m_ImageListData);
1209 if (DeviceNode->SetupNode())
1210 {
1211 m_DeviceNodeList.AddTail(DeviceNode);
1212 }
1213 else
1214 {
1215 ATLASSERT(FALSE);
1216 }
1217 }
1218
1219 SetupDiDestroyDeviceInfoList(hDevInfo);
1220
1221 return TRUE;
1222 }