|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation
//
// File: devicecol.cpp
//
// Description: This file handles device collections for the hotplug applet.
//
//--------------------------------------------------------------------------
#include "hotplug.h"
BOOL DeviceCollectionPrepImageList( IN PDEVICE_COLLECTION DeviceCollection );
VOID DeviceCollectionSwapFakeDockForRealDock( IN PDEVICE_COLLECTION DeviceCollection, IN PTSTR InstancePath, IN OUT DEVNODE *DeviceNode );
BOOL DeviceCollectionBuildFromPipe( IN HANDLE ReadPipe, IN COLLECTION_TYPE CollectionType, OUT PDEVICE_COLLECTION DeviceCollection ) { PTSTR deviceIds; PTSTR instancePath; BOOL bDockDeviceInList; PDEVICE_COLLECTION_ENTRY deviceEntry; DEVNODE deviceNode; DWORD capabilities, configFlags, cbSize; ULONG bytesReadFromPipe; ULONG numDevices, deviceIdsLength; BOOL success; CONFIGRET configRet; TCHAR classGuidString[MAX_GUID_STRING_LEN]; GUID classGuid;
//
// Preinit.
//
success = FALSE; deviceIds = NULL; deviceIdsLength = 0; bDockDeviceInList = FALSE; numDevices = 0;
DeviceCollection->NumDevices = 0; DeviceCollection->DockInList = FALSE; DeviceCollection->ClassImageList.cbSize = 0; InitializeListHead(&DeviceCollection->DeviceListHead);
//
// Our callers shouldn't have to handle an internal failure in any of this.
//
__try {
//
// Read the first ULONG from the pipe, this is the length of all the
// Device Ids.
//
if (!ReadFile(ReadPipe, (LPVOID)&deviceIdsLength, sizeof(ULONG), &bytesReadFromPipe, NULL) || (deviceIdsLength == 0)) {
goto clean0; }
//
// Allocate space to hold the DeviceIds
//
deviceIds = (PTSTR)LocalAlloc(LPTR, deviceIdsLength);
if (!deviceIds) {
goto clean0; }
//
// Read all of the DeviceIds from the pipe at once
//
if (!ReadFile(ReadPipe, (LPVOID)deviceIds, deviceIdsLength, &bytesReadFromPipe, NULL)) {
goto clean0; }
//
// Enumerate through the multi-sz list of Device Ids.
//
for (instancePath = deviceIds; *instancePath; instancePath += lstrlen(instancePath) + 1) {
deviceEntry = (PDEVICE_COLLECTION_ENTRY)LocalAlloc( LPTR, sizeof(DEVICE_COLLECTION_ENTRY) );
if (!deviceEntry) { goto clean0; }
//
// If we are building a blocked driver list, just put the driver
// GUID in the DeviceInstanceId field and continue to the next.
//
if (CollectionType == CT_BLOCKED_DRIVER_NOTIFICATION) { numDevices++; if (SUCCEEDED(StringCchCopy(deviceEntry->DeviceInstanceId, SIZECHARS(deviceEntry->DeviceInstanceId), instancePath)) && (pSetupGuidFromString(instancePath, &(deviceEntry->ClassGuid)) == ERROR_SUCCESS)) { InsertTailList( &DeviceCollection->DeviceListHead, &deviceEntry->Link ); } else { //
// Something went wrong, so free the DEVICE_COLLECTION_ENTRY
// node.
//
LocalFree(deviceEntry); deviceEntry = NULL; } } else { capabilities = 0; classGuid = GUID_NULL; if (CM_Locate_DevNode(&deviceNode, instancePath, (CollectionType == CT_CHILD_WITH_INVALID_ID_NOTIFICATION) ? 0 : CM_LOCATE_DEVNODE_PHANTOM) == CR_SUCCESS) { cbSize = sizeof(DWORD); configRet = CM_Get_DevNode_Registry_Property_Ex( deviceNode, CM_DRP_CAPABILITIES, NULL, (PVOID)&capabilities, &cbSize, 0, NULL ); if ((configRet == CR_SUCCESS) && (capabilities & CM_DEVCAP_DOCKDEVICE)) { DeviceCollectionSwapFakeDockForRealDock( DeviceCollection, instancePath, &deviceNode ); bDockDeviceInList = TRUE; } if (CollectionType == CT_SURPRISE_REMOVAL_WARNING) { //
// For surprise removal, we are careful to ignore any devices
// with the Suppress-Surprise flag set.
//
cbSize = sizeof(DWORD); configRet = CM_Get_DevNode_Registry_Property_Ex( deviceNode, CM_DRP_CONFIGFLAGS, NULL, (PVOID)&configFlags, &cbSize, 0, NULL ); if ((configRet == CR_SUCCESS) && (configFlags & CONFIGFLAG_SUPPRESS_SURPRISE)) { continue; } } //
// Get the class GUID string for the device
//
cbSize = sizeof(classGuidString); if ((CM_Get_DevNode_Registry_Property(deviceNode, CM_DRP_CLASSGUID, NULL, (PVOID)classGuidString, &cbSize, 0) != CR_SUCCESS) || (pSetupGuidFromString(classGuidString, &classGuid) != ERROR_SUCCESS)) {
classGuid = GUID_NULL; } } numDevices++; StringCchCopy(deviceEntry->DeviceInstanceId, SIZECHARS(deviceEntry->DeviceInstanceId), instancePath); deviceEntry->DeviceFriendlyName = BuildFriendlyName(deviceNode); deviceEntry->Capabilities = capabilities; deviceEntry->ClassGuid = classGuid; InsertTailList( &DeviceCollection->DeviceListHead, &deviceEntry->Link ); } }
DeviceCollection->NumDevices = numDevices; DeviceCollection->DockInList = bDockDeviceInList; DeviceCollectionPrepImageList(DeviceCollection); success = TRUE;
clean0: ;
} __except(EXCEPTION_EXECUTE_HANDLER) {
ASSERT(success == FALSE); ASSERT(0); }
if (deviceIds) {
LocalFree(deviceIds); }
if (!success) {
DeviceCollectionDestroy(DeviceCollection); }
return success; }
VOID DeviceCollectionDestroy( IN PDEVICE_COLLECTION DeviceCollection ) { PDEVICE_COLLECTION_ENTRY deviceEntry; PLIST_ENTRY listEntry;
while(!IsListEmpty(&DeviceCollection->DeviceListHead)) {
listEntry = RemoveHeadList(&DeviceCollection->DeviceListHead);
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link);
if (deviceEntry->DeviceFriendlyName) {
LocalFree(deviceEntry->DeviceFriendlyName); }
LocalFree(deviceEntry); }
if (DeviceCollection->ClassImageList.cbSize) {
SetupDiDestroyClassImageList(&DeviceCollection->ClassImageList); DeviceCollection->ClassImageList.cbSize = 0; }
DeviceCollection->NumDevices = 0; }
BOOL DeviceCollectionPrepImageList( IN PDEVICE_COLLECTION DeviceCollection ) { if (DeviceCollection->ClassImageList.cbSize != 0) {
//
// We already have an image list, no need to reacquire.
//
return TRUE; }
DeviceCollection->ClassImageList.cbSize = sizeof(SP_CLASSIMAGELIST_DATA); if (SetupDiGetClassImageList(&DeviceCollection->ClassImageList)) {
//
// Got it.
//
return TRUE; }
//
// Error path, put size back so we don't accidentally free garbage.
//
DeviceCollection->ClassImageList.cbSize = 0; return FALSE; }
VOID DeviceCollectionPopulateListView( IN PDEVICE_COLLECTION DeviceCollection, IN HWND ListHandle ) { LV_ITEM lviItem; LV_COLUMN lvcCol; PDEVICE_COLLECTION_ENTRY deviceEntry; PLIST_ENTRY listEntry; ULONG lvIndex; BOOL haveImageList = FALSE;
//
// Select in the correct image list.
//
if (DeviceCollectionPrepImageList(DeviceCollection)) {
ListView_SetImageList( ListHandle, DeviceCollection->ClassImageList.ImageList, LVSIL_SMALL );
haveImageList = TRUE; }
//
// Insert a column for the class list
//
lvcCol.mask = LVCF_FMT | LVCF_WIDTH; lvcCol.fmt = LVCFMT_LEFT; lvcCol.iSubItem = 0; ListView_InsertColumn(ListHandle, 0, (LV_COLUMN FAR *)&lvcCol);
//
// Walk the devinst list and add each of them to the listbox.
//
lvIndex = 0; for(listEntry = DeviceCollection->DeviceListHead.Flink; listEntry != &DeviceCollection->DeviceListHead; listEntry = listEntry->Flink) {
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link);
lviItem.mask = LVIF_TEXT; lviItem.iItem = lvIndex; lviItem.iSubItem = 0;
//
// In the worst possible scenario, we will give the user the instance
// path. This is by design because we put other things into the list
// sometimes.
//
lviItem.pszText = deviceEntry->DeviceInstanceId; if (deviceEntry->DeviceFriendlyName) {
lviItem.pszText = deviceEntry->DeviceFriendlyName; }
if (haveImageList) {
if (SetupDiGetClassImageIndex( &DeviceCollection->ClassImageList, &deviceEntry->ClassGuid, &lviItem.iImage) ) {
lviItem.mask |= LVIF_IMAGE; } }
lvIndex = ListView_InsertItem(ListHandle, &lviItem);
lvIndex++; } }
BOOL DeviceCollectionGetDockDeviceIndex( IN PDEVICE_COLLECTION DeviceCollection, OUT ULONG *DockDeviceIndex ) { PDEVICE_COLLECTION_ENTRY deviceEntry; PLIST_ENTRY listEntry; ULONG index;
index = 0; for(listEntry = DeviceCollection->DeviceListHead.Flink; listEntry != &DeviceCollection->DeviceListHead; listEntry = listEntry->Flink) {
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link);
if (!(deviceEntry->Capabilities & CM_DEVCAP_DOCKDEVICE)) {
index++; continue; }
*DockDeviceIndex = index; return TRUE; }
*DockDeviceIndex = 0; return FALSE; }
BOOL DeviceCollectionFormatDeviceText( IN PDEVICE_COLLECTION DeviceCollection, IN ULONG Index, IN PTSTR FormatString, IN ULONG BufferCharSize, OUT PTSTR BufferText ) { PDEVICE_COLLECTION_ENTRY deviceEntry; PTCHAR friendlyName; ULONG curIndex; PLIST_ENTRY listEntry;
curIndex = 0; for(listEntry = DeviceCollection->DeviceListHead.Flink; listEntry != &DeviceCollection->DeviceListHead; listEntry = listEntry->Flink) {
if (curIndex == Index) {
break; }
curIndex++; }
if (listEntry == &DeviceCollection->DeviceListHead) {
//
// We walked the entire list and didn't locate our device. Fail now.
//
if (BufferCharSize) {
*BufferText = TEXT('\0'); } return FALSE; }
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link);
//
// In the worst possible scenario, we will give the user the instance
// path. This is by design because we put other things into the list
// sometimes.
//
friendlyName = deviceEntry->DeviceInstanceId; if (deviceEntry->DeviceFriendlyName) {
friendlyName = deviceEntry->DeviceFriendlyName; }
StringCchPrintf(BufferText, BufferCharSize, FormatString, friendlyName); return TRUE; }
BOOL DeviceCollectionFormatServiceText( IN PDEVICE_COLLECTION DeviceCollection, IN ULONG Index, IN PTSTR FormatString, IN ULONG BufferCharSize, OUT PTSTR BufferText ) { PDEVICE_COLLECTION_ENTRY deviceEntry; PTCHAR serviceName; TCHAR szFriendlyName[MAX_SERVICE_NAME_LEN]; SC_HANDLE hSCManager; DWORD dwSize; ULONG curIndex; PLIST_ENTRY listEntry;
//
// Walk the list to the entry specified by the index.
//
curIndex = 0; for(listEntry = DeviceCollection->DeviceListHead.Flink; listEntry != &DeviceCollection->DeviceListHead; listEntry = listEntry->Flink) {
if (curIndex == Index) {
break; }
curIndex++; }
if (listEntry == &DeviceCollection->DeviceListHead) { //
// We walked the entire list and didn't locate our service. Fail now.
//
if (BufferCharSize) { *BufferText = TEXT('\0'); } return FALSE; }
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link);
//
// Our caller knows this collection entry is really a service (either a
// windows service, or a kernel driver), so the DeviceInstanceId is really
// the Service name. Query the SCM for its friendlier DisplayName property.
//
serviceName = deviceEntry->DeviceInstanceId;
*szFriendlyName = TEXT('\0');
if (serviceName) {
//
// Open the Service Control Manager
//
hSCManager = OpenSCManager( NULL, // local machine
SERVICES_ACTIVE_DATABASE, // SCM database name
GENERIC_READ // access type
);
if (hSCManager) {
//
// Query the SCM for this service's DisplayName. Note we use a
// constant buffer of MAX_SERVICE_NAME_LENGTH chars, which should
// always be large enough because that's what the SCM limits
// DisplayNames to. If GetServiceDisplayName fails, we will receive
// an empty string, which we'll handle below.
//
dwSize = MAX_SERVICE_NAME_LEN;
GetServiceDisplayName( hSCManager, // handle to SCM database
serviceName, // service name
szFriendlyName, // display name
&dwSize // size of display name buffer (in chars)
);
CloseServiceHandle(hSCManager); }
//
// We couldn't retrieve a friendly name for the service, so just use the
// name we were given.
//
if (!*szFriendlyName) { StringCchCopy(szFriendlyName, SIZECHARS(szFriendlyName), serviceName); } }
StringCchPrintf(BufferText, BufferCharSize, FormatString, szFriendlyName); return TRUE; }
PTSTR DeviceCollectionGetDeviceInstancePath( IN PDEVICE_COLLECTION DeviceCollection, IN ULONG Index ) { PDEVICE_COLLECTION_ENTRY deviceEntry; ULONG curIndex; PLIST_ENTRY listEntry;
curIndex = 0; for(listEntry = DeviceCollection->DeviceListHead.Flink; listEntry != &DeviceCollection->DeviceListHead; listEntry = listEntry->Flink) {
if (curIndex == Index) { break; }
curIndex++; }
if (listEntry == &DeviceCollection->DeviceListHead) { //
// We walked the entire list and didn't locate our device. Fail now.
//
return NULL; }
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link);
return deviceEntry->DeviceInstanceId; }
PTSTR DeviceCollectionGetDeviceFriendlyName( IN PDEVICE_COLLECTION DeviceCollection, IN ULONG Index ) { PDEVICE_COLLECTION_ENTRY deviceEntry; ULONG curIndex; PLIST_ENTRY listEntry;
curIndex = 0; for(listEntry = DeviceCollection->DeviceListHead.Flink; listEntry != &DeviceCollection->DeviceListHead; listEntry = listEntry->Flink) {
if (curIndex == Index) { break; }
curIndex++; }
if (listEntry == &DeviceCollection->DeviceListHead) { //
// We walked the entire list and didn't locate our device. Fail now.
//
return NULL; }
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link);
return deviceEntry->DeviceFriendlyName; }
BOOL DeviceCollectionGetGuid( IN PDEVICE_COLLECTION DeviceCollection, IN OUT LPGUID Guid, IN ULONG Index ) { PDEVICE_COLLECTION_ENTRY deviceEntry; ULONG curIndex; PLIST_ENTRY listEntry;
curIndex = 0; for(listEntry = DeviceCollection->DeviceListHead.Flink; listEntry != &DeviceCollection->DeviceListHead; listEntry = listEntry->Flink) {
if (curIndex == Index) { break; }
curIndex++; }
if (listEntry == &DeviceCollection->DeviceListHead) { //
// We walked the entire list and didn't locate our device. Fail now.
//
return FALSE; }
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link);
memcpy(Guid, &(deviceEntry->ClassGuid), sizeof(GUID)); return TRUE; }
VOID DeviceCollectionSwapFakeDockForRealDock( IN PDEVICE_COLLECTION DeviceCollection, IN PTSTR InstancePath, IN OUT DEVNODE *DeviceNode ) { DEVNODE fakeDock, realDock; ULONG cchSize; CONFIGRET configRet; PTSTR deviceIdRelations, realDockId, nextEjectionId, hardwareIds, curEntry;
//
// Preinit
//
fakeDock = *DeviceNode; deviceIdRelations = NULL; hardwareIds = NULL;
cchSize = 0; configRet = CM_Get_Device_ID_List_Size_Ex( &cchSize, InstancePath, CM_GETIDLIST_FILTER_EJECTRELATIONS, NULL );
if ((configRet != CR_SUCCESS) || (!cchSize)) {
goto Exit; }
deviceIdRelations = (PTSTR)LocalAlloc(LPTR, cchSize*sizeof(TCHAR));
if (!deviceIdRelations) {
goto Exit; }
*deviceIdRelations = TEXT('\0');
configRet = CM_Get_Device_ID_List_Ex( InstancePath, deviceIdRelations, cchSize, CM_GETIDLIST_FILTER_EJECTRELATIONS, NULL );
if (configRet != CR_SUCCESS) {
goto Exit; }
if (!(*deviceIdRelations)) {
//
// No ejection relations, bail.
//
goto Exit; }
//
// The last relation should be the real dock. Get it.
//
nextEjectionId = deviceIdRelations;
do {
realDockId = nextEjectionId; nextEjectionId += lstrlen(nextEjectionId)+1;
} while ( *nextEjectionId );
configRet = CM_Locate_DevNode( &realDock, realDockId, CM_LOCATE_DEVNODE_PHANTOM );
if (configRet != CR_SUCCESS) {
goto Exit; }
LocalFree(deviceIdRelations); deviceIdRelations = NULL;
//
// One last check - we need to check the hardware ID's and compatible ID's.
// We will only do this if we spot a *PNP0C15 amongst them.
//
cchSize = 0; configRet = CM_Get_DevNode_Registry_Property_Ex( realDock, CM_DRP_HARDWAREID, NULL, NULL, &cchSize, 0, NULL );
if (configRet != CR_SUCCESS) {
goto Exit; }
hardwareIds = (PTSTR)LocalAlloc(LPTR, cchSize*sizeof(TCHAR));
if (!hardwareIds) {
goto Exit; }
configRet = CM_Get_DevNode_Registry_Property_Ex( realDock, CM_DRP_HARDWAREID, NULL, hardwareIds, &cchSize, 0, NULL );
if (configRet == CR_SUCCESS) {
for(curEntry = hardwareIds; *curEntry; curEntry += lstrlen(curEntry)+1) {
if (!_wcsicmp(curEntry, TEXT("*PNP0C15"))) {
//
// We found an entry - we can successful "rebrand" this dock
// for the user.
//
*DeviceNode = realDock; LocalFree(hardwareIds); return; } } }
LocalFree(hardwareIds); hardwareIds = NULL;
//
// Now try the compatible ID's. This is where we really expect to find the
// real dock.
//
cchSize = 0; configRet = CM_Get_DevNode_Registry_Property_Ex( realDock, CM_DRP_COMPATIBLEIDS, NULL, NULL, &cchSize, 0, NULL );
if (configRet != CR_SUCCESS) {
goto Exit; }
hardwareIds = (PTSTR)LocalAlloc(LPTR, cchSize*sizeof(TCHAR));
if (!hardwareIds) {
goto Exit; }
configRet = CM_Get_DevNode_Registry_Property_Ex( realDock, CM_DRP_COMPATIBLEIDS, NULL, hardwareIds, &cchSize, 0, NULL );
if (configRet == CR_SUCCESS) {
for(curEntry = hardwareIds; *curEntry; curEntry += lstrlen(curEntry)+1) {
if (!_wcsicmp(curEntry, TEXT("*PNP0C15"))) {
//
// We found an entry - we can successful "rebrand" this dock
// for the user.
//
*DeviceNode = realDock; LocalFree(hardwareIds); return; } } }
Exit: if (deviceIdRelations) {
LocalFree(deviceIdRelations); }
if (hardwareIds) {
LocalFree(hardwareIds); } }
BOOL DeviceCollectionCheckIfAllRemoved( IN PDEVICE_COLLECTION DeviceCollection ) { PDEVICE_COLLECTION_ENTRY deviceEntry; PLIST_ENTRY listEntry; DEVNODE deviceNode;
for(listEntry = DeviceCollection->DeviceListHead.Flink; listEntry != &DeviceCollection->DeviceListHead; listEntry = listEntry->Flink) {
deviceEntry = CONTAINING_RECORD(listEntry, DEVICE_COLLECTION_ENTRY, Link); //
// If we can locate this device normally then it is a 'live'
// device, so return FALSE.
//
if (CM_Locate_DevNode(&deviceNode, deviceEntry->DeviceInstanceId, 0) == CR_SUCCESS) { return FALSE; } }
//
// We were able to locate all the devices in this device collection.
//
return TRUE; }
|