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
24 IN HWND hWndParent OPTIONAL
,
25 IN LPCWSTR lpMachineName OPTIONAL
,
26 IN LPCWSTR lpDeviceID OPTIONAL
,
27 IN DWORD dwFlags OPTIONAL
,
31 typedef INT_PTR(WINAPI
*pDevicePropertiesExW
)(HWND
,LPCWSTR
,LPCWSTR
,DWORD
,BOOL
);
33 struct RefreshThreadData
41 /* PUBLIC METHODS *************************************/
43 CDeviceView::CDeviceView(
48 m_hPropertyDialog(NULL
),
51 m_ViewType(DevicesByType
),
56 ZeroMemory(&m_ImageListData
, sizeof(SP_CLASSIMAGELIST_DATA
));
59 CDeviceView::~CDeviceView(void)
64 CDeviceView::Initialize()
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;
71 // Create the main treeview
72 m_hTreeView
= CreateWindowExW(WS_EX_CLIENTEDGE
,
75 WS_CHILD
| WS_VISIBLE
| WS_BORDER
| TVS_HASLINES
|
76 TVS_HASBUTTONS
| TVS_SHOWSELALWAYS
| TVS_LINESATROOT
,
84 // Set the image list against the treeview
85 (void)TreeView_SetImageList(m_hTreeView
,
86 m_ImageListData
.ImageList
,
89 // Give the treeview arrows instead of +/- boxes (on Win7)
90 SetWindowTheme(m_hTreeView
, L
"explorer", NULL
);
93 // Create the context menu and make properties the default item
94 m_hMenu
= LoadMenuW(g_hInstance
, MAKEINTRESOURCEW(IDR_POPUP
));
95 m_hContextMenu
= GetSubMenu(m_hMenu
, 0);
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_hMenu
);
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
) &&
169 INT xPos
= GET_X_LPARAM(lParam
);
170 INT yPos
= GET_Y_LPARAM(lParam
);
172 TrackPopupMenuEx(m_hContextMenu
,
186 CDeviceView::Refresh(
188 _In_
bool ScanForChanges
,
192 // Enum devices on a seperate thread to keep the gui responsive
196 RefreshThreadData
*ThreadData
;
197 ThreadData
= new RefreshThreadData();
198 ThreadData
->This
= this;
199 ThreadData
->ScanForChanges
= ScanForChanges
;
200 ThreadData
->UpdateView
= UpdateView
;
203 hThread
= (HANDLE
)_beginthreadex(NULL
,
210 if (hThread
) CloseHandle(hThread
);
214 CDeviceView::DisplayPropertySheet()
217 // In ReactOS we can link to DevicePropertiesEx but
218 // not in windows as it's not part of the SDK
221 HMODULE hModule
= LoadLibraryW(L
"devmgr.dll");
222 if (hModule
== NULL
) return;
224 pDevicePropertiesExW DevicePropertiesExW
;
225 DevicePropertiesExW
= (pDevicePropertiesExW
)GetProcAddress(hModule
,
226 "DevicePropertiesExW");
227 if (DevicePropertiesExW
== NULL
)
229 FreeLibrary(hModule
);
234 CNode
*Node
= GetSelectedNode();
235 if (Node
&& Node
->HasProperties())
237 DevicePropertiesExW(m_hTreeView
,
245 FreeLibrary(hModule
);
250 CDeviceView::SetFocus()
255 CDeviceView::HasProperties(
256 _In_ LPTV_ITEMW TvItem
259 CNode
*Node
= GetNode(TvItem
);
262 return Node
->HasProperties();
268 CDeviceView::IsDisabled(
269 _In_ LPTV_ITEMW TvItem
272 CNode
*Node
= GetNode(TvItem
);
275 return Node
->IsDisabled();
281 CDeviceView::CanDisable(
282 _In_ LPTV_ITEMW TvItem
285 CNode
*Node
= GetNode(TvItem
);
288 return Node
->CanDisable();
294 /* PRIVATE METHODS ********************************************/
297 CDeviceView::AddRootDevice()
299 // Check whether we've loaded the root bitmap into the imagelist (done on first run)
300 if (m_RootClassImage
== -1)
302 // Load the bitmap we'll be using as the root image
304 hRootImage
= LoadBitmapW(g_hInstance
,
305 MAKEINTRESOURCEW(IDB_ROOT_IMAGE
));
306 if (hRootImage
== NULL
) return FALSE
;
308 // Add this bitmap to the device image list. This is a bit hacky, but it's safe
309 m_RootClassImage
= ImageList_Add(m_ImageListData
.ImageList
,
312 DeleteObject(hRootImage
);
315 /* Get the root instance */
317 cr
= CM_Locate_DevNodeW(&m_RootDevInst
,
319 CM_LOCATE_DEVNODE_NORMAL
);
320 if (cr
!= CR_SUCCESS
)
325 /* The root name is the computer name */
326 WCHAR RootDeviceName
[ROOT_NAME_SIZE
];
327 DWORD Size
= ROOT_NAME_SIZE
;
328 if (GetComputerNameW(RootDeviceName
, &Size
))
329 _wcslwr_s(RootDeviceName
);
332 TV_INSERTSTRUCT tvins
;
333 ZeroMemory(&tvi
, sizeof(tvi
));
334 ZeroMemory(&tvins
, sizeof(tvins
));
336 // Insert the root / parent item into our treeview
337 tvi
.mask
= TVIF_TEXT
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
338 tvi
.pszText
= RootDeviceName
;
339 tvi
.cchTextMax
= wcslen(RootDeviceName
);
340 tvi
.iImage
= m_RootClassImage
;
341 tvi
.iSelectedImage
= m_RootClassImage
;
343 m_hTreeRoot
= TreeView_InsertItem(m_hTreeView
, &tvins
);
345 return (m_hTreeRoot
!= NULL
);
350 CDeviceView::GetNextClass(_In_ ULONG ClassIndex
,
351 _Out_ LPGUID ClassGuid
,
352 _Out_ HDEVINFO
*hDevInfo
)
356 // Get the next class in the list
357 cr
= CM_Enumerate_Classes(ClassIndex
,
360 if (cr
!= CR_SUCCESS
) return false;
362 // Check for devices without a class
363 if (IsEqualGUID(*ClassGuid
, GUID_DEVCLASS_UNKNOWN
))
365 // Get device info for all devices for all classes
366 *hDevInfo
= SetupDiGetClassDevsW(NULL
,
373 // We only want the devices for this class
374 *hDevInfo
= SetupDiGetClassDevsW(ClassGuid
,
381 return (hDevInfo
!= INVALID_HANDLE_VALUE
);
384 unsigned int __stdcall
CDeviceView::RefreshThread(void *Param
)
386 RefreshThreadData
*ThreadData
= (RefreshThreadData
*)Param
;
387 CDeviceView
*This
= ThreadData
->This
;
390 // Empty the treeview
391 This
->EmptyDeviceView();
392 This
->m_hTreeRoot
= NULL
;
394 // Refresh the devices only if requested. This means
395 // switching views uses the cache and remains fast
396 if (ThreadData
->ScanForChanges
)
398 This
->RefreshDeviceList();
401 // display the type of view the user wants
402 switch (This
->m_ViewType
)
405 (void)This
->ListDevicesByType();
408 case DevicesByConnection
:
409 (VOID
)This
->ListDevicesByConnection();
412 case ResourcesByType
:
415 case ResourcesByConnection
:
426 CDeviceView::ListDevicesByType()
428 CNode
*ClassNode
, *DeviceNode
;
430 HTREEITEM hTreeItem
= NULL
;
433 LPTSTR DeviceId
= NULL
;
434 BOOL bClassSuccess
, bSuccess
;
436 // Start by adding the root node to the tree
437 bSuccess
= AddRootDevice();
438 if (bSuccess
== false) return false;
443 // Loop through all the device classes
444 bClassSuccess
= GetNextClass(ClassIndex
, &ClassGuid
, &hDevInfo
);
447 bool bClassUnknown
= false;
448 bool AddedParent
= false;
452 // Get the cached class node
453 ClassNode
= GetClassNode(&ClassGuid
);
454 if (ClassNode
== NULL
)
461 // Set a flag is this is the (special case) unknown class
462 if (IsEqualGUID(ClassGuid
, GUID_DEVCLASS_UNKNOWN
))
463 bClassUnknown
= true;
467 // Get a handle to all the devices in this class
468 SP_DEVINFO_DATA DeviceInfoData
;
469 ZeroMemory(&DeviceInfoData
, sizeof(SP_DEVINFO_DATA
));
470 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
471 bSuccess
= SetupDiEnumDeviceInfo(hDevInfo
,
474 if (bSuccess
== FALSE
&& GetLastError() == ERROR_NO_MORE_ITEMS
)
481 // The unknown class handle contains all devices on the system,
482 // and we're just looking for the ones with a null GUID
485 if (IsEqualGUID(DeviceInfoData
.ClassGuid
, GUID_NULL
) == FALSE
)
487 // This is a known device, we aren't interested in it
493 // Get the cached device node
494 DeviceNode
= GetDeviceNode(DeviceInfoData
.DevInst
);
495 if (DeviceNode
== NULL
)
497 ATLASSERT(bClassUnknown
== true);
502 // Check if this is a hidden device
503 if (DeviceNode
->IsHidden())
505 // Ignore this device if we aren't displaying hidden devices
506 if (m_ShowHidden
== FALSE
)
513 // We have a device, we need to add the parent if it hasn't yet been added
514 if (AddedParent
== false)
516 // Insert the new class under the root item
517 hTreeItem
= InsertIntoTreeView(m_hTreeRoot
,
522 // Add the device under the class item node
523 (void)InsertIntoTreeView(hTreeItem
, DeviceNode
);
525 // Expand the class if it has a problem device
526 if (DeviceNode
->HasProblem())
528 (void)TreeView_Expand(m_hTreeView
,
538 // If this class has devices, sort them alphabetically
539 if (AddedParent
== true)
541 (void)TreeView_SortChildren(m_hTreeView
,
549 } while (bClassSuccess
);
551 // Sort the classes alphabetically
552 (void)TreeView_SortChildren(m_hTreeView
,
556 // Expand the root item
557 (void)TreeView_Expand(m_hTreeView
,
561 // Pre-select the root item
562 (VOID
)TreeView_SelectItem(m_hTreeView
,
569 CDeviceView::ListDevicesByConnection()
573 // Start by adding the root node to the tree
574 bSuccess
= AddRootDevice();
575 if (bSuccess
== false) return false;
577 /* Walk the device tree and add all the devices */
578 RecurseChildDevices(m_RootDevInst
, m_hTreeRoot
);
580 /* Expand the root item */
581 (VOID
)TreeView_Expand(m_hTreeView
,
589 CDeviceView::RecurseChildDevices(
590 _In_ DEVINST ParentDevice
,
591 _In_ HTREEITEM hParentTreeItem
595 HTREEITEM hDevItem
= NULL
;
599 /* Check if the parent has any child devices */
600 if (GetChildDevice(ParentDevice
, &Device
) == FALSE
)
603 // Get the cached device node
605 DeviceNode
= GetDeviceNode(Device
);
606 if (DeviceNode
== NULL
)
613 /* Check if this is a hidden device */
614 if ((m_ShowHidden
== TRUE
) || (!(DeviceNode
->IsHidden())))
616 /* Add this device to the tree under its parent */
617 hDevItem
= InsertIntoTreeView(hParentTreeItem
,
623 /* Check if this child has any children itself */
624 RecurseChildDevices(Device
, hDevItem
);
631 /* Check if the parent device has anything at the same level */
632 bSuccess
= GetSiblingDevice(Device
, &Device
);
633 if (bSuccess
== FALSE
) break;
635 DeviceNode
= GetDeviceNode(Device
);
636 if (DeviceNode
== NULL
)
641 /* Check if this is a hidden device */
642 if (DeviceNode
->IsHidden())
644 if (m_ShowHidden
== FALSE
)
648 /* Add this device to the tree under its parent */
649 hDevItem
= InsertIntoTreeView(hParentTreeItem
,
653 /* Check if this child has any children itself */
654 RecurseChildDevices(Device
, hDevItem
);
658 (void)TreeView_SortChildren(m_hTreeView
,
665 CDeviceView::GetChildDevice(
666 _In_ DEVINST ParentDevInst
,
667 _Out_ PDEVINST DevInst
671 cr
= CM_Get_Child(DevInst
,
674 return (cr
== CR_SUCCESS
);
678 CDeviceView::GetSiblingDevice(
679 _In_ DEVINST PrevDevice
,
680 _Out_ PDEVINST DevInst
684 cr
= CM_Get_Sibling(DevInst
,
687 return (cr
== CR_SUCCESS
);
691 CDeviceView::InsertIntoTreeView(
692 _In_ HTREEITEM hParent
,
698 lpLabel
= Node
->GetDisplayName();
701 TV_INSERTSTRUCT tvins
;
702 ZeroMemory(&tvi
, sizeof(tvi
));
703 ZeroMemory(&tvins
, sizeof(tvins
));
705 tvi
.mask
= TVIF_TEXT
| TVIF_PARAM
| TVIF_IMAGE
| TVIF_SELECTEDIMAGE
;
706 tvi
.pszText
= lpLabel
;
707 tvi
.cchTextMax
= wcslen(lpLabel
);
708 tvi
.lParam
= (LPARAM
)Node
;
709 tvi
.iImage
= Node
->GetClassImage();
710 tvi
.iSelectedImage
= Node
->GetClassImage();
712 if (Node
->GetOverlayImage())
714 tvi
.mask
|= TVIF_STATE
;
715 tvi
.stateMask
= TVIS_OVERLAYMASK
;
716 tvi
.state
= INDEXTOOVERLAYMASK(Node
->GetOverlayImage());
720 tvins
.hParent
= hParent
;
722 return TreeView_InsertItem(m_hTreeView
, &tvins
);
726 CDeviceView::RecurseDeviceView(
727 _In_ HTREEITEM hParentItem
733 // Check if this node has any children
734 hItem
= TreeView_GetChild(m_hTreeView
, hParentItem
);
735 if (hItem
== NULL
) return;
737 // The lParam contains the node pointer data
738 tvItem
.hItem
= hItem
;
739 tvItem
.mask
= TVIF_PARAM
;
740 if (TreeView_GetItem(m_hTreeView
, &tvItem
) &&
741 tvItem
.lParam
!= NULL
)
743 // Delete the node class
744 //delete reinterpret_cast<CNode *>(tvItem.lParam);
747 // This node may have its own children
748 RecurseDeviceView(hItem
);
750 // Delete all the siblings
753 // Get the next item at this level
754 hItem
= TreeView_GetNextSibling(m_hTreeView
, hItem
);
755 if (hItem
== NULL
) break;
757 // The lParam contains the node pointer data
758 tvItem
.hItem
= hItem
;
759 tvItem
.mask
= TVIF_PARAM
;
760 if (TreeView_GetItem(m_hTreeView
, &tvItem
))
762 //if (tvItem.lParam != NULL)
763 // delete reinterpret_cast<CNode *>(tvItem.lParam);
766 /* This node may have its own children */
767 RecurseDeviceView(hItem
);
773 CDeviceView::EmptyDeviceView()
777 // Check if there are any items in the tree
778 hItem
= TreeView_GetRoot(m_hTreeView
);
779 if (hItem
== NULL
) return;
781 // Free all the class nodes
782 //RecurseDeviceView(hItem);
784 // Delete all the items
785 (VOID
)TreeView_DeleteAllItems(m_hTreeView
);
792 CDeviceView::GetClassNode(_In_ LPGUID ClassGuid
)
797 Pos
= m_ClassNodeList
.GetHeadPosition();
801 Node
= m_ClassNodeList
.GetNext(Pos
);
802 if (IsEqualGUID(*Node
->GetClassGuid(), *ClassGuid
))
804 //ATLASSERT(Node->GetType() == NodeClass);
810 } while (Pos
!= NULL
);
816 CDeviceView::GetDeviceNode(_In_ DEVINST Device
)
821 Pos
= m_DeviceNodeList
.GetHeadPosition();
825 Node
= m_DeviceNodeList
.GetNext(Pos
);
826 if (Node
->GetDeviceInst() == Device
)
828 //ATLASSERT(Node->GetType() == NodeDevice);
834 } while (Pos
!= NULL
);
839 CNode
* CDeviceView::GetNode(LPTV_ITEMW TvItem
)
841 TvItem
->mask
= TVIF_PARAM
;
842 if (TreeView_GetItem(m_hTreeView
, TvItem
))
844 return (CNode
*)TvItem
->lParam
;
848 CNode
* CDeviceView::GetSelectedNode()
851 TvItem
.hItem
= TreeView_GetSelection(m_hTreeView
);
852 return GetNode(&TvItem
);
856 CDeviceView::EmptyLists()
861 if (!m_ClassNodeList
.IsEmpty())
863 Pos
= m_ClassNodeList
.GetHeadPosition();
866 Node
= m_ClassNodeList
.GetNext(Pos
);
869 } while (Pos
!= NULL
);
872 if (!m_DeviceNodeList
.IsEmpty())
874 Pos
= m_DeviceNodeList
.GetHeadPosition();
877 Node
= m_DeviceNodeList
.GetNext(Pos
);
880 } while (Pos
!= NULL
);
885 CDeviceView::RefreshDeviceList()
890 SP_DEVINFO_DATA DeviceInfoData
;
894 ULONG ClassIndex
= 0;
900 Success
= GetNextClass(ClassIndex
, &ClassGuid
, &hDevInfo
);
903 /* Create a new class node */
904 Node
= new CNode(&ClassGuid
, &m_ImageListData
);
907 m_ClassNodeList
.AddTail(Node
);
914 hDevInfo
= SetupDiGetClassDevsW(NULL
,
917 DIGCF_PRESENT
| DIGCF_ALLCLASSES
);
918 if (hDevInfo
== INVALID_HANDLE_VALUE
)
924 DeviceInfoData
.cbSize
= sizeof(SP_DEVINFO_DATA
);
927 Success
= SetupDiEnumDeviceInfo(hDevInfo
, i
, &DeviceInfoData
);
928 if (Success
== FALSE
) break;
931 Node
= new CNode(DeviceInfoData
.DevInst
, &m_ImageListData
);
933 m_DeviceNodeList
.AddTail(Node
);
936 SetupDiDestroyDeviceInfoList(hDevInfo
);