实现一个小型的C DLL来连接我们的C++代码和RawInput

Implement a small C DLL to interface between our C++ code and RawInput

本文关键字:连接 我们 C++ RawInput 代码 DLL 小型 一个 实现      更新时间:2023-10-16

阅读这篇Stack Overflow文章后,SlimDX:摇杆.Poll()在断开的游戏板上成功

我们决定完全放弃Windows 7 DirectInput,转而使用Windows 7 RawInput。我们需要实现一个小的C DLL来连接我的C++代码和RawInput。

My sample C++ source code is,
// Adapters.cpp : Defines the exported functions for the DLL application.
//
    #include "stdafx.h"
    #include "Adapters.h"
    extern "C" {
    #include "hidsdi.h"
    }
    #define MAX_GAMEPADS 32
    static TCHAR *deviceNames[MAX_GAMEPADS];
    static HANDLE deviceHandles[MAX_GAMEPADS];
    static UINT devicesActive;

    #define CHECK(exp)      { if(!(exp)) goto Error; }
    #define SAFE_FREE(p)    { if(p) { HeapFree(hHeap, 0, p); (p) = NULL; } }
    static BOOL ReadDeviceName(int index, HANDLE handle)
    {
        UINT size = 0;
        UINT result = GetRawInputDeviceInfo(handle,
            RIDI_DEVICENAME, NULL, &size);
        if (result != 0) {
            result = GetLastError();
            return false;
        }
        deviceNames[index] = (TCHAR *)HeapAlloc(GetProcessHeap(), 0, size * sizeof(TCHAR));
        result = GetRawInputDeviceInfo(handle,
            RIDI_DEVICENAME, deviceNames[index], &size);
        if (result < 0) {
            result = GetLastError();
            HeapFree(GetProcessHeap(), 0, deviceNames[index]);
            deviceHandles[index] = NULL;
            deviceNames[index] = NULL;
            return false;
        }
        deviceHandles[index] = handle;
        return TRUE;
    }

    static BOOL ReadDeviceInfo(int index, HANDLE handle)
    {
        // May be a gamepad
        RID_DEVICE_INFO *info;
        UINT size = 0; // sizeof(RID_DEVICE_INFO);
        UINT result = GetRawInputDeviceInfo(handle, RIDI_DEVICEINFO,
            NULL, &size);
        info = (RID_DEVICE_INFO *) HeapAlloc(GetProcessHeap(), 0, size);
        result = GetRawInputDeviceInfo(handle, RIDI_DEVICEINFO,
            info, &size);
        if (result != (UINT)-1) {
            if (info->hid.usUsagePage == 1 && 
            (info->hid.usUsage == 4 || info->hid.usUsage == 5))  {
                // It is a gamepad!!!
                HeapFree(GetProcessHeap(), 0, info);
                return ReadDeviceName(index, handle);;
            }
        } else {
            result = (UINT) GetLastError();
        }
        HeapFree(GetProcessHeap(), 0, info);
        return FALSE;
    }
    static int FindIndex(HANDLE handle)
    {
        for(int i = 0; i < MAX_GAMEPADS; i++)
            if (deviceHandles[i] == handle)
                return i;
        return -1;
    }
    ADAPTERS_API int InitialiseGamepads(HWND handle)
    {
        RAWINPUTDEVICE devices[2];
        devices[0].usUsagePage = 1;
        devices[0].usUsage = 4; // Joystick
        devices[0].dwFlags = 0 ;
        devices[0].hwndTarget = handle;
        devices[1].usUsagePage = 1;
        devices[1].usUsage = 5;
        devices[1].dwFlags = 0 ;
        devices[1].hwndTarget = handle;
        bool result = RegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE));
        if (! result) {
            DWORD error = GetLastError();
            return 0;
        }
        UINT numdevices;
        GetRawInputDeviceList(NULL, &numdevices, sizeof(RAWINPUTDEVICELIST));
        RAWINPUTDEVICELIST *list = (RAWINPUTDEVICELIST *) HeapAlloc(GetProcessHeap(), 0, 
            numdevices * sizeof(RAWINPUTDEVICELIST));
        GetRawInputDeviceList(list, &numdevices, sizeof(RAWINPUTDEVICELIST));
        for(UINT i = 0; i < numdevices; i++) {
            if (list[i].dwType == RIM_TYPEHID) {
                if (ReadDeviceInfo(devicesActive, list[i].hDevice))
                    devicesActive++;
            }
        }
        return devicesActive;
    }
    ADAPTERS_API TCHAR *GetDevicePath(int index)
    {
        if (index < 0 || index >= MAX_GAMEPADS)
            return NULL;
        return deviceNames[index];
    }

    ADAPTERS_API int PollDeviceChange()
    {
        // Read device list
        UINT numdevices;
        GetRawInputDeviceList(NULL, &numdevices, sizeof(RAWINPUTDEVICELIST));
        RAWINPUTDEVICELIST *list = (RAWINPUTDEVICELIST *) HeapAlloc(GetProcessHeap(), 0, 
            numdevices * sizeof(RAWINPUTDEVICELIST));
        GetRawInputDeviceList(list, &numdevices, sizeof(RAWINPUTDEVICELIST));
        BOOL found[MAX_GAMEPADS];
        for(int i = 0; i < MAX_GAMEPADS; i++)
            found[i] = FALSE;
        for(UINT i = 0; i < numdevices; i++) {
            if (list[i].dwType == RIM_TYPEHID) {
                int index = FindIndex(list[i].hDevice);
                if (index >= 0)
                    found[index] = true;
                else {
                    // Find empty slot. Only process one change at a time, to simplify
                    // interface to managed code
                    for(int j = 0; j < MAX_GAMEPADS; j++)
                        if (! deviceNames[j]) {
                            if (ReadDeviceInfo(j, list[i].hDevice))
                                return j;
                        }
                }
            }
        }
        for(int i = 0; i < MAX_GAMEPADS; i++) {
            if (deviceNames[i] && ! found[i]) {
                // Missing device
                deviceHandles[i] = NULL;
                HeapFree(GetProcessHeap(), 0, deviceNames[i]);
                deviceNames[i] = NULL;
                return i;
            }
        }
        return -1;
    }
    static void ParseRawInput(PRAWINPUT pRawInput,
        unsigned char *buttons, int *x, int *y)
    {
        PHIDP_PREPARSED_DATA pPreparsedData;
        HIDP_CAPS            Caps;
        PHIDP_BUTTON_CAPS    pButtonCaps;
        PHIDP_VALUE_CAPS     pValueCaps;
        USHORT               capsLength;
        UINT                 bufferSize;
        HANDLE               hHeap;
        ULONG                i, usageLength, value;
        pPreparsedData = NULL;
        pButtonCaps    = NULL;
        pValueCaps     = NULL;
        PUSAGE usages = NULL;
        hHeap          = GetProcessHeap();
        //
        // Get the preparsed data block
        //
        CHECK( GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize) == 0 );
        CHECK( pPreparsedData = (PHIDP_PREPARSED_DATA)HeapAlloc(hHeap, 0, bufferSize) );
        CHECK( (int)GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) >= 0 );
        //
        // Get the joystick's capabilities
        //
        // Button caps
        CHECK( HidP_GetCaps(pPreparsedData, &Caps) == HIDP_STATUS_SUCCESS )
        CHECK( pButtonCaps = (PHIDP_BUTTON_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_BUTTON_CAPS) * Caps.NumberInputButtonCaps) );
        capsLength = Caps.NumberInputButtonCaps;
        CHECK( HidP_GetButtonCaps(HidP_Input, pButtonCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS )
        int g_NumberOfButtons = pButtonCaps->Range.UsageMax - pButtonCaps->Range.UsageMin + 1;
        // Value caps
        CHECK( pValueCaps = (PHIDP_VALUE_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps) );
        capsLength = Caps.NumberInputValueCaps;
        CHECK( HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS )
        //
        // Get the pressed buttons - only length required to tell if any are pressed
        //
        usageLength = HidP_MaxUsageListLength(HidP_Input, pButtonCaps->UsagePage, pPreparsedData);
        usages = (PUSAGE)HeapAlloc(hHeap, 0, usageLength * sizeof(USAGE));
        CHECK(
            HidP_GetUsages(
                HidP_Input, pButtonCaps->UsagePage, 0, usages, &usageLength, pPreparsedData,
                (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid
            ) == HIDP_STATUS_SUCCESS );
        *buttons = (usageLength > 0);
        //
        // Get the state of discrete-valued-controls
        //
        for(i = 0; i < Caps.NumberInputValueCaps; i++)
        {
            CHECK(
                HidP_GetUsageValue(
                    HidP_Input, pValueCaps[i].UsagePage, 0, pValueCaps[i].Range.UsageMin, &value, pPreparsedData,
                    (PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid
                ) == HIDP_STATUS_SUCCESS );
            switch(pValueCaps[i].Range.UsageMin)
            {
            case 0x30:  // X-axis
                *x = (LONG)value - 128;
                break;
            case 0x31:  // Y-axis
                *y = (LONG)value - 128;
                break;
            }
        }
        //
        // Clean up
        //
    Error:
        SAFE_FREE(pPreparsedData);
        SAFE_FREE(pButtonCaps);
        SAFE_FREE(pValueCaps);
        SAFE_FREE(usages);
    }
    ADAPTERS_API int ProcessInput(HANDLE wParam, HANDLE lParam, unsigned char *buttons, int *x, int *y)
    {
        UINT      bufferSize;
        GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER));
        PRAWINPUT pRawInput = (PRAWINPUT)HeapAlloc(GetProcessHeap(), 0, bufferSize);
        if(!pRawInput)
            return -1;
        GetRawInputData((HRAWINPUT)lParam, RID_INPUT, pRawInput, &bufferSize, sizeof(RAWINPUTHEADER));
        int index = FindIndex(pRawInput->header.hDevice);
        if (index < 0) {
            for(int j = 0; j < MAX_GAMEPADS; j++)
                if (! deviceNames[j]) {
                    if (ReadDeviceName(j, pRawInput->header.hDevice)) {
                        index = j;
                        break;
                    }
                }
        }
        ParseRawInput(pRawInput, buttons, x, y);
        HeapFree(GetProcessHeap(), 0, pRawInput);

        return index;
    }

