Devcon ListClass命令代码实现
2022-01-28
44
0
ListClass命令用于列出当前系统中该设备类下的的有设备实例并显示设备的友名。
ListClass命令由两部分组成:
- 第一部分是根据设备的类名获取其类GUID,然后再通过SetupDiGetClassDevsEx打开该GUID分类的设备集,获取其 HDEVINFO devs句柄。当然这里也为了显法该设备类更详尽的信息,通过SetupDiGetClassDescriptionEx获取设备类的描述符信息。
由于这个命令支持多类名传递,故代码中搞了一个循环。
int cmdListClass(_In_ LPCTSTR BaseName, _In_opt_ LPCTSTR Machine, _In_ DWORD Flags, _In_ int argc, _In_reads_(argc) PTSTR argv[])
/*++
Routine Description:
LISTCLASS <name>....
lists all devices for each specified class
there can be more than one physical class for a class name (shouldn't be
though) in such cases, list each class
if machine given, list devices for that machine
Arguments:
BaseName - name of executable
Machine - if non-NULL, remote machine
argc/argv - remaining parameters - list of class names
Return Value:
EXIT_xxxx
--*/
{
DWORD reqGuids = 16;
int argIndex;
int failcode = EXIT_FAIL;
LPGUID guids = NULL;
HDEVINFO devs = INVALID_HANDLE_VALUE;
UNREFERENCED_PARAMETER(BaseName);
UNREFERENCED_PARAMETER(Flags);
if(!argc) {
return EXIT_USAGE;
}
guids = new GUID[reqGuids];
if(!guids) {
goto final;
}
for(argIndex = 0;argIndex<argc;argIndex++) {
DWORD numGuids;
DWORD index;
if(!(argv[argIndex] && argv[argIndex][0])) {
continue;
}
//
// there could be one to many name to GUID mapping
//
while(!SetupDiClassGuidsFromNameEx(argv[argIndex],guids,reqGuids,&numGuids,Machine,NULL)) {
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
goto final;
}
delete [] guids;
reqGuids = numGuids;
guids = new GUID[reqGuids];
if(!guids) {
goto final;
}
}
if(numGuids == 0) {
FormatToStream(stdout,Machine?MSG_LISTCLASS_NOCLASS:MSG_LISTCLASS_NOCLASS_LOCAL,argv[argIndex],Machine);
continue;
}
for(index = 0;index<numGuids;index++) {
TCHAR className[MAX_CLASS_NAME_LEN];
TCHAR classDesc[LINE_LEN];
DWORD devCount = 0;
SP_DEVINFO_DATA devInfo;
DWORD devIndex;
devs = SetupDiGetClassDevsEx(&guids[index],NULL,NULL,/*DIGCF_PRESENT*/0,NULL,Machine,NULL);
if(devs != INVALID_HANDLE_VALUE) {
//
// count number of devices
//
devInfo.cbSize = sizeof(devInfo);
while(SetupDiEnumDeviceInfo(devs,devCount,&devInfo)) {
devCount++;
}
}
if(!SetupDiClassNameFromGuidEx(&guids[index],className,MAX_CLASS_NAME_LEN,NULL,Machine,NULL)) {
if (FAILED(StringCchCopy(className,MAX_CLASS_NAME_LEN,TEXT("?")))) {
goto final;
}
}
if(!SetupDiGetClassDescriptionEx(&guids[index],classDesc,LINE_LEN,NULL,Machine,NULL)) {
if (FAILED(StringCchCopy(classDesc,LINE_LEN,className))) {
goto final;
}
}
//
// how many devices?
//
if (!devCount) {
FormatToStream(stdout,Machine?MSG_LISTCLASS_HEADER_NONE:MSG_LISTCLASS_HEADER_NONE_LOCAL,className,classDesc,Machine);
} else {
FormatToStream(stdout,Machine?MSG_LISTCLASS_HEADER:MSG_LISTCLASS_HEADER_LOCAL,devCount,className,classDesc,Machine);
for(devIndex=0;SetupDiEnumDeviceInfo(devs,devIndex,&devInfo);devIndex++) {
DumpDevice(devs,&devInfo);
}
}
if(devs != INVALID_HANDLE_VALUE) {
SetupDiDestroyDeviceInfoList(devs);
devs = INVALID_HANDLE_VALUE;
}
}
}
failcode = 0;
final:
if(guids) {
delete [] guids;
}
if(devs != INVALID_HANDLE_VALUE) {
SetupDiDestroyDeviceInfoList(devs);
}
return failcode;
}
第二部根据打开的设备集HDEVINFO devs句柄,枚举该设备类下的所有设备
for(devIndex=0;SetupDiEnumDeviceInfo(devs,devIndex,&devInfo);devIndex++) {
DumpDevice(devs,&devInfo);
}
然后通过DumpDevice函数打开设备的实例名和描述符信息。
BOOL DumpDevice(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo)
/*++
Routine Description:
Write device instance & description to stdout
Arguments:
Devs )_ uniquely identify device
DevInfo )
Return Value:
TRUE if success
--*/
{
LPTSTR desc;
BOOL b;
desc = GetDeviceDescription(Devs,DevInfo);
b = DumpDeviceWithInfo(Devs,DevInfo,desc);
if(desc) {
delete [] desc;
}
return b;
}
可以看到,设备的描述信息是通过自定义GetDeviceDescription获取的,其就是通过GetDeviceStringProperty获取设备的SPDRP_FRIENDLYNAME或SPDRP_DEVICEDESC信息。
LPTSTR GetDeviceDescription(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo)
/*++
Routine Description:
Return a string containing a description of the device, otherwise NULL
Always try friendly name first
Arguments:
Devs )_ uniquely identify device
DevInfo )
Return Value:
string containing description
--*/
{
LPTSTR desc;
desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_FRIENDLYNAME);
if(!desc) {
desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_DEVICEDESC);
}
return desc;
}
而设备的实例名信息是通过自定义函数DumpDeviceWithInfo实现的。
BOOL DumpDeviceWithInfo(_In_ HDEVINFO Devs, _In_ PSP_DEVINFO_DATA DevInfo, _In_opt_ LPCTSTR Info)
/*++
Routine Description:
Write device instance & info to stdout
Arguments:
Devs )_ uniquely identify device
DevInfo )
Return Value:
none
--*/
{
TCHAR devID[MAX_DEVICE_ID_LEN];
BOOL b = TRUE;
SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
devInfoListDetail.cbSize = sizeof(devInfoListDetail);
if((!SetupDiGetDeviceInfoListDetail(Devs,&devInfoListDetail)) ||
(CM_Get_Device_ID_Ex(DevInfo->DevInst,devID,MAX_DEVICE_ID_LEN,0,devInfoListDetail.RemoteMachineHandle)!=CR_SUCCESS)) {
StringCchCopy(devID, ARRAYSIZE(devID), TEXT("?"));
b = FALSE;
}
if(Info) {
_tprintf(TEXT("%-60s: %s\n"),devID,Info);
} else {
_tprintf(TEXT("%s\n"),devID);
}
return b;
}
可以看到,设备的实例名是首先通过SetupDiGetDeviceInfoListDetail获取设备的HMACHINE句柄,再通过CM_Get_Device_ID_Ex获取其实例名路径。
CM_Get_Device_ID_ExW(
_In_ DEVINST dnDevInst,
_Out_writes_(BufferLen) PWSTR Buffer,
_In_ ULONG BufferLen,
_In_ ULONG ulFlags,
_In_opt_ HMACHINE hMachine
);