1112 lines
31 KiB
C++
1112 lines
31 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997 - 1999
|
|
//
|
|
// File: devtree.c
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "HotPlug.h"
|
|
|
|
//
|
|
// Define and initialize all device class GUIDs.
|
|
// (This must only be done once per module!)
|
|
//
|
|
#include <initguid.h>
|
|
#include <devguid.h>
|
|
|
|
//
|
|
// Define and initialize a global variable, GUID_NULL
|
|
// (from coguid.h)
|
|
//
|
|
DEFINE_GUID(GUID_NULL, 0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
|
|
|
|
PDEVINST
|
|
BuildDeviceRelationsList(
|
|
PTCHAR DeviceId,
|
|
HMACHINE hMachine,
|
|
ULONG FilterFlag,
|
|
PUSHORT pNumDevinst
|
|
)
|
|
{
|
|
ULONG Len, NumDevinst, MaxDevinst;
|
|
USHORT NumDevInst;
|
|
CONFIGRET ConfigRet;
|
|
PTCHAR DeviceIdRelations = NULL, CurrDevId;
|
|
PDEVINST DevinstRelations = NULL;
|
|
|
|
DevinstRelations = NULL;
|
|
DeviceIdRelations = NULL;
|
|
NumDevInst = 0;
|
|
|
|
Len = 0;
|
|
ConfigRet = CM_Get_Device_ID_List_Size_Ex(&Len,
|
|
DeviceId,
|
|
FilterFlag,
|
|
hMachine
|
|
);
|
|
|
|
if (ConfigRet == CR_SUCCESS && Len) {
|
|
|
|
DeviceIdRelations = (PTCHAR)LocalAlloc(LPTR, Len*sizeof(TCHAR));
|
|
|
|
if (!DeviceIdRelations) {
|
|
|
|
goto BDEarlyExit;
|
|
}
|
|
|
|
*DeviceIdRelations = TEXT('\0');
|
|
|
|
if (DeviceIdRelations) {
|
|
|
|
ConfigRet = CM_Get_Device_ID_List_Ex(DeviceId,
|
|
DeviceIdRelations,
|
|
Len,
|
|
FilterFlag,
|
|
hMachine
|
|
);
|
|
|
|
|
|
|
|
if (ConfigRet != CR_SUCCESS || !*DeviceIdRelations) {
|
|
|
|
goto BDEarlyExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Len) {
|
|
|
|
goto BDEarlyExit;
|
|
}
|
|
|
|
//
|
|
// Convert the deviceId list into a Devinst list. We make the gross
|
|
// assumption that the Devinst list will NOT be bigger than the deviceId
|
|
// list.
|
|
//
|
|
|
|
Len = (Len + sizeof(DEVINST) - 1) & ~(sizeof(DEVINST) - 1);
|
|
MaxDevinst = Len / sizeof(DEVINST);
|
|
|
|
DevinstRelations = (PDEVINST)LocalAlloc(LPTR, Len);
|
|
|
|
if (!DevinstRelations) {
|
|
|
|
goto BDEarlyExit;
|
|
}
|
|
|
|
for (CurrDevId = DeviceIdRelations; *CurrDevId; CurrDevId += lstrlen(CurrDevId) + 1) {
|
|
|
|
ConfigRet = CM_Locate_DevNode_Ex(&DevinstRelations[NumDevInst],
|
|
CurrDevId,
|
|
CM_LOCATE_DEVNODE_NORMAL,
|
|
hMachine
|
|
);
|
|
|
|
if (ConfigRet == CR_SUCCESS) {
|
|
|
|
if (++NumDevInst >= MaxDevinst) {
|
|
|
|
//
|
|
// paranoia check, if our devinst size wasn't big enough
|
|
// the id list is probably trashed. so just give up.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BDEarlyExit:
|
|
|
|
if (DeviceIdRelations) {
|
|
|
|
LocalFree(DeviceIdRelations);
|
|
}
|
|
|
|
if (DevinstRelations) {
|
|
|
|
if (NumDevInst) {
|
|
|
|
PDEVINST DeviceInstance;
|
|
|
|
Len = NumDevInst * sizeof(DEVINST);
|
|
DeviceInstance = (PDEVINST)LocalReAlloc(DevinstRelations, Len, LMEM_FIXED);
|
|
|
|
if (DeviceInstance) {
|
|
|
|
DevinstRelations = DeviceInstance;
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
LocalFree(DevinstRelations);
|
|
DevinstRelations = NULL;
|
|
}
|
|
}
|
|
|
|
*pNumDevinst = NumDevInst;
|
|
return DevinstRelations;
|
|
}
|
|
|
|
LONG
|
|
AddChildSiblings(
|
|
PDEVICETREE DeviceTree,
|
|
PDEVTREENODE ParentNode,
|
|
DEVINST DeviceInstance,
|
|
int TreeDepth,
|
|
BOOL Recurse
|
|
)
|
|
{
|
|
DWORD Len;
|
|
CONFIGRET ConfigRet;
|
|
DEVINST ChildDeviceInstance;
|
|
PDEVTREENODE DeviceTreeNode;
|
|
PLIST_ENTRY ChildSiblingList;
|
|
TCHAR Buffer[MAX_PATH];
|
|
DWORD NumRelations;
|
|
PDEVINST pDevInst;
|
|
|
|
ChildSiblingList = ParentNode ? &ParentNode->ChildSiblingList
|
|
: &DeviceTree->ChildSiblingList;
|
|
|
|
if (!ParentNode) {
|
|
|
|
InitializeListHead(ChildSiblingList);
|
|
}
|
|
|
|
if (TreeDepth > DeviceTree->TreeDepth) {
|
|
|
|
DeviceTree->TreeDepth = TreeDepth;
|
|
}
|
|
|
|
do {
|
|
DeviceTreeNode = (PDEVTREENODE)LocalAlloc(LPTR, sizeof(DEVTREENODE));
|
|
|
|
if (!DeviceTreeNode) {
|
|
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
memset(DeviceTreeNode, 0, sizeof(DEVTREENODE));
|
|
InsertTailList(ChildSiblingList, &DeviceTreeNode->SiblingEntry);
|
|
|
|
DeviceTreeNode->ParentNode = ParentNode;
|
|
|
|
//
|
|
// fill in info about this device instance.
|
|
//
|
|
InitializeListHead(&DeviceTreeNode->ChildSiblingList);
|
|
DeviceTreeNode->DevInst = DeviceInstance;
|
|
DeviceTreeNode->TreeDepth = TreeDepth;
|
|
|
|
//
|
|
// Get ClassGUID, and class name.
|
|
//
|
|
Len = sizeof(Buffer);
|
|
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(DeviceInstance,
|
|
CM_DRP_CLASSGUID,
|
|
NULL,
|
|
&Buffer,
|
|
&Len,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
|
|
if (ConfigRet == CR_SUCCESS) {
|
|
|
|
pSetupGuidFromString(Buffer, &DeviceTreeNode->ClassGuid);
|
|
}
|
|
|
|
|
|
//
|
|
// try fetching the class name from the Class GUID.
|
|
// if that fails we will use the devnodes classname
|
|
//
|
|
*Buffer = TEXT('\0');
|
|
Len = SIZECHARS(Buffer);
|
|
ConfigRet = CM_Get_Class_Name_Ex(&DeviceTreeNode->ClassGuid,
|
|
Buffer,
|
|
&Len,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
if (ConfigRet == CR_SUCCESS && *Buffer) {
|
|
|
|
Len *= sizeof(TCHAR); // len includes term null.
|
|
}
|
|
|
|
else if (ConfigRet != CR_SUCCESS || !*Buffer) {
|
|
|
|
*Buffer = TEXT('\0');
|
|
Len = sizeof(Buffer);
|
|
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(
|
|
DeviceInstance,
|
|
CM_DRP_CLASS,
|
|
NULL,
|
|
(PVOID)Buffer,
|
|
&Len,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
if (ConfigRet != CR_SUCCESS || !*Buffer) {
|
|
|
|
Len = 0;
|
|
}
|
|
}
|
|
|
|
if (Len) {
|
|
|
|
DeviceTreeNode->ClassName = (PTCHAR)LocalAlloc(LPTR, Len);
|
|
|
|
if (DeviceTreeNode->ClassName) {
|
|
|
|
memcpy(DeviceTreeNode->ClassName, Buffer, Len);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Drive list
|
|
//
|
|
DeviceTreeNode->DriveList = DevNodeToDriveLetter(DeviceInstance);
|
|
|
|
//
|
|
// FriendlyName
|
|
//
|
|
*Buffer = TEXT('\0');
|
|
Len = sizeof(Buffer);
|
|
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(DeviceInstance,
|
|
CM_DRP_FRIENDLYNAME,
|
|
NULL,
|
|
Buffer,
|
|
&Len,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
if (ConfigRet == CR_SUCCESS && *Buffer) {
|
|
|
|
if (DeviceTreeNode->DriveList) {
|
|
|
|
Len += lstrlen(DeviceTreeNode->DriveList) * sizeof(TCHAR);
|
|
}
|
|
|
|
DeviceTreeNode->FriendlyName = (PTCHAR)LocalAlloc(LPTR, Len);
|
|
|
|
if (DeviceTreeNode->FriendlyName) {
|
|
|
|
memcpy(DeviceTreeNode->FriendlyName, Buffer, Len);
|
|
|
|
if (DeviceTreeNode->DriveList) {
|
|
|
|
lstrcat(DeviceTreeNode->FriendlyName, DeviceTreeNode->DriveList);
|
|
}
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
DeviceTreeNode->FriendlyName = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// DeviceDesc
|
|
//
|
|
*Buffer = TEXT('\0');
|
|
Len = sizeof(Buffer);
|
|
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(
|
|
DeviceInstance,
|
|
CM_DRP_DEVICEDESC,
|
|
NULL,
|
|
(PVOID)Buffer,
|
|
&Len,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
if (ConfigRet == CR_SUCCESS && *Buffer) {
|
|
|
|
if (DeviceTreeNode->DriveList) {
|
|
|
|
Len += lstrlen(DeviceTreeNode->DriveList) * sizeof(TCHAR);
|
|
}
|
|
|
|
DeviceTreeNode->DeviceDesc = (PTCHAR)LocalAlloc(LPTR, Len);
|
|
|
|
if (DeviceTreeNode->DeviceDesc) {
|
|
|
|
memcpy(DeviceTreeNode->DeviceDesc, Buffer, Len);
|
|
|
|
if (DeviceTreeNode->DriveList) {
|
|
|
|
lstrcat(DeviceTreeNode->DeviceDesc, DeviceTreeNode->DriveList);
|
|
}
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
DeviceTreeNode->DeviceDesc = NULL;
|
|
}
|
|
|
|
//
|
|
// Device capabilities
|
|
//
|
|
Len = sizeof(DeviceTreeNode->Capabilities);
|
|
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(
|
|
DeviceInstance,
|
|
CM_DRP_CAPABILITIES,
|
|
NULL,
|
|
(PVOID)&DeviceTreeNode->Capabilities,
|
|
&Len,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
if (ConfigRet != CR_SUCCESS) {
|
|
|
|
DeviceTreeNode->Capabilities = 0;
|
|
}
|
|
|
|
//
|
|
// Status and Problem number
|
|
//
|
|
ConfigRet = CM_Get_DevNode_Status_Ex(&DeviceTreeNode->DevNodeStatus,
|
|
&DeviceTreeNode->Problem,
|
|
DeviceInstance,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
if (ConfigRet != CR_SUCCESS) {
|
|
|
|
DeviceTreeNode->DevNodeStatus = 0;
|
|
DeviceTreeNode->Problem = 0;
|
|
}
|
|
|
|
//
|
|
// We need to do the following special case. If a device is not started and
|
|
// it doesn't have a problem and it is a RAW device then give it a problem
|
|
// CM_PROB_FAILED_START.
|
|
//
|
|
if (!(DeviceTreeNode->DevNodeStatus & DN_STARTED) &&
|
|
!(DeviceTreeNode->DevNodeStatus & DN_HAS_PROBLEM) &&
|
|
(DeviceTreeNode->Capabilities & CM_DEVCAP_RAWDEVICEOK)) {
|
|
|
|
DeviceTreeNode->Problem = CM_PROB_FAILED_START;
|
|
}
|
|
|
|
//
|
|
// LocationInformation
|
|
//
|
|
DeviceTreeNode->Location = BuildLocationInformation(DeviceInstance,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
|
|
//
|
|
// Get InstanceId
|
|
//
|
|
*Buffer = TEXT('\0');
|
|
Len = sizeof(Buffer);
|
|
ConfigRet = CM_Get_Device_ID_ExW(DeviceInstance,
|
|
Buffer,
|
|
Len/sizeof(TCHAR),
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
if (ConfigRet == CR_SUCCESS && *Buffer) {
|
|
|
|
Len = lstrlen(Buffer) * sizeof(TCHAR) + sizeof(TCHAR);
|
|
DeviceTreeNode->InstanceId = (PTCHAR)LocalAlloc(LPTR, Len);
|
|
|
|
if (DeviceTreeNode->InstanceId) {
|
|
|
|
memcpy(DeviceTreeNode->InstanceId, Buffer, Len);
|
|
}
|
|
}
|
|
|
|
else {
|
|
|
|
DeviceTreeNode->InstanceId = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Fetch removal and eject relations
|
|
//
|
|
if (ConfigRet == CR_SUCCESS) {
|
|
|
|
DeviceTreeNode->EjectRelations = BuildDeviceRelationsList(
|
|
Buffer,
|
|
DeviceTree->hMachine,
|
|
CM_GETIDLIST_FILTER_EJECTRELATIONS,
|
|
&DeviceTreeNode->NumEjectRelations
|
|
);
|
|
|
|
DeviceTreeNode->RemovalRelations = BuildDeviceRelationsList(
|
|
Buffer,
|
|
DeviceTree->hMachine,
|
|
CM_GETIDLIST_FILTER_REMOVALRELATIONS,
|
|
&DeviceTreeNode->NumRemovalRelations
|
|
);
|
|
}
|
|
|
|
//
|
|
// Only get children and siblings if the Recurse value was TRUE
|
|
// otherwise we will just build a DeviceTreeNode structure for
|
|
// the individual devnode that was passed in.
|
|
//
|
|
// Also, only add rejection and removal relations when Recurse is TRUE,
|
|
// otherwise we get messed up if two devices have removal or ejection
|
|
// relations to each other.
|
|
//
|
|
if (Recurse) {
|
|
|
|
LPTSTR tempDriveList;
|
|
|
|
DeviceTreeNode->bCopy = FALSE;
|
|
|
|
//
|
|
// Add Ejection relation drive letters
|
|
//
|
|
NumRelations = DeviceTreeNode->NumEjectRelations;
|
|
pDevInst = DeviceTreeNode->EjectRelations;
|
|
|
|
while (NumRelations--) {
|
|
|
|
if (tempDriveList = DevNodeToDriveLetter(*pDevInst)) {
|
|
|
|
AddChildSiblings(DeviceTree,
|
|
DeviceTreeNode,
|
|
*pDevInst,
|
|
TreeDepth+1,
|
|
FALSE
|
|
);
|
|
|
|
LocalFree(tempDriveList);
|
|
}
|
|
|
|
pDevInst++;
|
|
}
|
|
|
|
//
|
|
// Add Removal relation drive letters
|
|
//
|
|
NumRelations = DeviceTreeNode->NumRemovalRelations;
|
|
pDevInst = DeviceTreeNode->RemovalRelations;
|
|
|
|
while (NumRelations--) {
|
|
|
|
if (tempDriveList = DevNodeToDriveLetter(*pDevInst)) {
|
|
|
|
AddChildSiblings(DeviceTree,
|
|
DeviceTreeNode,
|
|
*pDevInst,
|
|
TreeDepth+1,
|
|
FALSE
|
|
);
|
|
|
|
LocalFree(tempDriveList);
|
|
}
|
|
|
|
pDevInst++;
|
|
}
|
|
|
|
//
|
|
// If this devinst has children, then recurse to fill in its
|
|
// child sibling list.
|
|
//
|
|
ConfigRet = CM_Get_Child_Ex(&ChildDeviceInstance,
|
|
DeviceInstance,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
|
|
if (ConfigRet == CR_SUCCESS) {
|
|
|
|
|
|
AddChildSiblings(DeviceTree,
|
|
DeviceTreeNode,
|
|
ChildDeviceInstance,
|
|
TreeDepth+1,
|
|
TRUE
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Next sibling ...
|
|
//
|
|
|
|
ConfigRet = CM_Get_Sibling_Ex(&DeviceInstance,
|
|
DeviceInstance,
|
|
0,
|
|
DeviceTree->hMachine
|
|
);
|
|
} else {
|
|
|
|
//
|
|
// If Recurse is FALSE then we are making a Copy of an already existing DeviceTreeNode.
|
|
// We do this when a HotPlug Device has a relation that will get removed when it gets
|
|
// removed. We need to set the bCopy flag because in certain cases the HotPlug device's
|
|
// relation is also a HotPlug device. If we don't mark that it is a copy then it will
|
|
// get added to the list of removeable devices twice.
|
|
//
|
|
DeviceTreeNode->bCopy = TRUE;
|
|
}
|
|
|
|
} while (Recurse && (ConfigRet == CR_SUCCESS));
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
void
|
|
RemoveChildSiblings(
|
|
PDEVICETREE DeviceTree,
|
|
PLIST_ENTRY ChildSiblingList
|
|
)
|
|
{
|
|
PLIST_ENTRY Next;
|
|
TV_INSERTSTRUCT tvi;
|
|
PDEVTREENODE DeviceTreeNode;
|
|
|
|
Next = ChildSiblingList->Flink;
|
|
while (Next != ChildSiblingList) {
|
|
|
|
DeviceTreeNode = CONTAINING_RECORD(Next, DEVTREENODE, SiblingEntry);
|
|
|
|
|
|
//
|
|
// recurse to free this nodes ChildSiblingList
|
|
//
|
|
if (!IsListEmpty(&DeviceTreeNode->ChildSiblingList)) {
|
|
|
|
RemoveChildSiblings(DeviceTree,
|
|
&DeviceTreeNode->ChildSiblingList
|
|
);
|
|
}
|
|
|
|
//
|
|
// free up this node and move on to the next sibling.
|
|
//
|
|
Next = Next->Flink;
|
|
RemoveEntryList(&DeviceTreeNode->SiblingEntry);
|
|
|
|
if (DeviceTreeNode->ClassName) {
|
|
|
|
LocalFree(DeviceTreeNode->ClassName);
|
|
}
|
|
|
|
if (DeviceTreeNode->FriendlyName) {
|
|
|
|
LocalFree(DeviceTreeNode->FriendlyName);
|
|
}
|
|
|
|
if (DeviceTreeNode->DeviceDesc) {
|
|
|
|
LocalFree(DeviceTreeNode->DeviceDesc);
|
|
}
|
|
|
|
if (DeviceTreeNode->DriveList) {
|
|
|
|
LocalFree(DeviceTreeNode->DriveList);
|
|
}
|
|
|
|
if (DeviceTreeNode->Location) {
|
|
|
|
LocalFree(DeviceTreeNode->Location);
|
|
}
|
|
|
|
if (DeviceTreeNode->InstanceId) {
|
|
|
|
LocalFree(DeviceTreeNode->InstanceId);
|
|
}
|
|
|
|
if (DeviceTreeNode->EjectRelations) {
|
|
|
|
LocalFree(DeviceTreeNode->EjectRelations);
|
|
}
|
|
|
|
if (DeviceTreeNode->RemovalRelations) {
|
|
|
|
LocalFree(DeviceTreeNode->RemovalRelations);
|
|
}
|
|
|
|
if (DeviceTree->SelectedTreeNode == DeviceTreeNode) {
|
|
|
|
DeviceTree->SelectedTreeNode = NULL;
|
|
}
|
|
|
|
memset(DeviceTreeNode, 0, sizeof(DeviceTreeNode));
|
|
LocalFree(DeviceTreeNode);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Retrives the UI Device Name from the DeviceTreeNode.
|
|
* It is chosen from the following device registry fields
|
|
* (in order of preference):
|
|
* - FriendlyName
|
|
* - DeviceDescription
|
|
* - ClassName
|
|
* - Location
|
|
*
|
|
* if none of the above is available uses "Unknown"
|
|
*
|
|
*/
|
|
PTCHAR
|
|
FetchDeviceName(
|
|
PDEVTREENODE DeviceTreeNode
|
|
)
|
|
{
|
|
if (DeviceTreeNode->FriendlyName) {
|
|
|
|
return DeviceTreeNode->FriendlyName;
|
|
}
|
|
|
|
if (DeviceTreeNode->DeviceDesc) {
|
|
|
|
return DeviceTreeNode->DeviceDesc;
|
|
}
|
|
|
|
if (DeviceTreeNode->ClassName &&
|
|
!IsEqualGUID(DeviceTreeNode->ClassGuid, GUID_NULL) &&
|
|
!IsEqualGUID(DeviceTreeNode->ClassGuid, GUID_DEVCLASS_UNKNOWN))
|
|
{
|
|
return DeviceTreeNode->ClassName;
|
|
}
|
|
|
|
//
|
|
// darn everything failed
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
BOOL
|
|
DisplayChildSiblings(
|
|
PDEVICETREE DeviceTree,
|
|
PLIST_ENTRY ChildSiblingList,
|
|
HTREEITEM hParentTreeItem,
|
|
BOOL HotPlugParent
|
|
)
|
|
{
|
|
PLIST_ENTRY Next;
|
|
CONFIGRET ConfigRet;
|
|
PDEVTREENODE DeviceTreeNode;
|
|
TV_INSERTSTRUCT tvi;
|
|
BOOL ChildDisplayed = FALSE;
|
|
|
|
Next = ChildSiblingList->Flink;
|
|
while (Next != ChildSiblingList) {
|
|
|
|
DeviceTreeNode = CONTAINING_RECORD(Next, DEVTREENODE, SiblingEntry);
|
|
|
|
//
|
|
// - If this device has a hotplug parent and we are in the complex view then
|
|
// add this device to the tree.
|
|
// - If this device is a hotplug device and it is not a bCopy then add this device
|
|
// to the tree. A bCopy device is one where we create another DeviceTreeNode structure
|
|
// for a device that is a relation of a hotplug device. The problem is that this relation
|
|
// itself could be a hotplug device and we don't want to show to copies of it in the UI.
|
|
//
|
|
if (!DeviceTree->HotPlugTree ||
|
|
(HotPlugParent && DeviceTree->ComplexView) ||
|
|
(!DeviceTreeNode->bCopy && IsHotPlugDevice(DeviceTreeNode->DevInst, DeviceTree->hMachine)))
|
|
{
|
|
tvi.item.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE ;
|
|
|
|
if (SetupDiGetClassImageIndex(&DeviceTree->ClassImageList,
|
|
&DeviceTreeNode->ClassGuid,
|
|
&tvi.item.iImage
|
|
))
|
|
{
|
|
tvi.item.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
|
|
}
|
|
|
|
tvi.hParent = hParentTreeItem;
|
|
tvi.hInsertAfter = TVI_LAST;
|
|
tvi.item.iSelectedImage = tvi.item.iImage;
|
|
tvi.item.pszText = FetchDeviceName(DeviceTreeNode);
|
|
|
|
if (!tvi.item.pszText) {
|
|
|
|
tvi.item.pszText = szUnknown;
|
|
}
|
|
|
|
tvi.item.lParam = (LPARAM)DeviceTreeNode;
|
|
tvi.item.stateMask = TVIS_OVERLAYMASK;
|
|
|
|
if (DeviceTreeNode->Problem == CM_PROB_DISABLED) {
|
|
|
|
tvi.item.state = INDEXTOOVERLAYMASK(IDI_DISABLED_OVL - IDI_CLASSICON_OVERLAYFIRST + 1);
|
|
|
|
} else if (DeviceTreeNode->Problem) {
|
|
|
|
tvi.item.state = INDEXTOOVERLAYMASK(IDI_PROBLEM_OVL - IDI_CLASSICON_OVERLAYFIRST + 1);
|
|
|
|
} else {
|
|
|
|
tvi.item.state = INDEXTOOVERLAYMASK(0);
|
|
}
|
|
|
|
DeviceTreeNode->hTreeItem = TreeView_InsertItem(DeviceTree->hwndTree, &tvi);
|
|
ChildDisplayed = TRUE;
|
|
|
|
} else {
|
|
|
|
DeviceTreeNode->hTreeItem = NULL;
|
|
}
|
|
|
|
//
|
|
// recurse to display this nodes ChildSiblingList
|
|
//
|
|
if (!IsListEmpty(&DeviceTreeNode->ChildSiblingList)) {
|
|
|
|
if (DisplayChildSiblings(DeviceTree,
|
|
&DeviceTreeNode->ChildSiblingList,
|
|
DeviceTree->ComplexView ? DeviceTreeNode->hTreeItem
|
|
: hParentTreeItem,
|
|
DeviceTreeNode->hTreeItem != NULL
|
|
))
|
|
{
|
|
ChildDisplayed = TRUE;
|
|
|
|
//
|
|
// if we are at the root expand the list of child items.
|
|
//
|
|
if (DeviceTreeNode->hTreeItem && DeviceTree->ComplexView) {
|
|
|
|
TreeView_Expand(DeviceTree->hwndTree,
|
|
DeviceTreeNode->hTreeItem,
|
|
TVE_EXPAND
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// and on to the next sibling.
|
|
//
|
|
Next = Next->Flink;
|
|
|
|
}
|
|
|
|
return ChildDisplayed;
|
|
}
|
|
|
|
void
|
|
AddChildRemoval(
|
|
PDEVICETREE DeviceTree,
|
|
PLIST_ENTRY ChildSiblingList
|
|
)
|
|
{
|
|
PLIST_ENTRY Next;
|
|
PDEVTREENODE DeviceTreeNode;
|
|
PDEVTREENODE FirstDeviceTreeNode;
|
|
|
|
if (IsListEmpty(ChildSiblingList)) {
|
|
|
|
return;
|
|
}
|
|
|
|
FirstDeviceTreeNode = DeviceTree->ChildRemovalList;
|
|
|
|
Next = ChildSiblingList->Flink;
|
|
while (Next != ChildSiblingList) {
|
|
|
|
DeviceTreeNode = CONTAINING_RECORD(Next, DEVTREENODE, SiblingEntry);
|
|
|
|
DeviceTreeNode->NextChildRemoval = FirstDeviceTreeNode->NextChildRemoval;
|
|
FirstDeviceTreeNode->NextChildRemoval = DeviceTreeNode;
|
|
|
|
InvalidateTreeItemRect(DeviceTree->hwndTree, DeviceTreeNode->hTreeItem);
|
|
|
|
//
|
|
// recurse to Add this node's Childs
|
|
//
|
|
AddChildRemoval(DeviceTree, &DeviceTreeNode->ChildSiblingList);
|
|
|
|
//
|
|
// and on to the next sibling.
|
|
//
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
ClearRemovalList(
|
|
PDEVICETREE DeviceTree
|
|
|
|
)
|
|
{
|
|
PDEVTREENODE Next;
|
|
PDEVTREENODE DeviceTreeNode;
|
|
|
|
DeviceTreeNode = DeviceTree->ChildRemovalList;
|
|
|
|
if (!DeviceTreeNode) {
|
|
|
|
return;
|
|
}
|
|
|
|
do {
|
|
|
|
Next = DeviceTreeNode->NextChildRemoval;
|
|
DeviceTreeNode->NextChildRemoval = NULL;
|
|
|
|
//
|
|
// force redraw of this item to reset the colors
|
|
//
|
|
InvalidateTreeItemRect(DeviceTree->hwndTree, DeviceTreeNode->hTreeItem);
|
|
|
|
DeviceTreeNode = Next;
|
|
|
|
} while (DeviceTreeNode != DeviceTree->ChildRemovalList);
|
|
|
|
|
|
DeviceTree->ChildRemovalList = NULL;
|
|
}
|
|
|
|
PDEVTREENODE
|
|
DevTreeNodeByInstanceId(
|
|
PTCHAR InstanceId,
|
|
PLIST_ENTRY ChildSiblingList
|
|
)
|
|
{
|
|
PLIST_ENTRY Next;
|
|
PDEVTREENODE DeviceTreeNode;
|
|
|
|
if (!InstanceId) {
|
|
return NULL;
|
|
}
|
|
|
|
Next = ChildSiblingList->Flink;
|
|
while (Next != ChildSiblingList) {
|
|
|
|
DeviceTreeNode = CONTAINING_RECORD(Next, DEVTREENODE, SiblingEntry);
|
|
|
|
if (DeviceTreeNode->InstanceId &&
|
|
!lstrcmp(DeviceTreeNode->InstanceId, InstanceId))
|
|
{
|
|
return DeviceTreeNode;
|
|
}
|
|
|
|
//
|
|
// recurse to display this nodes ChildSiblingList
|
|
//
|
|
if (!IsListEmpty(&DeviceTreeNode->ChildSiblingList)) {
|
|
|
|
DeviceTreeNode = DevTreeNodeByInstanceId(InstanceId,
|
|
&DeviceTreeNode->ChildSiblingList
|
|
);
|
|
if (DeviceTreeNode) {
|
|
|
|
return DeviceTreeNode;
|
|
}
|
|
}
|
|
|
|
//
|
|
// and on to the next sibling.
|
|
//
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PDEVTREENODE
|
|
DevTreeNodeByDevInst(
|
|
DEVINST DevInst,
|
|
PLIST_ENTRY ChildSiblingList
|
|
)
|
|
{
|
|
PLIST_ENTRY Next;
|
|
PDEVTREENODE DeviceTreeNode;
|
|
|
|
if (!DevInst) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Next = ChildSiblingList->Flink;
|
|
while (Next != ChildSiblingList) {
|
|
|
|
DeviceTreeNode = CONTAINING_RECORD(Next, DEVTREENODE, SiblingEntry);
|
|
|
|
//
|
|
// We currently assume that we can compare DEVINST from separate
|
|
// "CM_Locate_Devnode" invocations, since a DEVINST is not a real
|
|
// handle, but a pointer to a globalstring table.
|
|
//
|
|
if (DevInst == DeviceTreeNode->DevInst) {
|
|
|
|
return DeviceTreeNode;
|
|
}
|
|
|
|
//
|
|
// recurse to display this nodes ChildSiblingList
|
|
//
|
|
if (!IsListEmpty(&DeviceTreeNode->ChildSiblingList)) {
|
|
|
|
DeviceTreeNode = DevTreeNodeByDevInst(DevInst,
|
|
&DeviceTreeNode->ChildSiblingList
|
|
);
|
|
if (DeviceTreeNode) {
|
|
|
|
return DeviceTreeNode;
|
|
}
|
|
}
|
|
|
|
//
|
|
// and on to the next sibling.
|
|
//
|
|
Next = Next->Flink;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PDEVTREENODE
|
|
TopLevelRemovalNode(
|
|
PDEVICETREE DeviceTree,
|
|
PDEVTREENODE DeviceTreeNode
|
|
)
|
|
{
|
|
PDEVTREENODE ParentNode = DeviceTreeNode;
|
|
|
|
while (ParentNode) {
|
|
|
|
DeviceTreeNode = ParentNode;
|
|
|
|
if (IsHotPlugDevice(ParentNode->DevInst, DeviceTree->hMachine)) {
|
|
|
|
return ParentNode;
|
|
}
|
|
|
|
ParentNode = ParentNode->ParentNode;
|
|
}
|
|
|
|
return DeviceTreeNode;
|
|
}
|
|
|
|
void
|
|
AddEjectToRemoval(
|
|
PDEVICETREE DeviceTree
|
|
)
|
|
{
|
|
PDEVTREENODE RelationTreeNode;
|
|
PDEVTREENODE DeviceTreeNode;
|
|
PDEVINST pDevInst;
|
|
USHORT NumRelations;
|
|
|
|
|
|
//
|
|
// For each DeviceTreeNode in the removal list
|
|
// If it has ejection or removal relations, add it to the removal list.
|
|
//
|
|
DeviceTreeNode = DeviceTree->ChildRemovalList;
|
|
if (!DeviceTreeNode) {
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
do {
|
|
|
|
//
|
|
// Ejection Relations
|
|
//
|
|
NumRelations = DeviceTreeNode->NumEjectRelations;
|
|
pDevInst = DeviceTreeNode->EjectRelations;
|
|
|
|
while (NumRelations--) {
|
|
|
|
RelationTreeNode = DevTreeNodeByDevInst(*pDevInst++,
|
|
&DeviceTree->ChildSiblingList
|
|
);
|
|
|
|
//
|
|
// If we can't get a DeviceTreeNode for this device, or it is already
|
|
// in the list of devices that will be removed (it's NextChildRemoval)
|
|
// is non-NULL then we won't add this device to the list that will be removed.
|
|
//
|
|
// If this is a drive letter devnode then we have also already added it to
|
|
// the list so we can skip it.
|
|
//
|
|
if (!RelationTreeNode ||
|
|
RelationTreeNode->NextChildRemoval ||
|
|
RelationTreeNode->DriveList) {
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Insert the new devtreenode
|
|
//
|
|
RelationTreeNode->NextChildRemoval = DeviceTreeNode->NextChildRemoval;
|
|
DeviceTreeNode->NextChildRemoval = RelationTreeNode;
|
|
}
|
|
|
|
//
|
|
// Removal Relations
|
|
//
|
|
NumRelations = DeviceTreeNode->NumRemovalRelations;
|
|
pDevInst = DeviceTreeNode->RemovalRelations;
|
|
|
|
while (NumRelations--) {
|
|
|
|
RelationTreeNode = DevTreeNodeByDevInst(*pDevInst++,
|
|
&DeviceTree->ChildSiblingList
|
|
);
|
|
|
|
//
|
|
// If we can't get a DeviceTreeNode for this device, or it is already
|
|
// in the list of devices that will be removed (it's NextChildRemoval)
|
|
// is non-NULL then we won't add this device to the list that will be removed.
|
|
//
|
|
// If this is a drive letter devnode then we have also already added it to
|
|
// the list so we can skip it.
|
|
//
|
|
if (!RelationTreeNode ||
|
|
RelationTreeNode->NextChildRemoval ||
|
|
RelationTreeNode->DriveList) {
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// Insert the new devtreenode
|
|
//
|
|
RelationTreeNode->NextChildRemoval = DeviceTreeNode->NextChildRemoval;
|
|
DeviceTreeNode->NextChildRemoval = RelationTreeNode;
|
|
}
|
|
|
|
|
|
//
|
|
// And on to the next node.
|
|
//
|
|
|
|
DeviceTreeNode = DeviceTreeNode->NextChildRemoval;
|
|
|
|
} while (DeviceTreeNode != DeviceTree->ChildRemovalList);
|
|
}
|