在成功编译了上面的示例C++代码并尝试将其链接到Microsoft Visual Studio 2013 DLL项目中后,我遇到了一些链接器错误,这些错误涉及hdi库C函数。当我不清楚地记得将hdi.lib及其路径添加到Microsoft Visual Studio 2013 DLL项目链接器依赖项中时,试图找出原因是非常令人困惑的。这个问题可能与adapter.cpp中"hdisdi.h"的外部C链接直接相关吗?此外,请确定我们构建名为Adapter?的Microsoft Visual Studio 2013 DLL项目所需的确切包含文件和正确的链接器库?

使用WDK 7(也称为Windows 7 Raw Input HID API),WDK附带的标头与Visual Studio附带的标头不兼容

用于Windows 的USB HID API

你好,

去年,我用USB HID设备做了一些工作,我对使用Windows HID API(HID.dll)与它们通信的困难感到有点沮丧。我可以确定的是,HID.dll的头和导入库不存在于平台SDK中,而只存在于Windows驱动程序包(WDK)中。在过去,人们可以下载WDK,并将其include\和lib\路径添加到Visual Studio中,还可以#include和链接到hid.lib,一切都会很好。然而,随着WDK 7.0.0.0的发布,这种情况似乎发生了变化。对于WDK 7,WDK附带的标头与Visual Studio附带的标头不兼容。

