Winapi GetRawInputDeviceInfo表示RIDI_DEVICENAME的缓冲区大小为1个字符
我从RIDI_DEVICENAME中得到了荒谬的行为。根据文件, 返回值 类型:Winapi GetRawInputDeviceInfo表示RIDI_DEVICENAME的缓冲区大小为1个字符,winapi,input,raw-input,Winapi,Input,Raw Input,我从RIDI_DEVICENAME中得到了荒谬的行为。根据文件, 返回值 类型:UINT 如果成功,此函数将返回一个非负数,指示复制到pData的字节数 如果pData对于数据来说不够大,则函数返回-1。如果pData为NULL,则函数返回一个零值。在这两种情况下,pcbSize被设置为pData缓冲区所需的最小大小 调用GetLastError以识别任何其他错误 忽略-1不是UINT返回类型中可表示的值这一明显问题,函数似乎应该告诉我所需的缓冲区大小,如果我提供了此大小的缓冲区,函数应该成功,
UINT
如果成功,此函数将返回一个非负数,指示复制到pData
的字节数
如果pData
对于数据来说不够大,则函数返回-1
。如果pData
为NULL
,则函数返回一个零值。在这两种情况下,pcbSize
被设置为pData
缓冲区所需的最小大小
调用GetLastError
以识别任何其他错误
忽略-1
不是UINT
返回类型中可表示的值这一明显问题,函数似乎应该告诉我所需的缓冲区大小,如果我提供了此大小的缓冲区,函数应该成功,或者至少遵循它自己的失败规则
然而,我根本看不到这一点。在Windows 10上,当pData
为空时,Unicode版本的函数将pcbSize
设置为1
,否则不使用它,在所有情况下都会失败。当pData
为空时,该函数的ANSI版本将pcbSize
设置为2
,否则会将传入的任何值加倍,并且仍然失败
用于任一版本测试代码的标题:
#define WIN32_EXTRA_LEAN 1
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
std::string GetRawInputDeviceName( HANDLE hRaw )
{
UINT numChars = 0u;
INT validChars;
validChars = static_cast<INT>(::GetRawInputDeviceInfoA(hRaw, RIDI_DEVICENAME, nullptr, &numChars));
auto lasterror = ::GetLastError();
if (lasterror != ERROR_INSUFFICIENT_BUFFER) {
std::wcerr << L"Failed to get length of name of raw input device, retcode = " << validChars << L", last error = " << lasterror << L"\n";
return {};
}
std::string name;
name.resize(numChars);
validChars = static_cast<INT>(::GetRawInputDeviceInfoA(hRaw, RIDI_DEVICENAME, &name[0], &numChars));
lasterror = ::GetLastError();
if (validChars > 0) {
name.resize(validChars);
return name;
}
else {
std::wcerr << L"Failed to get name of raw input device, retcode = " << validChars << L", last error = " << lasterror << L"\n";
return {};
}
}
std::wstring GetRawInputDeviceName( HANDLE hRaw )
{
UINT numChars = 0u;
INT validChars;
validChars = static_cast<INT>(::GetRawInputDeviceInfoW(hRaw, RIDI_DEVICENAME, nullptr, &numChars));
auto lasterror = ::GetLastError();
if (lasterror != ERROR_INSUFFICIENT_BUFFER) {
std::wcerr << L"Failed to get length of name of raw input device, retcode = " << validChars << L", last error = " << lasterror << L"\n";
return {};
}
std::wstring name;
name.resize(numChars);
validChars = static_cast<INT>(::GetRawInputDeviceInfoW(hRaw, RIDI_DEVICENAME, &name[0], &numChars));
lasterror = ::GetLastError();
if (validChars > 0) {
name.resize(validChars);
return name;
}
else {
std::wcerr << L"Failed to get name of raw input device, retcode = " << validChars << L", last error = " << lasterror << L"\n";
return {};
}
}
#定义WIN32_额外_精益1
#包括
#包括
#包括
#包括
#包括
ANSI测试代码:
#define WIN32_EXTRA_LEAN 1
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
std::string GetRawInputDeviceName( HANDLE hRaw )
{
UINT numChars = 0u;
INT validChars;
validChars = static_cast<INT>(::GetRawInputDeviceInfoA(hRaw, RIDI_DEVICENAME, nullptr, &numChars));
auto lasterror = ::GetLastError();
if (lasterror != ERROR_INSUFFICIENT_BUFFER) {
std::wcerr << L"Failed to get length of name of raw input device, retcode = " << validChars << L", last error = " << lasterror << L"\n";
return {};
}
std::string name;
name.resize(numChars);
validChars = static_cast<INT>(::GetRawInputDeviceInfoA(hRaw, RIDI_DEVICENAME, &name[0], &numChars));
lasterror = ::GetLastError();
if (validChars > 0) {
name.resize(validChars);
return name;
}
else {
std::wcerr << L"Failed to get name of raw input device, retcode = " << validChars << L", last error = " << lasterror << L"\n";
return {};
}
}
std::wstring GetRawInputDeviceName( HANDLE hRaw )
{
UINT numChars = 0u;
INT validChars;
validChars = static_cast<INT>(::GetRawInputDeviceInfoW(hRaw, RIDI_DEVICENAME, nullptr, &numChars));
auto lasterror = ::GetLastError();
if (lasterror != ERROR_INSUFFICIENT_BUFFER) {
std::wcerr << L"Failed to get length of name of raw input device, retcode = " << validChars << L", last error = " << lasterror << L"\n";
return {};
}
std::wstring name;
name.resize(numChars);
validChars = static_cast<INT>(::GetRawInputDeviceInfoW(hRaw, RIDI_DEVICENAME, &name[0], &numChars));
lasterror = ::GetLastError();
if (validChars > 0) {
name.resize(validChars);
return name;
}
else {
std::wcerr << L"Failed to get name of raw input device, retcode = " << validChars << L", last error = " << lasterror << L"\n";
return {};
}
}
std::string GetRawInputDeviceName(句柄hRaw)
{
UINT numChars=0u;
INT-validChars;
validChars=static_cast(::getrawinputdeviceinfo(hRaw、RIDI_DEVICENAME、nullptr和numChars));
自动lasterror=::GetLastError();
if(lasterror!=错误\u缓冲区不足){
std::wcerrGetRawInputDeviceName
在声明/实现/文档中有几个错误
事实上,将返回值声明为有符号(LONG
或INT
)而不是UINT更为正确
存在3种情况:
1。函数返回负值(或者如果需要-1
):这是错误
根据设计,必须设置最后一个错误。但事实并非如此
始终设置(实现错误)
最常见的错误:
- pcbSize或pData指向无效或只读内存位置。在这种情况下,通常会出现错误
(翻译自error\u NOACCESS
)状态\u访问\u违规
- hDevice not valid handle-
返回错误\u无效\u handle
- uiCommand无效RIDI\U XXX常量-
错误\u无效\u参数
- *pcbSize对于数据来说不够大-在这种情况下*pcbSize被设置为pData缓冲区所需的最小大小。
同样-仅在这种情况下(错误\u缓冲区不足
)存在检测调用-1
GetLastError();
RIDI\u PREPARSEDDATA
或RIDI\u DEVICEINFO
)或
写入缓冲区的字符(如果RIDI\u DEVICENAME
)
所以这里的文档是错误的:
PCB尺寸
[进来,出去]
指向变量的指针,该变量包含中数据的大小(以字节为单位)
pData
如果RIDI_DEVICENAME
位于字符中
因此,在设计(返回值类型-无符号)和混合字节/字符方面已经存在非常严重的问题。许多不同的情况
但在实现中存在着严重的错误。在函数的开头,句柄hDevice被转换为指针
PDEVICEINFO-PDEVICEINFO=HMValidateHandle(hDevice,键入设备信息);
(如果0返回-我们在退出时得到-1,带有错误\u无效\u句柄
)
在DEVICEINFO
存在UNICODE\u字符串ustrName
-此名称并复制到用户模式
switch (uiCommand) {
case RIDI_DEVICENAME:
/*
* N.b. UNICODE_STRING counts the length by the BYTE count, not by the character count.
* Our APIs always treat the strings by the character count. Thus, for RIDI_DEVICNAME
* only, cbOutSize holds the character count, not the byte count, in spite of its
* name. Confusing, but cch is the way to be consistent.
*/
cbOutSize = pDeviceInfo->ustrName.Length / sizeof(WCHAR) + 1; // for Null terminator
break;
//...
}
必需的cbOutSize
与cbBufferSize=*pcbSize;
和如果(cbBufferSize>=cbOutSize)
api开始复制操作
存在下一个代码
case RIDI_DEVICENAME:
if (cbOutSize <= 2) { // !!!! error !!!!
retval = -1;
goto leave;
}
RtlCopyMemory(pData, pDeviceInfo->ustrName.Buffer, pDeviceInfo->ustrName.Length);
((WCHAR*)pData)[1] = '\\'; // convert nt prefix ( \??\ ) to win32 ( \\?\ )
((WCHAR*)pData)[cbOutSize - 1] = 0; // make it null terminated
break;
然后,getrawInputDeviceInfo
添加其他错误比较getrawInputDeviceInfo
-由于某种原因,*pcbSize的值在2上是倍数。但是,在所有情况下都会出现此错误。
请注意,DeviceName(由上的驱动程序返回的字符串格式化)有非常严格的限制:
如果驱动程序返回带有非法字符的ID,系统将
错误检查。ID中具有以下值的字符是非法的
对于本IRP:
- 小于或等于0x20(“”)
- 大于0x7F
- 等于0x2C(',')
*2
缓冲区大小
然后我已经查看了代码中的错误-您在
GetRawInputDeviceInfo
之后无条件地调用::GetLastError();
-但返回值只有在api返回-1
时才有意义
解释观察到的行为:
对于本地设备,api一般工作正常(如果代码中没有错误)
对于终端服务设备-was 0长度ustrName
。因此,如果我们在pData中传递NULL,则返回值将为
pDeviceInfo->ustrName.Length / sizeof(WCHAR) + 1;
因为pDeviceInfo->ustname.Length==0
-1将在*pcbSize内返回
如果错误地返回了版本-2*1==2
。
但是当e pass在pData中不为NULL时,我们就陷入了这个陷阱
if (cbOutSize <= 2) { // !!!! error !!!!
retval = -1;
goto leave;
}
用法示例:(/RTCs
必须禁用)
void Demo()
{
PRAWINPUTDEVICELIST pRawInput