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