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>
13 #include "DeviceView.h"
16 // DATA ********************************************/
18 #define CLASS_NAME_LEN 256
19 #define CLASS_DESC_LEN 256
20 #define ROOT_NAME_SIZE MAX_COMPUTERNAME_LENGTH + 1
25 IN HWND hWndParent OPTIONAL
,
26 IN LPCWSTR lpMachineName OPTIONAL
,
27 IN LPCWSTR lpDeviceID OPTIONAL
,
28 IN DWORD dwFlags OPTIONAL
,
32 typedef INT_PTR(WINAPI
*pDevicePropertiesExW
)(HWND
,LPCWSTR
,LPCWSTR
,DWORD
,BOOL
);
34 struct RefreshThreadData
42 // PUBLIC METHODS ************************************/
44 CDeviceView::CDeviceView(
49 m_hPropertyDialog(NULL
),
52 m_ViewType(DevicesByType
),
57 ZeroMemory(&m_ImageListData
, sizeof(SP_CLASSIMAGELIST_DATA
));
60 CDeviceView::~CDeviceView(void)
65 CDeviceView::Initialize()
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;
72 // Create the main treeview
73 m_hTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
76 WS_CHILD
| WS_VISIBLE
| WS_BORDER
| TVS_HASLINES
|
77 TVS_HASBUTTONS
| TVS_SHOWSELALWAYS
| TVS_LINESATROOT
,
85 // Set the image list against the treeview
86 (void)TreeView_SetImageList(m_hTreeView
,
87 m_ImageListData
.ImageList
,
90 // Give the treeview arrows instead of +/- boxes (on Win7)
91 SetWindowTheme(m_hTreeView
, L
"explorer", NULL
);
94 // Create the context menu and make properties the default item
95 m_hContextMenu
= CreatePopupMenu();
96 SetMenuDefaultItem(m_hContextMenu
, IDC_PROPERTIES
, FALSE
);
98 return !!(m_hTreeView
);
102 CDeviceView::Uninitialize()
106 if (m_ImageListData
.ImageList
!= NULL
)
108 SetupDiDestroyClassImageList(&m_ImageListData
);
109 ZeroMemory(&m_ImageListData
, sizeof(SP_CLASSIMAGELIST_DATA
));
112 DestroyMenu(m_hContextMenu
);
125 // Resize the treeview
126 SetWindowPos(m_hTreeView
,
138 CDeviceView::OnRightClick(
142 HTREEITEM hItem
= TreeView_GetNextItem(NmHdr
->hwndFrom
, 0, TVGN_DROPHILITE
);
145 TreeView_SelectItem(NmHdr
->hwndFrom
, hItem
);
152 CDeviceView::OnContextMenu(
156 HTREEITEM hSelected
= TreeView_GetSelection(m_hTreeView
);
159 if (TreeView_GetItemRect(m_hTreeView
,
165 if (GetCursorPos(&pt
) &&
166 ScreenToClient(m_hTreeView
, &pt
) &&
170 CNode
*Node
= GetSelectedNode();
171 if (Node
&& Node
->HasProperties())
181 INT xPos
= GET_X_LPARAM(lParam
);
182 INT yPos
= GET_Y_LPARAM(lParam
);
184 TrackPopupMenuEx(m_hContextMenu
,
198 CDeviceView::Refresh(
200 _In_
bool ScanForChanges
,
204 // Enum devices on a seperate thread to keep the gui responsive
208 RefreshThreadData
*ThreadData
;
209 ThreadData
= new RefreshThreadData();
210 ThreadData
->This
= this;
211 ThreadData
->ScanForChanges
= ScanForChanges
;
212 ThreadData
->UpdateView
= UpdateView
;
215 hThread
= (HANDLE
)_beginthreadex(NULL
,
222 if (hThread
) CloseHandle(hThread
);
226 CDeviceView::DisplayPropertySheet()
229 // In ReactOS we can link to DevicePropertiesEx but
230 // not in windows as it's not part of the SDK
233 HMODULE hModule
= LoadLibraryW(L
"devmgr.dll");
234 if (hModule
== NULL
) return;
236 pDevicePropertiesExW DevicePropertiesExW
;
237 DevicePropertiesExW
= (pDevicePropertiesExW
)GetProcAddress(hModule
,
238 "DevicePropertiesExW");
239 if (DevicePropertiesExW
== NULL
)
241 FreeLibrary(hModule
);
246 CNode
*Node
= GetSelectedNode();
247 if (Node
&& Node
->HasProperties())
249 DevicePropertiesExW(m_hTreeView
,
257 FreeLibrary(hModule
);
262 CDeviceView::SetFocus()
267 CDeviceView::HasProperties(
268 _In_ LPTV_ITEMW TvItem
271 CNode
*Node
= GetNode(TvItem
);
274 return Node
->HasProperties();
280 CDeviceView::IsDisabled(
281 _In_ LPTV_ITEMW TvItem
284 CDeviceNode
*Node
= dynamic_cast<CDeviceNode
*>(GetNode(TvItem
));
287 return Node
->IsDisabled();
293 CDeviceView::CanDisable(
294 _In_ LPTV_ITEMW TvItem
297 CDeviceNode
*Node
= dynamic_cast<CDeviceNode
*>(GetNode(TvItem
));
300 return Node
->CanDisable();
306 CDeviceView::EnableSelectedDevice(
308 _Out_
bool &NeedsReboot
311 CDeviceNode
*Node
= dynamic_cast<CDeviceNode
*>(GetSelectedNode());
314 if (Node
->EnableDevice(Enable
, NeedsReboot
))
316 Refresh(m_ViewType
, true, true);
324 // PRIVATE METHODS *******************************************/
327 CDeviceView::AddRootDevice()
329 // Check whether we've loaded the root bitmap into the imagelist (done on first run)
330 if (m_RootClassImage
== -1)
332 // Load the bitmap we'll be using as the root image
334 hRootImage
= LoadBitmapW(g_hInstance
,
335 MAKEINTRESOURCEW(IDB_ROOT_IMAGE
));
336 if (hRootImage
== NULL
) return FALSE
;
338 // Add this bitmap to the device image list. This is a bit hacky, but it's safe
339 m_RootClassImage
= ImageList_Add(m_ImageListData
.ImageList
,
342 DeleteObject(hRootImage
);
345 // Get the root instance
347 cr
= CM_Locate_DevNodeW(&m_RootDevInst
,
349 CM_LOCATE_DEVNODE_NORMAL
);
350 if (cr
!= CR_SUCCESS
)
355 // The root name is the computer name
356 WCHAR RootDeviceName
[ROOT_NAME_SIZE
];
357 DWORD Size
= ROOT_NAME_SIZE
;
358 if (GetComputerNameW(RootDeviceName
, &Size
))
359 _wcslwr_s(RootDeviceName
);
362 TV_INSERTSTRUCT tvins
;
363 ZeroMemory(&tvi
, sizeof(tvi
));
364 ZeroMemory(&tvins
, sizeof(tvins
));
366 // Insert the root / parent item into our treeview
367 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
368 tvi
.pszText
= RootDeviceName
;
369 tvi
.cchTextMax
= wcslen(RootDeviceName
);
370 tvi
.iImage
= m_RootClassImage
;
371 tvi
.iSelectedImage
= m_RootClassImage
;
373 m_hTreeRoot
= TreeView_InsertItem(m_hTreeView
, &tvins
);
375 return (m_hTreeRoot
!= NULL
);
380 CDeviceView::GetNextClass(
381 _In_ ULONG ClassIndex
,
382 _Out_ LPGUID ClassGuid
,
383 _Out_ HDEVINFO
*hDevInfo
388 // Get the next class in the list
389 cr
= CM_Enumerate_Classes(ClassIndex
,
392 if (cr
!= CR_SUCCESS
) return false;
394 // Check if this is the unknown class
395 if (IsEqualGUID(*ClassGuid
, GUID_DEVCLASS_UNKNOWN
))
397 // Get device info for all devices
398 *hDevInfo
= SetupDiGetClassDevsW(NULL
,
405 // We only want the devices for this class
406 *hDevInfo
= SetupDiGetClassDevsW(ClassGuid
,
412 return (hDevInfo
!= INVALID_HANDLE_VALUE
);
415 unsigned int __stdcall
CDeviceView::RefreshThread(void *Param
)
417 RefreshThreadData
*ThreadData
= (RefreshThreadData
*)Param
;
418 CDeviceView
*This
= ThreadData
->This
;
421 // Empty the treeview
422 This
->EmptyDeviceView();
423 This
->m_hTreeRoot
= NULL
;
425 // Refresh the devices only if requested. This means
426 // switching views uses the cache and remains fast
427 if (ThreadData
->ScanForChanges
)
429 This
->RefreshDeviceList();
432 // display the type of view the user wants
433 switch (This
->m_ViewType
)
436 (void)This
->ListDevicesByType();
439 case DevicesByConnection
:
440 (VOID
)This
->ListDevicesByConnection();
443 case ResourcesByType
:
446 case ResourcesByConnection
:
457 CDeviceView::ListDevicesByType()
459 CClassNode
*ClassNode
;
460 CDeviceNode
*DeviceNode
;
462 HTREEITEM hTreeItem
= NULL
;
465 LPTSTR DeviceId
= NULL
;
466 BOOL bClassSuccess
, bSuccess
;
468 // Start by adding the root node to the tree
469 bSuccess
= AddRootDevice();
470 if (bSuccess
== false) return false;
475 // Loop through all the device classes
476 bClassSuccess
= GetNextClass(ClassIndex
, &ClassGuid
, &hDevInfo
);
479 bool bClassUnknown
= false;
480 bool AddedParent
= false;
484 // Get the cached class node
485 ClassNode
= GetClassNode(&ClassGuid
);
486 if (ClassNode
== NULL
)
493 // Set a flag is this is the (special case) unknown class
494 if (IsEqualGUID(ClassGuid
, GUID_DEVCLASS_UNKNOWN
))
495 bClassUnknown
= true;
499 // Get a handle to all the devices in this class
500 SP_DEVINFO_DATA DeviceInfoData
;
501 ZeroMemory(&DeviceInfoData
, sizeof(SP_DEVINFO_DATA
));
502 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
503 bSuccess
= SetupDiEnumDeviceInfo(hDevInfo
,
506 if (bSuccess
== FALSE
&& GetLastError() == ERROR_NO_MORE_ITEMS
)
513 // The unknown class handle contains all devices on the system,
514 // and we're just looking for the ones with a null GUID
517 if (IsEqualGUID(DeviceInfoData
.ClassGuid
, GUID_NULL
) == FALSE
)
519 // This is a known device, we aren't interested in it
525 // Get the cached device node
526 DeviceNode
= GetDeviceNode(DeviceInfoData
.DevInst
);
527 if (DeviceNode
== NULL
)
529 ATLASSERT(bClassUnknown
== true);
534 // Check if this is a hidden device
535 if (DeviceNode
->IsHidden())
537 // Ignore this device if we aren't displaying hidden devices
538 if (m_ShowHidden
== FALSE
)
545 // We have a device, we need to add the parent if it hasn't yet been added
546 if (AddedParent
== false)
548 // Insert the new class under the root item
549 hTreeItem
= InsertIntoTreeView(m_hTreeRoot
,
554 // Add the device under the class item node
555 (void)InsertIntoTreeView(hTreeItem
, DeviceNode
);
557 // Expand the class if it has a problem device
558 if (DeviceNode
->HasProblem())
560 (void)TreeView_Expand(m_hTreeView
,
570 // If this class has devices, sort them alphabetically
571 if (AddedParent
== true)
573 (void)TreeView_SortChildren(m_hTreeView
,
581 } while (bClassSuccess
);
583 // Sort the classes alphabetically
584 (void)TreeView_SortChildren(m_hTreeView
,
588 // Expand the root item
589 (void)TreeView_Expand(m_hTreeView
,
593 // Pre-select the root item
594 (VOID
)TreeView_SelectItem(m_hTreeView
,
601 CDeviceView::ListDevicesByConnection()
605 // Start by adding the root node to the tree
606 bSuccess
= AddRootDevice();
607 if (bSuccess
== false) return false;
609 // Walk the device tree and add all the devices
610 (void)RecurseChildDevices(m_RootDevInst
, m_hTreeRoot
);
612 // Expand the root item
613 (void)TreeView_Expand(m_hTreeView
,
621 CDeviceView::RecurseChildDevices(
622 _In_ DEVINST ParentDevice
,
623 _In_ HTREEITEM hParentTreeItem
626 HTREEITEM hDevItem
= NULL
;
628 bool HasProblem
= false;
631 // Check if the parent has any child devices
632 if (GetChildDevice(ParentDevice
, &Device
) == FALSE
)
635 // Get the cached device node
636 CDeviceNode
*DeviceNode
;
637 DeviceNode
= dynamic_cast<CDeviceNode
*>(GetDeviceNode(Device
));
638 if (DeviceNode
== NULL
)
644 // Don't show hidden devices if not requested
645 if ((m_ShowHidden
== TRUE
) || (!(DeviceNode
->IsHidden())))
647 // Add this device to the tree under its parent
648 hDevItem
= InsertIntoTreeView(hParentTreeItem
,
652 // Check if this child has any children itself
653 if (!RecurseChildDevices(Device
, hDevItem
))
657 if (DeviceNode
->HasProblem())
664 // Check for siblings
667 // Check if the parent device has anything at the same level
668 bSuccess
= GetSiblingDevice(Device
, &Device
);
669 if (bSuccess
== FALSE
) break;
671 DeviceNode
= dynamic_cast<CDeviceNode
*>(GetDeviceNode(Device
));
672 if (DeviceNode
== NULL
)
677 // Don't show hidden devices if not requested
678 if ((m_ShowHidden
== TRUE
) || (!(DeviceNode
->IsHidden())))
680 if (DeviceNode
->HasProblem())
685 // Add this device to the tree under its parent
686 hDevItem
= InsertIntoTreeView(hParentTreeItem
,
690 // Check if this child has any children itself
691 if (!RecurseChildDevices(Device
, hDevItem
))
697 (void)TreeView_SortChildren(m_hTreeView
,
701 // Expand the class if it has a problem device
702 if (HasProblem
== true)
704 (void)TreeView_Expand(m_hTreeView
,
709 // If there was a problem, expand the ancestors
710 if (HasProblem
) return false;
716 CDeviceView::GetChildDevice(
717 _In_ DEVINST ParentDevInst
,
718 _Out_ PDEVINST DevInst
722 cr
= CM_Get_Child(DevInst
,
725 return (cr
== CR_SUCCESS
);
729 CDeviceView::GetSiblingDevice(
730 _In_ DEVINST PrevDevice
,
731 _Out_ PDEVINST DevInst
735 cr
= CM_Get_Sibling(DevInst
,
738 return (cr
== CR_SUCCESS
);
742 CDeviceView::InsertIntoTreeView(
743 _In_ HTREEITEM hParent
,
749 lpLabel
= Node
->GetDisplayName();
752 TV_INSERTSTRUCT tvins
;
753 ZeroMemory(&tvi
, sizeof(tvi
));
754 ZeroMemory(&tvins
, sizeof(tvins
));
756 tvi
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
757 tvi
.pszText
= lpLabel
;
758 tvi
.cchTextMax
= wcslen(lpLabel
);
759 tvi
.lParam
= (LPARAM
)Node
;
760 tvi
.iImage
= Node
->GetClassImage();
761 tvi
.iSelectedImage
= Node
->GetClassImage();
763 // try to cast it to a device node. This will only suceed if it's the correct type
764 CDeviceNode
*DeviceNode
= dynamic_cast<CDeviceNode
*>(Node
);
765 if (DeviceNode
&& DeviceNode
->GetOverlayImage())
767 tvi
.mask
|= TVIF_STATE
;
768 tvi
.stateMask
= TVIS_OVERLAYMASK
;
769 tvi
.state
= INDEXTOOVERLAYMASK(DeviceNode
->GetOverlayImage());
773 tvins
.hParent
= hParent
;
775 return TreeView_InsertItem(m_hTreeView
, &tvins
);
779 CDeviceView::RecurseDeviceView(
780 _In_ HTREEITEM hParentItem
786 // Check if this node has any children
787 hItem
= TreeView_GetChild(m_hTreeView
, hParentItem
);
788 if (hItem
== NULL
) return;
790 // The lParam contains the node pointer data
791 tvItem
.hItem
= hItem
;
792 tvItem
.mask
= TVIF_PARAM
;
793 if (TreeView_GetItem(m_hTreeView
, &tvItem
) &&
794 tvItem
.lParam
!= NULL
)
796 // Delete the node class
797 //delete reinterpret_cast<CNode *>(tvItem.lParam);
800 // This node may have its own children
801 RecurseDeviceView(hItem
);
803 // Delete all the siblings
806 // Get the next item at this level
807 hItem
= TreeView_GetNextSibling(m_hTreeView
, hItem
);
808 if (hItem
== NULL
) break;
810 // The lParam contains the node pointer data
811 tvItem
.hItem
= hItem
;
812 tvItem
.mask
= TVIF_PARAM
;
813 if (TreeView_GetItem(m_hTreeView
, &tvItem
))
815 //if (tvItem.lParam != NULL)
816 // delete reinterpret_cast<CNode *>(tvItem.lParam);
819 // This node may have its own children
820 RecurseDeviceView(hItem
);
826 CDeviceView::EmptyDeviceView()
830 // Check if there are any items in the tree
831 hItem
= TreeView_GetRoot(m_hTreeView
);
832 if (hItem
== NULL
) return;
834 // Free all the class nodes
835 //RecurseDeviceView(hItem);
837 // Delete all the items
838 (VOID
)TreeView_DeleteAllItems(m_hTreeView
);
845 CDeviceView::GetClassNode(
846 _In_ LPGUID ClassGuid
852 Pos
= m_ClassNodeList
.GetHeadPosition();
856 Node
= m_ClassNodeList
.GetNext(Pos
);
857 if (IsEqualGUID(*Node
->GetClassGuid(), *ClassGuid
))
859 //ATLASSERT(Node->GetType() == NodeClass);
865 } while (Pos
!= NULL
);
871 CDeviceView::GetDeviceNode(
878 Pos
= m_DeviceNodeList
.GetHeadPosition();
882 Node
= m_DeviceNodeList
.GetNext(Pos
);
883 if (Node
->GetDeviceInst() == Device
)
885 //ATLASSERT(Node->GetType() == NodeDevice);
891 } while (Pos
!= NULL
);
896 CNode
* CDeviceView::GetNode(
897 _In_ LPTV_ITEMW TvItem
900 TvItem
->mask
= TVIF_PARAM
;
901 if (TreeView_GetItem(m_hTreeView
, TvItem
))
903 return (CNode
*)TvItem
->lParam
;
908 CNode
* CDeviceView::GetSelectedNode()
911 TvItem
.hItem
= TreeView_GetSelection(m_hTreeView
);
912 return GetNode(&TvItem
);
916 CDeviceView::EmptyLists()
920 while (!m_ClassNodeList
.IsEmpty())
922 Node
= m_ClassNodeList
.RemoveTail();
926 while (!m_DeviceNodeList
.IsEmpty())
928 Node
= m_DeviceNodeList
.RemoveTail();
934 CDeviceView::RefreshDeviceList()
937 CClassNode
*ClassNode
;
938 CDeviceNode
*DeviceNode
;
940 SP_DEVINFO_DATA DeviceInfoData
;
944 ULONG ClassIndex
= 0;
948 // Loop through all the classes
951 Success
= GetNextClass(ClassIndex
, &ClassGuid
, &hDevInfo
);
954 // Create a new class node and add it to the list
955 ClassNode
= new CClassNode(&ClassGuid
, &m_ImageListData
);
956 if (ClassNode
->SetupNode())
958 m_ClassNodeList
.AddTail(ClassNode
);
965 // Get all the devices on the local machine
966 hDevInfo
= SetupDiGetClassDevsW(NULL
,
969 DIGCF_PRESENT
| DIGCF_ALLCLASSES
);
970 if (hDevInfo
== INVALID_HANDLE_VALUE
)
975 // loop though all the devices
976 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
979 // Get the devinst for this device
980 Success
= SetupDiEnumDeviceInfo(hDevInfo
, i
, &DeviceInfoData
);
981 if (Success
== FALSE
) break;
983 // create a new device node and add it to the list
984 DeviceNode
= new CDeviceNode(DeviceInfoData
.DevInst
, &m_ImageListData
);
985 if (DeviceNode
->SetupNode())
987 m_DeviceNodeList
.AddTail(DeviceNode
);
991 SetupDiDestroyDeviceInfoList(hDevInfo
);