将HMIDIIN转HANDLE调用NtQueryObject遇无效句柄错误求助
问题分析与解决方案
一、为什么HMIDIIN转HANDLE调用NtQueryObject失败?
- 句柄类型不匹配:虽然文档提到新应用可将
HMIDIIN转为标准HANDLE,但这里的“标准句柄”仅适用于部分Win32 API,并非NtQueryObject所需的内核对象句柄。HMIDIIN是多媒体子系统在用户态封装的句柄,底层未直接映射到内核对象,强制转换后无法被原生NT API识别,返回0xc0000008(无效句柄)是必然结果。 - 参数错误:代码中
NtQueryObject的最后一个参数rlen传的是空指针(0),该参数需要传入ULONG变量的地址,用于接收实际所需的缓冲区长度。即使句柄有效,这个参数错误也会导致调用失败。
二、替代方案:通过设备ID+CfgMgr32获取设备树信息
步骤1:用midiInGetDevCaps获取设备名称
通过midiInGetDevCaps获取指定设备ID的友好名称,这是后续匹配设备的关键:
MIDIINCAPS midiCaps; MMRESULT result = midiInGetDevCaps(i, &midiCaps, sizeof(MIDIINCAPS)); if (result != MMSYSERR_NOERROR) { // 处理错误逻辑 } // midiCaps.szPname 即为设备的友好名称
步骤2:通过CfgMgr32枚举并匹配MIDI设备
MIDI输入设备属于固定的设备接口类GUID_DEVINTERFACE_MIDIIN,可通过CfgMgr32函数枚举该类设备接口,匹配名称后获取设备树信息:
#include <Cfgmgr32.h> #include <initguid.h> DEFINE_GUID(GUID_DEVINTERFACE_MIDIIN, 0x25E3994C, 0xD408, 0x4F0F, 0x9F, 0x1A, 0x62, 0xDF, 0x6D, 0xA7, 0x1C, 0x62); void QueryMidiDeviceTreeInfo(const wchar_t* targetDeviceName) { PWSTR deviceInterfaceList = nullptr; ULONG listSize = 0; CONFIGRET cr; // 获取设备接口列表所需内存大小 cr = CM_Get_Device_Interface_List_Size(&listSize, &GUID_DEVINTERFACE_MIDIIN, nullptr, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); if (cr != CR_SUCCESS) return; // 分配内存存储接口列表 deviceInterfaceList = (PWSTR)LocalAlloc(LPTR, listSize * sizeof(WCHAR)); if (!deviceInterfaceList) return; // 获取设备接口列表 cr = CM_Get_Device_Interface_List(&GUID_DEVINTERFACE_MIDIIN, nullptr, deviceInterfaceList, listSize, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); if (cr != CR_SUCCESS) { LocalFree(deviceInterfaceList); return; } // 遍历每个设备接口 PWSTR currentInterface = deviceInterfaceList; while (*currentInterface != L'\0') { WCHAR friendlyName[256]; ULONG nameSize = sizeof(friendlyName); // 获取当前接口的设备友好名称 cr = CM_Get_Device_Interface_Property(currentInterface, &DEVPKEY_Device_FriendlyName, nullptr, (PBYTE)friendlyName, &nameSize, 0); if (cr != CR_SUCCESS) { currentInterface += wcslen(currentInterface) + 1; continue; } // 匹配目标设备名称 if (wcscmp(friendlyName, targetDeviceName) == 0) { DEVINST devInst; // 获取设备实例 cr = CM_Locate_DevNode(&devInst, nullptr, CM_LOCATE_DEVNODE_NORMAL); if (cr == CR_SUCCESS) { // 查询父设备实例(设备树回溯示例) DEVINST parentDevInst; cr = CM_Get_Parent(&parentDevInst, devInst, 0); if (cr == CR_SUCCESS) { // 处理父设备信息 } // 获取设备硬件ID(进一步查询示例) WCHAR hardwareId[256]; ULONG hwIdSize = sizeof(hardwareId); cr = CM_Get_Device_ID(devInst, hardwareId, hwIdSize, 0); if (cr == CR_SUCCESS) { // hardwareId 可用于后续设备信息查询 } } break; } currentInterface += wcslen(currentInterface) + 1; } LocalFree(deviceInterfaceList); }
步骤3:整合到原有代码中
修改原循环逻辑,获取每个设备名称后调用查询函数:
for (int i = 0; i < uDeviceID; i++) { MIDIINCAPS midiCaps; MMRESULT capsResult = midiInGetDevCaps(i, &midiCaps, sizeof(MIDIINCAPS)); if (capsResult != MMSYSERR_NOERROR) { std::cout << "\nFailed to get caps for device " << i; continue; } std::wcout << L"\nDevice " << i << L": " << midiCaps.szPname; QueryMidiDeviceTreeInfo(midiCaps.szPname); }
三、额外注意事项
- 项目需链接
winmm.lib和Cfgmgr32.lib库。 CM_Get_Device_Interface_Property仅支持Windows Vista及以上系统。- 如需遍历完整设备树,可使用
CM_Get_Child、CM_Get_Sibling等函数递归查询。
内容的提问来源于stack exchange,提问作者Kavukamari
相关产品推荐
相关产品推荐

