[SETUPAPI] Use CM_* functions to get list of devices

- this prevents duplicating the code with umpnpmgr::PNP_GetDeviceList
- we can enumerate devices on other computers

This is part of the bugfix for CORE-17529
This commit is contained in:
Hervé Poussineau 2021-04-02 20:00:31 +02:00
parent ade5413362
commit 0f3133c308
3 changed files with 97 additions and 210 deletions

View file

@ -117,139 +117,6 @@ SetupDiDestroyClassImageList(
return ret; return ret;
} }
/***********************************************************************
* SETUP_CreateDevicesListFromEnumerator
*
* PARAMS
* list [IO] Device info set to fill with discovered devices.
* pClassGuid [I] If specified, only devices which belong to this class will be added.
* Enumerator [I] Location to search devices to add.
* hEnumeratorKey [I] Registry key corresponding to Enumerator key. Must have KEY_ENUMERATE_SUB_KEYS right.
*
* RETURNS
* Success: ERROR_SUCCESS.
* Failure: an error code.
*/
static LONG
SETUP_CreateDevicesListFromEnumerator(
IN OUT struct DeviceInfoSet *list,
IN CONST GUID *pClassGuid OPTIONAL,
IN LPCWSTR Enumerator,
IN HKEY hEnumeratorKey) /* handle to Enumerator registry key */
{
HKEY hDeviceIdKey = NULL, hInstanceIdKey;
WCHAR KeyBuffer[MAX_PATH];
WCHAR InstancePath[MAX_PATH];
LPWSTR pEndOfInstancePath; /* Pointer into InstancePath buffer */
struct DeviceInfo *deviceInfo;
DWORD i = 0, j;
DWORD dwLength, dwRegType;
DWORD rc;
/* Enumerate device IDs (subkeys of hEnumeratorKey) */
while (TRUE)
{
dwLength = sizeof(KeyBuffer) / sizeof(KeyBuffer[0]);
rc = RegEnumKeyExW(hEnumeratorKey, i, KeyBuffer, &dwLength, NULL, NULL, NULL, NULL);
if (rc == ERROR_NO_MORE_ITEMS)
break;
if (rc != ERROR_SUCCESS)
goto cleanup;
i++;
/* Open device id sub key */
if (hDeviceIdKey != NULL)
RegCloseKey(hDeviceIdKey);
rc = RegOpenKeyExW(hEnumeratorKey, KeyBuffer, 0, KEY_ENUMERATE_SUB_KEYS, &hDeviceIdKey);
if (rc != ERROR_SUCCESS)
goto cleanup;
if (FAILED(StringCchCopyW(InstancePath, _countof(InstancePath), Enumerator)) ||
FAILED(StringCchCatW(InstancePath, _countof(InstancePath), BackSlash)) ||
FAILED(StringCchCatW(InstancePath, _countof(InstancePath), KeyBuffer)) ||
FAILED(StringCchCatW(InstancePath, _countof(InstancePath), BackSlash)))
{
rc = ERROR_GEN_FAILURE;
goto cleanup;
}
pEndOfInstancePath = &InstancePath[strlenW(InstancePath)];
/* Enumerate instance IDs (subkeys of hDeviceIdKey) */
j = 0;
while (TRUE)
{
GUID KeyGuid;
dwLength = sizeof(KeyBuffer) / sizeof(KeyBuffer[0]);
rc = RegEnumKeyExW(hDeviceIdKey, j, KeyBuffer, &dwLength, NULL, NULL, NULL, NULL);
if (rc == ERROR_NO_MORE_ITEMS)
break;
if (rc != ERROR_SUCCESS)
goto cleanup;
j++;
/* Open instance id sub key */
rc = RegOpenKeyExW(hDeviceIdKey, KeyBuffer, 0, KEY_QUERY_VALUE, &hInstanceIdKey);
if (rc != ERROR_SUCCESS)
goto cleanup;
*pEndOfInstancePath = '\0';
strcatW(InstancePath, KeyBuffer);
/* Read ClassGUID value */
dwLength = sizeof(KeyBuffer) - sizeof(WCHAR);
rc = RegQueryValueExW(hInstanceIdKey, ClassGUID, NULL, &dwRegType, (LPBYTE)KeyBuffer, &dwLength);
RegCloseKey(hInstanceIdKey);
if (rc == ERROR_FILE_NOT_FOUND)
{
if (pClassGuid)
/* Skip this bad entry as we can't verify it */
continue;
/* Set a default GUID for this device */
memcpy(&KeyGuid, &GUID_NULL, sizeof(GUID));
}
else if (rc != ERROR_SUCCESS)
{
goto cleanup;
}
else if (dwRegType != REG_SZ || dwLength < MAX_GUID_STRING_LEN * sizeof(WCHAR))
{
rc = ERROR_GEN_FAILURE;
goto cleanup;
}
else
{
KeyBuffer[MAX_GUID_STRING_LEN - 2] = '\0'; /* Replace the } by a NULL character */
if (UuidFromStringW(&KeyBuffer[1], &KeyGuid) != RPC_S_OK)
/* Bad GUID, skip the entry */
continue;
}
if (pClassGuid && !IsEqualIID(&KeyGuid, pClassGuid))
{
/* Skip this entry as it is not the right device class */
continue;
}
/* Add the entry to the list */
if (!CreateDeviceInfo(list, InstancePath, &KeyGuid, &deviceInfo))
{
rc = GetLastError();
goto cleanup;
}
TRACE("Adding '%s' to device info set %p\n", debugstr_w(InstancePath), list);
InsertTailList(&list->ListHead, &deviceInfo->ListEntry);
}
}
rc = ERROR_SUCCESS;
cleanup:
if (hDeviceIdKey != NULL)
RegCloseKey(hDeviceIdKey);
return rc;
}
LONG LONG
SETUP_CreateDevicesList( SETUP_CreateDevicesList(
IN OUT struct DeviceInfoSet *list, IN OUT struct DeviceInfoSet *list,
@ -257,91 +124,108 @@ SETUP_CreateDevicesList(
IN CONST GUID *Class OPTIONAL, IN CONST GUID *Class OPTIONAL,
IN PCWSTR Enumerator OPTIONAL) IN PCWSTR Enumerator OPTIONAL)
{ {
HKEY HKLM = HKEY_LOCAL_MACHINE; PWCHAR Buffer = NULL;
HKEY hEnumKey = NULL; DWORD BufferLength = 4096;
HKEY hEnumeratorKey = NULL; PCWSTR InstancePath;
WCHAR KeyBuffer[MAX_PATH]; struct DeviceInfo *deviceInfo;
DWORD i; WCHAR ClassGuidBuffer[MAX_GUID_STRING_LEN];
DWORD dwLength; DWORD ClassGuidBufferSize;
DWORD rc; GUID ClassGuid;
DEVINST dnDevInst;
CONFIGRET cr;
if (Class && IsEqualIID(Class, &GUID_NULL)) Buffer = HeapAlloc(GetProcessHeap(), 0, BufferLength);
Class = NULL; if (!Buffer)
return ERROR_NOT_ENOUGH_MEMORY;
/* Open Enum key (if applicable) */ do
if (MachineName != NULL)
{ {
rc = RegConnectRegistryW(MachineName, HKEY_LOCAL_MACHINE, &HKLM); cr = CM_Get_Device_ID_List_ExW(Enumerator,
if (rc != ERROR_SUCCESS) Buffer,
goto cleanup; BufferLength,
} Enumerator ? CM_GETIDLIST_FILTER_ENUMERATOR : CM_GETIDLIST_FILTER_NONE,
list->hMachine);
rc = RegOpenKeyExW( if (cr == CR_BUFFER_SMALL)
HKLM,
REGSTR_PATH_SYSTEMENUM,
0,
KEY_ENUMERATE_SUB_KEYS,
&hEnumKey);
if (rc != ERROR_SUCCESS)
goto cleanup;
/* If enumerator is provided, call directly SETUP_CreateDevicesListFromEnumerator.
* Else, enumerate all enumerators and call SETUP_CreateDevicesListFromEnumerator
* for each one.
*/
if (Enumerator)
{
rc = RegOpenKeyExW(
hEnumKey,
Enumerator,
0,
KEY_ENUMERATE_SUB_KEYS,
&hEnumeratorKey);
if (rc != ERROR_SUCCESS)
{ {
if (rc == ERROR_FILE_NOT_FOUND) if (Buffer)
rc = ERROR_INVALID_DATA; HeapFree(GetProcessHeap(), 0, Buffer);
goto cleanup; BufferLength *= 2;
Buffer = HeapAlloc(GetProcessHeap(), 0, BufferLength);
if (!Buffer)
return ERROR_NOT_ENOUGH_MEMORY;
} }
rc = SETUP_CreateDevicesListFromEnumerator(list, Class, Enumerator, hEnumeratorKey); else if (cr != CR_SUCCESS)
}
else
{
/* Enumerate enumerators */
i = 0;
while (TRUE)
{ {
dwLength = sizeof(KeyBuffer) / sizeof(KeyBuffer[0]); TRACE("CM_Get_Device_ID_List_ExW() failed with status 0x%x\n", cr);
rc = RegEnumKeyExW(hEnumKey, i, KeyBuffer, &dwLength, NULL, NULL, NULL, NULL); if (Buffer)
if (rc == ERROR_NO_MORE_ITEMS) HeapFree(GetProcessHeap(), 0, Buffer);
break; return GetErrorCodeFromCrCode(cr);
else if (rc != ERROR_SUCCESS)
goto cleanup;
i++;
/* Open sub key */
if (hEnumeratorKey != NULL)
RegCloseKey(hEnumeratorKey);
rc = RegOpenKeyExW(hEnumKey, KeyBuffer, 0, KEY_ENUMERATE_SUB_KEYS, &hEnumeratorKey);
if (rc != ERROR_SUCCESS)
goto cleanup;
/* Call SETUP_CreateDevicesListFromEnumerator */
rc = SETUP_CreateDevicesListFromEnumerator(list, Class, KeyBuffer, hEnumeratorKey);
if (rc != ERROR_SUCCESS)
goto cleanup;
} }
rc = ERROR_SUCCESS; }
while (cr != CR_SUCCESS);
for (InstancePath = Buffer; *InstancePath != UNICODE_NULL; InstancePath += wcslen(InstancePath) + 1)
{
/* Check that device really exists */
TRACE("Checking %S\n", InstancePath);
cr = CM_Locate_DevNode_Ex(&dnDevInst,
(DEVINSTID_W)InstancePath,
CM_LOCATE_DEVNODE_NORMAL,
list->hMachine);
if (cr != CR_SUCCESS)
{
ERR("CM_Locate_DevNode_ExW('%S') failed with status 0x%x\n", InstancePath, cr);
continue;
}
/* Retrieve GUID of this device */
if (Class)
{
ClassGuidBufferSize = sizeof(ClassGuidBuffer);
cr = CM_Get_DevNode_Registry_Property_ExW(dnDevInst,
CM_DRP_CLASSGUID,
NULL,
ClassGuidBuffer,
&ClassGuidBufferSize,
0,
list->hMachine);
if (cr == CR_SUCCESS)
{
ClassGuidBuffer[MAX_GUID_STRING_LEN - 2] = '\0'; /* Replace the } by a NULL character */
if (UuidFromStringW(&ClassGuidBuffer[1], &ClassGuid) != RPC_S_OK)
{
/* Bad GUID, skip the entry */
ERR("Invalid ClassGUID '%S' for device %S\n", ClassGuidBuffer, InstancePath);
continue;
}
}
else
{
TRACE("Using default class GUID_NULL for device %S\n", InstancePath);
memcpy(&ClassGuid, &GUID_NULL, sizeof(GUID));
}
if (!IsEqualIID(&ClassGuid, Class))
{
TRACE("Skipping %S due to wrong class GUID\n", InstancePath);
continue;
}
}
/* Good! Create a device info element */
if (!CreateDeviceInfo(list, InstancePath, &ClassGuid, &deviceInfo))
{
ERR("Failed to create info for %S\n", InstancePath);
HeapFree(GetProcessHeap(), 0, Buffer);
return GetLastError();
}
TRACE("Adding device %s to list\n", debugstr_w(InstancePath));
InsertTailList(&list->ListHead, &deviceInfo->ListEntry);
} }
cleanup: HeapFree(GetProcessHeap(), 0, Buffer);
if (HKLM != HKEY_LOCAL_MACHINE) return ERROR_SUCCESS;
RegCloseKey(HKLM);
if (hEnumKey != NULL)
RegCloseKey(hEnumKey);
if (hEnumeratorKey != NULL)
RegCloseKey(hEnumeratorKey);
return rc;
} }
static BOOL static BOOL

View file

@ -88,7 +88,7 @@ static void SETUPDI_GuidToString(const GUID *guid, LPWSTR guidStr)
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
} }
static DWORD DWORD
GetErrorCodeFromCrCode(const IN CONFIGRET cr) GetErrorCodeFromCrCode(const IN CONFIGRET cr)
{ {
switch (cr) switch (cr)

View file

@ -298,6 +298,9 @@ UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification, UINT_PTR, U
/* devinst.c */ /* devinst.c */
DWORD
GetErrorCodeFromCrCode(const IN CONFIGRET cr);
BOOL BOOL
CreateDeviceInfo( CreateDeviceInfo(
IN struct DeviceInfoSet *list, IN struct DeviceInfoSet *list,