然后似乎就剩下一个选择了:要么告诉你的Visual Studio项目忽略标准包含路径,转而使用WDK路径,要么使用WDK构建一个包装DLL,并在你的应用程序中与之链接(使用Visual Studio和Platform SDK构建)。我已经完成了后者,并创建了HIDAPI(HIDAPI.dll)。HIDAPI是围绕hidsdi.h系统调用的包装器,使用WDK构建。任何Windows可执行文件都可以链接到hidapi.dll,而无需担心平台SDK。它提供了一个干净的接口,适合于做应用程序开发人员需要做的事情,以便与USB HID设备通信。

我去年就这么做了,但最终还是把它放在了GitHub上,并完成了我今天为它搭建的简单网页。

需要明确的是,这是为在Windows上运行的C和C++应用程序而设计的。我也想在Linux中实现它,为HID设备提供一个可移植的接口。

该项目的网站位于:http://www.signal11.us/oss/hidapi/

请检查一下,让我知道它是否对你有用。

阿兰。


我今天整个下午都在努力。然而,有一个可能的解决方案:

HIDAPI可以从GitHub下载。下载页面提供了一个zip文件。要获得最新的trunk修订版(如果您安装了git,请运行以下操作:git克隆git://github.com/signal11/hidapi.git构建说明

Windows:使用Visual Studio在windows/目录中生成.sln文件。

Linux:切换到linux/目录并运行make。

Mac OS X:切换到mac/目录并运行make。

要构建测试GUI:在Windows上,在hidtest/目录中生成.sln文件。请确保首先按照README.txt中的说明设置externals(Fox Toolkit)。在Linux和Mac上,从hidtest/目录运行make。请确保首先按照README.txt

中的说明安装fox工具包

早上好,我对这个堆栈溢出问题的回答是阅读并应用以下URLS:

https://forum.pjrc.com/threads/24049-windows-dll-for-rawhid

WDK(Windows驱动程序套件)和VC++标头问题

https://social.msdn.microsoft.com/Forums/vstudio/en-US/522a7180-c6aa-439f-963f-0ed10d49a239/how-do-i-use-wdk-with-visual-studio-2008?forum=vcgeneral

谢谢你,节日快乐。