实现一个小型的C DLL来连接我们的C++代码和RawInput
Implement a small C DLL to interface between our C++ code and RawInput
阅读这篇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
早上好,我对这个堆栈溢出问题的回答是阅读并应用以下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
谢谢你,节日快乐。
- 为什么在运行时没有向我们提供有关分段错误的更多信息?
- 我们可以访问一个不存在的联盟的成员吗
- 当套接字连接断开时检测C/C++Unix
- 如果编译的源代码是特定于它编译的硬件的,我们如何分发它
- 当使用透明的std函数对象时,我们还需要写空的尖括号吗
- 如何在C++中读取空格分隔的输入 当我们不知道输入的数量时
- 我们可以删除链表中静态内存中的节点吗
- 无法在windows上使用mingw将sqlite3与c连接
- 到连接组件算法的问题(递归)
- QTcpSocket在不阻塞GUI的情况下重新连接到服务器
- 无法在C++中建立与MySQL数据库的连接
- 为什么我们要为avl树实现返回一个指向节点的指针,而不是void函数
- PC中的程序和PHONE中的本机描述应用程序之间的数据连接
- 在Qt Creator中,如何在连接到正在运行的进程后查看控制台输出
- 当我们从/tp地址中添加/减去一个整数时会发生什么
- 为什么我们应该为网络连接指定一个端口?
- 我们可以连接qpushbutton以更改QSlider的值吗?
- 我们如何检查我们是否有有效的连接
- 为什么我们需要连接超时管理
- 实现一个小型的C DLL来连接我们的C++代码和RawInput