d314f96ae15125dd712de626c4333e4c5d773e78
[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/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 INT_PTR
23 WINAPI
24 DevicePropertiesExW(
25 IN HWND hWndParent OPTIONAL,
26 IN LPCWSTR lpMachineName OPTIONAL,
27 IN LPCWSTR lpDeviceID OPTIONAL,
28 IN DWORD dwFlags OPTIONAL,
29 IN BOOL bShowDevMgr
30 );
31
32 typedef INT_PTR(WINAPI *pDevicePropertiesExW)(HWND,LPCWSTR,LPCWSTR,DWORD,BOOL);
33
34 struct RefreshThreadData
35 {
36 CDeviceView *This;
37 BOOL ScanForChanges;
38 BOOL UpdateView;
39 LPWSTR DeviceId;
40 };
41
42
43 // PUBLIC METHODS ************************************/
44
45 CDeviceView::CDeviceView(
46 HWND hMainWnd
47 ) :
48 m_hMainWnd(hMainWnd),
49 m_hTreeView(NULL),
50 m_hPropertyDialog(NULL),
51 m_hMenu(NULL),
52 m_ViewType(DevicesByType),
53 m_ShowHidden(FALSE),
54 m_RootNode(NULL)
55 {
56 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
57 }
58
59 CDeviceView::~CDeviceView(void)
60 {
61 }
62
63 bool
64 CDeviceView::Initialize()
65 {
66 // Get the device image list
67 m_ImageListData.cbSize = sizeof(SP_CLASSIMAGELIST_DATA);
68 BOOL bSuccess = SetupDiGetClassImageList(&m_ImageListData);
69 if (bSuccess == FALSE) return false;
70
71 // Create the main treeview
72 m_hTreeView = CreateWindowExW(WS_EX_CLIENTEDGE,
73 WC_TREEVIEW,
74 NULL,
75 WS_CHILD | WS_VISIBLE | WS_BORDER | TVS_HASLINES |
76 TVS_HASBUTTONS | TVS_SHOWSELALWAYS | TVS_LINESATROOT,
77 0, 0, 0, 0,
78 m_hMainWnd,
79 (HMENU)IDC_TREEVIEW,
80 g_hInstance,
81 NULL);
82 if (m_hTreeView)
83 {
84 // Set the image list against the treeview
85 (void)TreeView_SetImageList(m_hTreeView,
86 m_ImageListData.ImageList,
87 TVSIL_NORMAL);
88
89 // Give the treeview arrows instead of +/- boxes (on Win7)
90 SetWindowTheme(m_hTreeView, L"explorer", NULL);
91 }
92
93
94
95 return !!(m_hTreeView);
96 }
97
98 bool
99 CDeviceView::Uninitialize()
100 {
101 EmptyDeviceView();
102
103 if (m_ImageListData.ImageList != NULL)
104 {
105 SetupDiDestroyClassImageList(&m_ImageListData);
106 ZeroMemory(&m_ImageListData, sizeof(SP_CLASSIMAGELIST_DATA));
107 }
108
109 return true;
110 }
111
112 LRESULT
113 CDeviceView::OnSize(
114 _In_ int x,
115 _In_ int y,
116 _In_ int cx,
117 _In_ int cy
118 )
119 {
120 // Resize the treeview
121 SetWindowPos(m_hTreeView,
122 NULL,
123 x,
124 y,
125 cx,
126 cy,
127 SWP_NOZORDER);
128
129 return 0;
130 }
131
132 LRESULT
133 CDeviceView::OnRightClick(
134 _In_ LPNMHDR NmHdr
135 )
136 {
137 HTREEITEM hItem = TreeView_GetNextItem(NmHdr->hwndFrom, 0, TVGN_DROPHILITE);
138 if (hItem)
139 {
140 TreeView_SelectItem(NmHdr->hwndFrom, 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 _In_opt_ LPWSTR DeviceId
199 )
200 {
201 // Enum devices on a seperate 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 ThreadData->DeviceId = NULL;
211
212 if (DeviceId)
213 {
214 // Node gets deleted on refresh so we copy it to another block
215 size_t Length = wcslen(DeviceId) + 1;
216 ThreadData->DeviceId = new WCHAR[Length];
217 wcscpy_s(ThreadData->DeviceId, Length, DeviceId);
218 }
219
220
221 HANDLE hThread;
222 hThread = (HANDLE)_beginthreadex(NULL,
223 0,
224 &RefreshThread,
225 ThreadData,
226 0,
227 NULL);
228
229 if (hThread) CloseHandle(hThread);
230 }
231
232 LRESULT
233 CDeviceView::OnAction(
234 _In_ UINT Action
235 )
236 {
237 switch (Action)
238 {
239 case IDC_PROPERTIES:
240 {
241 DisplayPropertySheet();
242 break;
243 }
244
245 case IDC_SCAN_HARDWARE:
246 {
247 Refresh(GetCurrentView(),
248 true,
249 true,
250 NULL);
251 break;
252 }
253
254 case IDC_ENABLE_DRV:
255 {
256 bool NeedsReboot;
257 if (EnableSelectedDevice(true, NeedsReboot) &&
258 NeedsReboot)
259 {
260 MessageBox(m_hMainWnd, L"Rebooting", L"Enable", MB_OK);
261 }
262 break;
263 }
264
265 case IDC_DISABLE_DRV:
266 {
267 bool NeedsReboot;
268 EnableSelectedDevice(false, NeedsReboot);
269 break;
270 }
271
272 case IDC_UPDATE_DRV:
273 {
274 MessageBox(m_hMainWnd, L"Not yet implemented", L"Update Driver", MB_OK);
275 break;
276 }
277
278 case IDC_UNINSTALL_DRV:
279 {
280 UninstallSelectedDevice();
281 break;
282 }
283
284 case IDC_ADD_HARDWARE:
285 {
286 MessageBox(m_hMainWnd, L"Not yet implemented", L"Add Hardware", MB_OK);
287 break;
288 }
289 }
290
291 return 0;
292 }
293
294 void
295 CDeviceView::DisplayPropertySheet()
296 {
297 //
298 // In ReactOS we can link to DevicePropertiesEx but
299 // not in windows as it's not part of the SDK
300
301 #ifndef __REACTOS__
302 HMODULE hModule = LoadLibraryW(L"devmgr.dll");
303 if (hModule == NULL) return;
304
305 pDevicePropertiesExW DevicePropertiesExW;
306 DevicePropertiesExW = (pDevicePropertiesExW)GetProcAddress(hModule,
307 "DevicePropertiesExW");
308 if (DevicePropertiesExW == NULL)
309 {
310 FreeLibrary(hModule);
311 return;
312 }
313 #endif
314
315 CNode *Node = GetSelectedNode();
316 if (Node && Node->HasProperties())
317 {
318 DevicePropertiesExW(m_hTreeView,
319 NULL,
320 Node->GetDeviceId(),
321 1,//DPF_EXTENDED,
322 FALSE);
323 }
324
325 #ifndef __REACTOS__
326 FreeLibrary(hModule);
327 #endif
328 }
329
330 void
331 CDeviceView::SetFocus()
332 {
333 }
334
335 bool
336 CDeviceView::CreateActionMenu(
337 _In_ HMENU OwnerMenu,
338 _In_ bool MainMenu
339 )
340 {
341 CNode *Node = GetSelectedNode();
342 if (Node)
343 {
344 BuildActionMenuForNode(OwnerMenu, Node, MainMenu);
345 return true;
346 }
347
348 return false;
349 }
350
351 CNode*
352 CDeviceView::GetSelectedNode()
353 {
354 TV_ITEM TvItem;
355 TvItem.hItem = TreeView_GetSelection(m_hTreeView);
356 return GetNode(&TvItem);
357 }
358
359
360
361 // PRIVATE METHODS *******************************************/
362
363 bool
364 CDeviceView::AddRootDevice()
365 {
366 m_hTreeRoot = InsertIntoTreeView(NULL, m_RootNode);
367 return (m_hTreeRoot != NULL);
368 }
369
370 bool
371 CDeviceView::GetNextClass(
372 _In_ ULONG ClassIndex,
373 _Out_ LPGUID ClassGuid,
374 _Out_ HDEVINFO *hDevInfo
375 )
376 {
377 CONFIGRET cr;
378
379 // Get the next class in the list
380 cr = CM_Enumerate_Classes(ClassIndex,
381 ClassGuid,
382 0);
383 if (cr != CR_SUCCESS) return false;
384
385 // Check if this is the unknown class
386 if (IsEqualGUID(*ClassGuid, GUID_DEVCLASS_UNKNOWN))
387 {
388 // Get device info for all devices
389 *hDevInfo = SetupDiGetClassDevsW(NULL,
390 NULL,
391 NULL,
392 DIGCF_ALLCLASSES);
393 }
394 else
395 {
396 // We only want the devices for this class
397 *hDevInfo = SetupDiGetClassDevsW(ClassGuid,
398 NULL,
399 NULL,
400 DIGCF_PRESENT);
401 }
402
403 return (hDevInfo != INVALID_HANDLE_VALUE);
404 }
405
406 unsigned int __stdcall CDeviceView::RefreshThread(void *Param)
407 {
408 RefreshThreadData *ThreadData = (RefreshThreadData *)Param;
409 CDeviceView *This = ThreadData->This;
410
411
412 // Empty the treeview
413 This->EmptyDeviceView();
414 This->m_hTreeRoot = NULL;
415
416 // Refresh the devices only if requested. This means
417 // switching views uses the cache and remains fast
418 if (ThreadData->ScanForChanges)
419 {
420 This->RefreshDeviceList();
421 }
422
423 // display the type of view the user wants
424 switch (This->m_ViewType)
425 {
426 case DevicesByType:
427 (void)This->ListDevicesByType();
428 break;
429
430 case DevicesByConnection:
431 (VOID)This->ListDevicesByConnection();
432 break;
433
434 case ResourcesByType:
435 break;
436
437 case ResourcesByConnection:
438 break;
439 }
440
441
442 This->SelectNode(ThreadData->DeviceId);
443
444 if (ThreadData->DeviceId)
445 delete[] ThreadData->DeviceId;
446 delete ThreadData;
447
448 return 0;
449 }
450
451
452 bool
453 CDeviceView::ListDevicesByType()
454 {
455 CClassNode *ClassNode;
456 CDeviceNode *DeviceNode;
457 HDEVINFO hDevInfo;
458 HTREEITEM hTreeItem = NULL;
459 GUID ClassGuid;
460 INT ClassIndex;
461 LPTSTR DeviceId = NULL;
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;
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 }