You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:47:35