Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1015 lines
29 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation
//
// File: devtree.cpp
//
//--------------------------------------------------------------------------
#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,
ULONG FilterFlag,
PUSHORT pNumDevinst
)
{
ULONG cchSize, cbSize, MaxDevinst;
USHORT NumDevInst;
CONFIGRET ConfigRet;
PTCHAR DeviceIdRelations = NULL, CurrDevId;
PDEVINST DevinstRelations = NULL;
DevinstRelations = NULL;
DeviceIdRelations = NULL;
NumDevInst = 0;
cchSize = 0;
ConfigRet = CM_Get_Device_ID_List_Size_Ex(&cchSize,
DeviceId,
FilterFlag,
NULL
);
if ((ConfigRet != CR_SUCCESS) || !cchSize) {
goto BDEarlyExit;
}
DeviceIdRelations = (PTCHAR)LocalAlloc(LPTR, cchSize*sizeof(TCHAR));
if (!DeviceIdRelations) {
goto BDEarlyExit;
}
*DeviceIdRelations = TEXT('\0');
if (DeviceIdRelations) {
ConfigRet = CM_Get_Device_ID_List_Ex(DeviceId,
DeviceIdRelations,
cchSize,
FilterFlag,
NULL
);
if (ConfigRet != CR_SUCCESS || !*DeviceIdRelations) {
goto BDEarlyExit;
}
}
//
// Count up the number of Device Instance Ids in the list so we know how
// big to make our array of devnodes.
//
MaxDevinst = 0;
for (CurrDevId = DeviceIdRelations; *CurrDevId; CurrDevId += lstrlen(CurrDevId) + 1) {
MaxDevinst++;
}
if (MaxDevinst == 0) {
goto BDEarlyExit;
}
DevinstRelations = (PDEVINST)LocalAlloc(LPTR, MaxDevinst * sizeof(DEVNODE));
if (!DevinstRelations) {
goto BDEarlyExit;
}
for (CurrDevId = DeviceIdRelations; *CurrDevId; CurrDevId += lstrlen(CurrDevId) + 1) {
ConfigRet = CM_Locate_DevNode_Ex(&DevinstRelations[NumDevInst],
CurrDevId,
CM_LOCATE_DEVNODE_NORMAL,
NULL
);
if (ConfigRet == CR_SUCCESS) {
++NumDevInst;
}
}
BDEarlyExit:
if (DeviceIdRelations) {
LocalFree(DeviceIdRelations);
}
if (DevinstRelations) {
if (NumDevInst == 0) {
//
// If we coundn't get any devnodes then return NULL
//
LocalFree(DevinstRelations);
DevinstRelations = NULL;
}
}
*pNumDevinst = NumDevInst;
return DevinstRelations;
}
LONG
AddChildSiblings(
PDEVICETREE DeviceTree,
PDEVTREENODE ParentNode,
DEVINST DeviceInstance,
int TreeDepth,
BOOL Recurse
)
{
DWORD cbSize;
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;
}
ZeroMemory(DeviceTreeNode, 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.
//
cbSize = sizeof(Buffer);
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(DeviceInstance,
CM_DRP_CLASSGUID,
NULL,
Buffer,
&cbSize,
0,
NULL
);
if (ConfigRet == CR_SUCCESS) {
pSetupGuidFromString(Buffer, &DeviceTreeNode->ClassGuid);
}
//
// Drive list
//
DeviceTreeNode->DriveList = DevNodeToDriveLetter(DeviceInstance);
//
// FriendlyName
//
*Buffer = TEXT('\0');
cbSize = sizeof(Buffer);
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(DeviceInstance,
CM_DRP_FRIENDLYNAME,
NULL,
Buffer,
&cbSize,
0,
NULL
);
if (ConfigRet == CR_SUCCESS && *Buffer) {
if (DeviceTreeNode->DriveList) {
cbSize += lstrlen(DeviceTreeNode->DriveList) * sizeof(TCHAR);
}
DeviceTreeNode->FriendlyName = (PTCHAR)LocalAlloc(LPTR, cbSize);
if (DeviceTreeNode->FriendlyName) {
StringCbCopy(DeviceTreeNode->FriendlyName,
cbSize,
Buffer);
if (DeviceTreeNode->DriveList) {
StringCbCat(DeviceTreeNode->FriendlyName,
cbSize,
DeviceTreeNode->DriveList);
}
}
}
else {
DeviceTreeNode->FriendlyName = NULL;
}
//
// DeviceDesc
//
*Buffer = TEXT('\0');
cbSize = sizeof(Buffer);
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(
DeviceInstance,
CM_DRP_DEVICEDESC,
NULL,
(PVOID)Buffer,
&cbSize,
0,
NULL
);
if (ConfigRet == CR_SUCCESS && *Buffer) {
if (DeviceTreeNode->DriveList) {
cbSize += lstrlen(DeviceTreeNode->DriveList) * sizeof(TCHAR);
}
DeviceTreeNode->DeviceDesc = (PTCHAR)LocalAlloc(LPTR, cbSize);
if (DeviceTreeNode->DeviceDesc) {
StringCbCopy(DeviceTreeNode->DeviceDesc,
cbSize,
Buffer);
if (DeviceTreeNode->DriveList) {
StringCbCat(DeviceTreeNode->DeviceDesc,
cbSize,
DeviceTreeNode->DriveList);
}
}
}
else {
DeviceTreeNode->DeviceDesc = NULL;
}
//
// Device capabilities
//
cbSize = sizeof(DeviceTreeNode->Capabilities);
ConfigRet = CM_Get_DevNode_Registry_Property_Ex(
DeviceInstance,
CM_DRP_CAPABILITIES,
NULL,
(PVOID)&DeviceTreeNode->Capabilities,
&cbSize,
0,
NULL
);
if (ConfigRet != CR_SUCCESS) {
DeviceTreeNode->Capabilities = 0;
}
//
// Status and Problem number
//
ConfigRet = CM_Get_DevNode_Status_Ex(&DeviceTreeNode->DevNodeStatus,
&DeviceTreeNode->Problem,
DeviceInstance,
0,
NULL
);
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);
//
// Get InstanceId
//
*Buffer = TEXT('\0');
ConfigRet = CM_Get_Device_ID_ExW(DeviceInstance,
Buffer,
SIZECHARS(Buffer),
0,
NULL
);
if (ConfigRet == CR_SUCCESS && *Buffer) {
cbSize = lstrlen(Buffer) * sizeof(TCHAR) + sizeof(TCHAR);
DeviceTreeNode->InstanceId = (PTCHAR)LocalAlloc(LPTR, cbSize);
if (DeviceTreeNode->InstanceId) {
StringCbCopy(DeviceTreeNode->InstanceId, cbSize, Buffer);
}
}
else {
DeviceTreeNode->InstanceId = NULL;
}
//
// Fetch removal and eject relations
//
if (ConfigRet == CR_SUCCESS) {
DeviceTreeNode->EjectRelations = BuildDeviceRelationsList(
Buffer,
CM_GETIDLIST_FILTER_EJECTRELATIONS,
&DeviceTreeNode->NumEjectRelations
);
DeviceTreeNode->RemovalRelations = BuildDeviceRelationsList(
Buffer,
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)) != NULL) {
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)) != NULL) {
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,
NULL
);
if (ConfigRet == CR_SUCCESS) {
AddChildSiblings(DeviceTree,
DeviceTreeNode,
ChildDeviceInstance,
TreeDepth+1,
TRUE
);
}
//
// Next sibling ...
//
ConfigRet = CM_Get_Sibling_Ex(&DeviceInstance,
DeviceInstance,
0,
NULL
);
} 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;
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->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;
}
ZeroMemory(DeviceTreeNode, sizeof(DEVTREENODE));
LocalFree(DeviceTreeNode);
}
return;
}
PTCHAR
FetchDeviceName(
PDEVTREENODE DeviceTreeNode
)
{
if (DeviceTreeNode->FriendlyName) {
return DeviceTreeNode->FriendlyName;
}
if (DeviceTreeNode->DeviceDesc) {
return DeviceTreeNode->DeviceDesc;
}
return NULL;
}
BOOL
DisplayChildSiblings(
PDEVICETREE DeviceTree,
PLIST_ENTRY ChildSiblingList,
HTREEITEM hParentTreeItem,
BOOL HotPlugParent
)
{
PLIST_ENTRY Next;
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)))
{
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)) {
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);
}