添加项目文件。

This commit is contained in:
2026-03-26 10:45:29 +08:00
parent 10441f488c
commit b576d3f19d
48 changed files with 4812 additions and 0 deletions

168
DRI/Dri_Consumer.cpp Normal file
View File

@@ -0,0 +1,168 @@
#include "DRI/Dri_Consumer.h"
namespace
{
bool Dri_Consumer_Func_BeginRead(Dri_Consumer_Struct_Port* p_Port, QString* p_TextStatus)
{
if (!p_Port->IsOpened || (p_Port->InputLength == 0))
{
return false;
}
if (p_Port->IsReadPending)
{
return true;
}
ResetEvent(p_Port->HandleEvent);
p_Port->ReadBuffer.fill(0, p_Port->InputLength);
p_Port->OverlappedRead = {};
p_Port->OverlappedRead.hEvent = p_Port->HandleEvent;
DWORD BytesRead = 0;
if (ReadFile(
p_Port->HandleRead,
p_Port->ReadBuffer.data(),
p_Port->InputLength,
&BytesRead,
&p_Port->OverlappedRead) ||
(GetLastError() == ERROR_IO_PENDING))
{
p_Port->IsReadPending = true;
return true;
}
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("Consumer 接口启动异步读取失败:%1").arg(GetLastError());
}
return false;
}
} // namespace
void Dri_Consumer_Func_Close(Dri_Consumer_Struct_Port* p_Port)
{
if ((p_Port->HandleRead != INVALID_HANDLE_VALUE) && p_Port->IsReadPending)
{
CancelIoEx(p_Port->HandleRead, &p_Port->OverlappedRead);
}
if (p_Port->HandleRead != INVALID_HANDLE_VALUE)
{
CloseHandle(p_Port->HandleRead);
}
if (p_Port->HandleEvent != nullptr)
{
CloseHandle(p_Port->HandleEvent);
}
*p_Port = Dri_Consumer_Struct_Port();
}
bool Dri_Consumer_Func_Open(
Dri_Consumer_Struct_Port* p_Port,
const Mid_Struct_DeviceConfig& DeviceConfig,
QString* p_TextStatus)
{
Dri_Consumer_Func_Close(p_Port);
QString DevicePath;
quint16 InputLength = 0;
if (!Mid_Func_FindHidInterface(
Mid_Func_GetConsumerMatch(DeviceConfig),
&DevicePath,
&InputLength,
nullptr))
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("未找到 Consumer 接口000C / 0001。");
}
return false;
}
p_Port->HandleRead = CreateFileW(
reinterpret_cast<LPCWSTR>(DevicePath.utf16()),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
nullptr);
if (p_Port->HandleRead == INVALID_HANDLE_VALUE)
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("Consumer 接口打开失败:%1").arg(GetLastError());
}
return false;
}
p_Port->HandleEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
if (p_Port->HandleEvent == nullptr)
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("Consumer 接口创建事件失败:%1").arg(GetLastError());
}
Dri_Consumer_Func_Close(p_Port);
return false;
}
p_Port->InputLength = InputLength;
p_Port->ReadBuffer = QByteArray(InputLength, 0);
p_Port->OverlappedRead.hEvent = p_Port->HandleEvent;
p_Port->IsOpened = true;
Dri_Consumer_Func_BeginRead(p_Port, p_TextStatus);
return true;
}
bool Dri_Consumer_Func_Read(
Dri_Consumer_Struct_Port* p_Port,
Mid_Struct_RawPacket* p_Packet,
QString* p_TextStatus)
{
p_Packet->IsValid = false;
p_Packet->ByteArray.clear();
p_Packet->PortName = QStringLiteral("Consumer");
if (!p_Port->IsOpened)
{
return false;
}
if (!p_Port->IsReadPending)
{
Dri_Consumer_Func_BeginRead(p_Port, p_TextStatus);
return false;
}
DWORD BytesRead = 0;
if (!GetOverlappedResult(p_Port->HandleRead, &p_Port->OverlappedRead, &BytesRead, FALSE))
{
const DWORD ErrorCode = GetLastError();
if (ErrorCode == ERROR_IO_INCOMPLETE)
{
return false;
}
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("Consumer 接口读包失败:%1").arg(ErrorCode);
}
Dri_Consumer_Func_Close(p_Port);
return false;
}
p_Port->IsReadPending = false;
if (BytesRead > 0)
{
p_Packet->IsValid = true;
p_Packet->ByteArray = p_Port->ReadBuffer.left(static_cast<int>(BytesRead));
}
Dri_Consumer_Func_BeginRead(p_Port, p_TextStatus);
return p_Packet->IsValid;
}

35
DRI/Dri_Consumer.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include "MID/Mid_Def.h"
#include <QtCore/QByteArray>
#include <QtCore/QString>
#include <Windows.h>
/*
* DRI Consumer 层:抽象 HID Consumer 端点,封装 Win32 句柄、缓存与状态。
*
*/
struct Dri_Consumer_Struct_Port
{
/* 设备读写所需的句柄与异步上下文 */
HANDLE HandleRead = INVALID_HANDLE_VALUE;
HANDLE HandleEvent = nullptr;
OVERLAPPED OverlappedRead = {};
/* 快速判断是否已经建立连接以及当前是否正挂起读 */
bool IsOpened = false;
bool IsReadPending = false;
/* USB 输入报文长度 & 环形缓存 */
quint16 InputLength = 0;
QByteArray ReadBuffer;
};
/* 主动释放 Consumer 端口,确保句柄/Overlapped 都被 reset。 */
void Dri_Consumer_Func_Close(Dri_Consumer_Struct_Port* p_Port);
/* 打开设备:按照 VID/PID 查询接口并创建 Overlapped 句柄。 */
bool Dri_Consumer_Func_Open(Dri_Consumer_Struct_Port* p_Port,
const Mid_Struct_DeviceConfig& DeviceConfig,
QString* p_TextStatus);
/* 读取最新的 Consumer 原始包,失败时返回状态描述。 */
bool Dri_Consumer_Func_Read(Dri_Consumer_Struct_Port* p_Port,
Mid_Struct_RawPacket* p_Packet,
QString* p_TextStatus);

282
DRI/Dri_NkroRaw.cpp Normal file
View File

@@ -0,0 +1,282 @@
#include "DRI/Dri_NkroRaw.h"
#include <QtCore/QVector>
#include <Windows.h>
namespace
{
/* ---------- 设备过滤与 Usage 映射 ---------- */
QString Dri_NkroRaw_Func_GetDevicePath(HANDLE DeviceHandle)
{
if (DeviceHandle == nullptr)
{
return QString();
}
UINT NeedChars = 0;
GetRawInputDeviceInfoW(DeviceHandle, RIDI_DEVICENAME, nullptr, &NeedChars);
if (NeedChars == 0)
{
return QString();
}
QVector<wchar_t> Buffer(static_cast<int>(NeedChars) + 1, 0);
if (GetRawInputDeviceInfoW(DeviceHandle, RIDI_DEVICENAME, Buffer.data(), &NeedChars) == static_cast<UINT>(-1))
{
return QString();
}
return QString::fromWCharArray(Buffer.constData()).trimmed();
}
bool Dri_NkroRaw_Func_IsTargetDevice(const QString& DevicePath, const Mid_Struct_DeviceConfig& DeviceConfig)
{
if (DevicePath.isEmpty())
{
return false;
}
const QString UpperPath = DevicePath.toUpper();
return UpperPath.contains(QStringLiteral("VID_%1").arg(DeviceConfig.VendorId, 4, 16, QLatin1Char('0')).toUpper()) &&
UpperPath.contains(QStringLiteral("PID_%1").arg(DeviceConfig.ProductId, 4, 16, QLatin1Char('0')).toUpper());
}
quint16 Dri_NkroRaw_Func_GetUsage(const RAWKEYBOARD& Keyboard)
{
const bool IsE0 = (Keyboard.Flags & RI_KEY_E0) != 0;
const bool IsE1 = (Keyboard.Flags & RI_KEY_E1) != 0;
const USHORT ScanCode = Keyboard.MakeCode;
if (IsE1)
{
return 0;
}
if (IsE0)
{
switch (ScanCode)
{
case 0x35: return 0x0054;
case 0x1C: return 0x0058;
case 0x1D: return 0x00E4;
case 0x38: return 0x00E6;
case 0x5B: return 0x00E3;
case 0x5C: return 0x00E7;
default:
return 0;
}
}
switch (ScanCode)
{
case 0x45: return 0x0053;
case 0x37: return 0x0055;
case 0x4A: return 0x0056;
case 0x4E: return 0x0057;
case 0x47: return 0x005F;
case 0x48: return 0x0060;
case 0x49: return 0x0061;
case 0x4B: return 0x005C;
case 0x4C: return 0x005D;
case 0x4D: return 0x005E;
case 0x4F: return 0x0059;
case 0x50: return 0x005A;
case 0x51: return 0x005B;
case 0x52: return 0x0062;
case 0x53: return 0x0063;
case 0x1D: return 0x00E0;
case 0x2A: return 0x00E1;
case 0x36: return 0x00E5;
case 0x38: return 0x00E2;
default:
return 0;
}
}
} // namespace
/* ---------- 生命周期 ---------- */
void Dri_NkroRaw_Func_Close(Dri_NkroRaw_Struct_Port* p_Port)
{
*p_Port = Dri_NkroRaw_Struct_Port();
}
bool Dri_NkroRaw_Func_Open(
Dri_NkroRaw_Struct_Port* p_Port,
const Mid_Struct_DeviceConfig& DeviceConfig,
void* WindowHandle,
QString* p_TextStatus)
{
Dri_NkroRaw_Func_Close(p_Port);
if (WindowHandle == nullptr)
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("NKRO 原生输入链路打开失败:窗口句柄为空。");
}
return false;
}
RAWINPUTDEVICE Device = {};
Device.usUsagePage = MID_CONST_USAGE_PAGE_NKRO;
Device.usUsage = MID_CONST_USAGE_NKRO;
Device.dwFlags = RIDEV_INPUTSINK;
Device.hwndTarget = reinterpret_cast<HWND>(WindowHandle);
if (!RegisterRawInputDevices(&Device, 1, sizeof(Device)))
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("NKRO 原生输入链路注册失败:%1").arg(GetLastError());
}
return false;
}
p_Port->IsOpened = true;
p_Port->WindowHandle = WindowHandle;
p_Port->DeviceConfig = DeviceConfig;
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("已启用 NKRO 原生输入链路。");
}
return true;
}
/* ---------- RawInput 主流程 ---------- */
bool Dri_NkroRaw_Func_HandleNativeMessage(
Dri_NkroRaw_Struct_Port* p_Port,
void* p_Message,
QString* p_TextStatus)
{
if (!p_Port->IsOpened || (p_Message == nullptr))
{
return false;
}
MSG* p_Msg = reinterpret_cast<MSG*>(p_Message);
if (p_Msg->message != WM_INPUT)
{
return false;
}
UINT NeedSize = 0;
GetRawInputData(reinterpret_cast<HRAWINPUT>(p_Msg->lParam), RID_INPUT, nullptr, &NeedSize, sizeof(RAWINPUTHEADER));
if (NeedSize == 0)
{
return false;
}
QByteArray Buffer(static_cast<int>(NeedSize), 0);
if (GetRawInputData(
reinterpret_cast<HRAWINPUT>(p_Msg->lParam),
RID_INPUT,
Buffer.data(),
&NeedSize,
sizeof(RAWINPUTHEADER)) == static_cast<UINT>(-1))
{
return false;
}
RAWINPUT* p_Input = reinterpret_cast<RAWINPUT*>(Buffer.data());
if (p_Input->header.dwType != RIM_TYPEKEYBOARD)
{
return false;
}
const QString DevicePath = Dri_NkroRaw_Func_GetDevicePath(p_Input->header.hDevice);
if (!Dri_NkroRaw_Func_IsTargetDevice(DevicePath, p_Port->DeviceConfig))
{
return false;
}
const quint16 Usage = Dri_NkroRaw_Func_GetUsage(p_Input->data.keyboard);
if (Usage == 0)
{
return false;
}
const bool IsPressed = (p_Input->data.keyboard.Flags & RI_KEY_BREAK) == 0;
bool IsChanged = false;
if ((Usage >= 0x00E0) && (Usage <= 0x00E7))
{
const quint8 BitMask = static_cast<quint8>(1U << (Usage - 0x00E0));
const quint8 OldModifier = p_Port->Modifier;
if (IsPressed)
{
p_Port->Modifier = static_cast<quint8>(p_Port->Modifier | BitMask);
}
else
{
p_Port->Modifier = static_cast<quint8>(p_Port->Modifier & static_cast<quint8>(~BitMask));
}
IsChanged = (OldModifier != p_Port->Modifier);
}
else
{
const int ByteIndex = Usage / 8;
const quint8 BitMask = static_cast<quint8>(1U << (Usage % 8));
quint8 Value = static_cast<quint8>(p_Port->UsageBitmap.at(ByteIndex));
const bool OldPressed = (Value & BitMask) != 0;
if (OldPressed == IsPressed)
{
return false;
}
Value = IsPressed ? static_cast<quint8>(Value | BitMask) : static_cast<quint8>(Value & static_cast<quint8>(~BitMask));
p_Port->UsageBitmap[ByteIndex] = static_cast<char>(Value);
IsChanged = true;
}
if (!IsChanged)
{
return false;
}
if (p_Port->DevicePath != DevicePath)
{
p_Port->DevicePath = DevicePath;
if (p_TextStatus != nullptr)
{
*p_TextStatus = QStringLiteral("原生输入已命中目标设备:%1").arg(DevicePath);
}
}
Mid_Struct_RawPacket Packet;
Packet.IsValid = true;
Packet.PortName = QStringLiteral("NKRO(原生输入)");
Packet.ByteArray = QByteArray(MID_CONST_PACKET_SIZE_NKRO, 0);
Packet.ByteArray[0] = static_cast<char>(Mid_Enum_ReportId_Nkro);
Packet.ByteArray[1] = static_cast<char>(p_Port->Modifier);
for (int Index = 0; Index < MID_CONST_USAGE_BITMAP_SIZE; ++Index)
{
Packet.ByteArray[2 + Index] = p_Port->UsageBitmap.at(Index);
}
p_Port->PacketQueue.append(Packet);
return true;
}
bool Dri_NkroRaw_Func_Read(
Dri_NkroRaw_Struct_Port* p_Port,
Mid_Struct_RawPacket* p_Packet,
QString*)
{
p_Packet->IsValid = false;
p_Packet->ByteArray.clear();
p_Packet->PortName = QStringLiteral("NKRO(原生输入)");
if (!p_Port->IsOpened || p_Port->PacketQueue.isEmpty())
{
return false;
}
*p_Packet = p_Port->PacketQueue.takeFirst();
return p_Packet->IsValid;
}

40
DRI/Dri_NkroRaw.h Normal file
View File

@@ -0,0 +1,40 @@
#pragma once
#include "MID/Mid_Def.h"
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QString>
/*
* DRI NKRO RAW 层:负责注册 Windows RAWINPUT接收键盘 104+ 通道。
* 这里集中保存窗口句柄、设备配置、按键队列与 Modifier/Bitmap 缓冲。
*/
struct Dri_NkroRaw_Struct_Port
{
/* 运行状态:是否已注册 RAWINPUT 以及宿主窗口 HWND */
bool IsOpened = false;
void* WindowHandle = nullptr;
Mid_Struct_DeviceConfig DeviceConfig;
/* 最新的 Modifier 字节和 NKRO Usage 位图 */
quint8 Modifier = 0;
QByteArray UsageBitmap = QByteArray(MID_CONST_USAGE_BITMAP_SIZE, 0);
/* RAWINPUT 读取结果放入 PacketQueue供上层逻辑顺序消费 */
QList<Mid_Struct_RawPacket> PacketQueue;
QString DevicePath;
};
/* 注销 RAWINPUT 并清空所有缓存。 */
void Dri_NkroRaw_Func_Close(Dri_NkroRaw_Struct_Port* p_Port);
/* 注册 RAWINPUT绑定目标窗口并监听指定 VID/PID。 */
bool Dri_NkroRaw_Func_Open(Dri_NkroRaw_Struct_Port* p_Port,
const Mid_Struct_DeviceConfig& DeviceConfig,
void* WindowHandle,
QString* p_TextStatus);
/* 把 Windows 消息转为 NKRO 数据:核心教学入口。 */
bool Dri_NkroRaw_Func_HandleNativeMessage(Dri_NkroRaw_Struct_Port* p_Port,
void* p_Message,
QString* p_TextStatus);
/* 上层每次取一个封装好的 RawPacket。 */
bool Dri_NkroRaw_Func_Read(Dri_NkroRaw_Struct_Port* p_Port,
Mid_Struct_RawPacket* p_Packet,
QString* p_TextStatus);

338
DRI/Dri_Vendor.cpp Normal file
View File

@@ -0,0 +1,338 @@
#include "DRI/Dri_Vendor.h"
#include <hidsdi.h>
#pragma comment(lib, "hid.lib")
namespace
{
/* ---------- 句柄与状态小工具 ---------- */
void Dri_Vendor_Func_SetStatus(QString* p_TextStatus, const QString& Text)
{
if (p_TextStatus != nullptr)
{
*p_TextStatus = Text;
}
}
HANDLE Dri_Vendor_Func_OpenHandle(const QString& DevicePath, DWORD DesiredAccess, DWORD Flags)
{
return CreateFileW(
reinterpret_cast<LPCWSTR>(DevicePath.utf16()),
DesiredAccess,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
Flags,
nullptr);
}
bool Dri_Vendor_Func_BeginRead(Dri_Vendor_Struct_Port* p_Port, QString* p_TextStatus)
{
if (!p_Port->IsOpened || (p_Port->InputLength == 0))
{
return false;
}
if (p_Port->IsReadPending)
{
return true;
}
ResetEvent(p_Port->HandleEvent);
p_Port->ReadBuffer.fill(0, p_Port->InputLength);
p_Port->OverlappedRead = {};
p_Port->OverlappedRead.hEvent = p_Port->HandleEvent;
DWORD BytesRead = 0;
if (ReadFile(
p_Port->HandleRead,
p_Port->ReadBuffer.data(),
p_Port->InputLength,
&BytesRead,
&p_Port->OverlappedRead) ||
(GetLastError() == ERROR_IO_PENDING))
{
p_Port->IsReadPending = true;
return true;
}
Dri_Vendor_Func_SetStatus(
p_TextStatus,
QStringLiteral("Vendor 接口启动异步读取失败:%1").arg(GetLastError()));
return false;
}
HANDLE Dri_Vendor_Func_OpenWriteHandle(const QString& DevicePath, QString* p_TextError)
{
HANDLE HandleWrite = Dri_Vendor_Func_OpenHandle(DevicePath, GENERIC_WRITE, 0);
if (HandleWrite != INVALID_HANDLE_VALUE)
{
return HandleWrite;
}
const DWORD WriteError = GetLastError();
HandleWrite = Dri_Vendor_Func_OpenHandle(DevicePath, 0, 0);
if ((HandleWrite == INVALID_HANDLE_VALUE) && (p_TextError != nullptr))
{
*p_TextError = QStringLiteral("GENERIC_WRITE=%1ZeroAccess=%2")
.arg(WriteError)
.arg(GetLastError());
}
return HandleWrite;
}
bool Dri_Vendor_Func_TryWritePacket(
HANDLE HandleWrite,
quint16 OutputLength,
const QByteArray& Packet,
QString* p_TextError)
{
if ((HandleWrite == INVALID_HANDLE_VALUE) || Packet.isEmpty())
{
return false;
}
QByteArray Buffer = Packet;
if ((OutputLength > 0) && (Buffer.size() < OutputLength))
{
Buffer.append(OutputLength - Buffer.size(), 0);
}
if (HidD_SetOutputReport(HandleWrite, Buffer.data(), static_cast<ULONG>(Buffer.size())))
{
return true;
}
const DWORD SetReportError = GetLastError();
DWORD BytesWritten = 0;
if (WriteFile(
HandleWrite,
Buffer.constData(),
static_cast<DWORD>(Buffer.size()),
&BytesWritten,
nullptr) &&
(BytesWritten == static_cast<DWORD>(Buffer.size())))
{
return true;
}
if (p_TextError != nullptr)
{
*p_TextError = QStringLiteral("SetOutputReport=%1WriteFile=%2")
.arg(SetReportError)
.arg(GetLastError());
}
return false;
}
} // namespace
/* ---------- 生命周期 ---------- */
void Dri_Vendor_Func_Close(Dri_Vendor_Struct_Port* p_Port)
{
if ((p_Port->HandleRead != INVALID_HANDLE_VALUE) && p_Port->IsReadPending)
{
CancelIoEx(p_Port->HandleRead, &p_Port->OverlappedRead);
}
for (HANDLE* p_Handle : { &p_Port->HandleRead, &p_Port->HandleWriteVendor, &p_Port->HandleWriteNkro, &p_Port->HandleEvent })
{
if ((*p_Handle != nullptr) && (*p_Handle != INVALID_HANDLE_VALUE))
{
CloseHandle(*p_Handle);
}
}
*p_Port = Dri_Vendor_Struct_Port();
}
bool Dri_Vendor_Func_Open(
Dri_Vendor_Struct_Port* p_Port,
const Mid_Struct_DeviceConfig& DeviceConfig,
QString* p_TextStatus)
{
Dri_Vendor_Func_Close(p_Port);
QString VendorPath;
quint16 InputLength = 0;
quint16 OutputLength = 0;
if (!Mid_Func_FindHidInterface(
Mid_Func_GetVendorMatch(DeviceConfig),
&VendorPath,
&InputLength,
&OutputLength))
{
Dri_Vendor_Func_SetStatus(p_TextStatus, QStringLiteral("未找到 Vendor 接口FF00 / 0002。"));
return false;
}
p_Port->HandleRead = Dri_Vendor_Func_OpenHandle(VendorPath, GENERIC_READ, FILE_FLAG_OVERLAPPED);
if (p_Port->HandleRead == INVALID_HANDLE_VALUE)
{
Dri_Vendor_Func_SetStatus(
p_TextStatus,
QStringLiteral("Vendor 接口打开读句柄失败:%1").arg(GetLastError()));
return false;
}
p_Port->HandleEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
if (p_Port->HandleEvent == nullptr)
{
Dri_Vendor_Func_SetStatus(
p_TextStatus,
QStringLiteral("Vendor 接口创建事件失败:%1").arg(GetLastError()));
Dri_Vendor_Func_Close(p_Port);
return false;
}
p_Port->HandleWriteVendor = Dri_Vendor_Func_OpenWriteHandle(VendorPath, nullptr);
QString NkroPath;
quint16 NkroOutputLength = 0;
if (Mid_Func_FindHidInterface(
Mid_Func_GetNkroMatch(DeviceConfig),
&NkroPath,
nullptr,
&NkroOutputLength))
{
QString TextError;
p_Port->HandleWriteNkro = Dri_Vendor_Func_OpenWriteHandle(NkroPath, &TextError);
p_Port->NkroWriteOutputLength = NkroOutputLength;
if (p_Port->HandleWriteNkro == INVALID_HANDLE_VALUE)
{
Dri_Vendor_Func_SetStatus(
p_TextStatus,
QStringLiteral("Vendor 读链路已打开,但 NKRO 写句柄打开失败:%1").arg(TextError));
}
}
else
{
Dri_Vendor_Func_SetStatus(
p_TextStatus,
QStringLiteral("Vendor 读链路已打开,但没有找到 NKRO 写接口。"));
}
p_Port->InputLength = InputLength;
p_Port->OutputLength = OutputLength;
p_Port->ReadBuffer = QByteArray(InputLength, 0);
p_Port->OverlappedRead.hEvent = p_Port->HandleEvent;
p_Port->IsOpened = true;
if (!Dri_Vendor_Func_BeginRead(p_Port, p_TextStatus))
{
Dri_Vendor_Func_Close(p_Port);
return false;
}
return true;
}
/* ---------- 读写流程 ---------- */
bool Dri_Vendor_Func_Read(
Dri_Vendor_Struct_Port* p_Port,
Mid_Struct_RawPacket* p_Packet,
QString* p_TextStatus)
{
*p_Packet = Mid_Struct_RawPacket();
p_Packet->PortName = QStringLiteral("Vendor");
if (!p_Port->IsOpened)
{
return false;
}
if (!p_Port->IsReadPending)
{
Dri_Vendor_Func_BeginRead(p_Port, p_TextStatus);
return false;
}
DWORD BytesRead = 0;
if (!GetOverlappedResult(p_Port->HandleRead, &p_Port->OverlappedRead, &BytesRead, FALSE))
{
const DWORD ErrorCode = GetLastError();
if (ErrorCode == ERROR_IO_INCOMPLETE)
{
return false;
}
Dri_Vendor_Func_SetStatus(
p_TextStatus,
QStringLiteral("Vendor 接口读包失败:%1").arg(ErrorCode));
Dri_Vendor_Func_Close(p_Port);
return false;
}
p_Port->IsReadPending = false;
if (BytesRead > 0)
{
p_Packet->IsValid = true;
p_Packet->ByteArray = p_Port->ReadBuffer.left(static_cast<int>(BytesRead));
}
Dri_Vendor_Func_BeginRead(p_Port, p_TextStatus);
return p_Packet->IsValid;
}
bool Dri_Vendor_Func_Write(
Dri_Vendor_Struct_Port* p_Port,
const QByteArray& ByteArray,
QString* p_TextStatus)
{
if (!p_Port->IsOpened)
{
Dri_Vendor_Func_SetStatus(
p_TextStatus,
QStringLiteral("Vendor 读链路未打开,不能发送掩码。"));
return false;
}
QStringList Errors;
const auto TryWriteTo = [&](HANDLE HandleWrite,
quint16 OutputLength,
const QString& SuccessText,
const QString& ErrorPrefix)
{
QString TextError;
if (Dri_Vendor_Func_TryWritePacket(HandleWrite, OutputLength, ByteArray, &TextError))
{
Dri_Vendor_Func_SetStatus(p_TextStatus, SuccessText);
return true;
}
if (!TextError.isEmpty())
{
Errors.append(ErrorPrefix.arg(TextError));
}
return false;
};
if (TryWriteTo(
p_Port->HandleWriteNkro,
p_Port->NkroWriteOutputLength,
QStringLiteral("掩码已发送到 NKRO 写接口。"),
QStringLiteral("NKRO 写接口发送失败:%1")))
{
return true;
}
if (TryWriteTo(
p_Port->HandleWriteVendor,
p_Port->OutputLength,
QStringLiteral("掩码已发送到 Vendor 写接口。"),
QStringLiteral("Vendor 写接口发送失败:%1")))
{
return true;
}
Dri_Vendor_Func_SetStatus(
p_TextStatus,
Errors.isEmpty()
? QStringLiteral("没有可用的写句柄,掩码发送未执行。")
: Errors.join(QStringLiteral("\n")));
return false;
}

42
DRI/Dri_Vendor.h Normal file
View File

@@ -0,0 +1,42 @@
#pragma once
#include "MID/Mid_Def.h"
#include <QtCore/QByteArray>
#include <QtCore/QString>
#include <Windows.h>
/*
* DRI Vendor 层:读写固件的 Vendor/NKRO HID 接口,支撑逻辑层功能诊断。
*
*/
struct Dri_Vendor_Struct_Port
{
/* 读写句柄Vendor 写、NKRO 写与通用读各占一个 */
HANDLE HandleRead = INVALID_HANDLE_VALUE;
HANDLE HandleWriteVendor = INVALID_HANDLE_VALUE;
HANDLE HandleWriteNkro = INVALID_HANDLE_VALUE;
HANDLE HandleEvent = nullptr;
OVERLAPPED OverlappedRead = {};
/* 运行状态 + 报文长度缓存,便于 UI/LOGIC 层快速判断 */
bool IsOpened = false;
bool IsReadPending = false;
quint16 InputLength = 0;
quint16 OutputLength = 0;
quint16 NkroWriteOutputLength = 0;
QByteArray ReadBuffer;
};
/* 关闭全部句柄,安全退出时务必调用以便复用设备。 */
void Dri_Vendor_Func_Close(Dri_Vendor_Struct_Port* p_Port);
/* 根据 VID/PID 打开对应 HID 接口,并创建必需句柄。 */
bool Dri_Vendor_Func_Open(Dri_Vendor_Struct_Port* p_Port,
const Mid_Struct_DeviceConfig& DeviceConfig,
QString* p_TextStatus);
/* 读取 Vendor 报文:一次返回一个 Mid_Struct_RawPacket。 */
bool Dri_Vendor_Func_Read(Dri_Vendor_Struct_Port* p_Port,
Mid_Struct_RawPacket* p_Packet,
QString* p_TextStatus);
/* 写入 Vendor/NKRO 报文ByteArray 按固件定义组包。 */
bool Dri_Vendor_Func_Write(Dri_Vendor_Struct_Port* p_Port,
const QByteArray& ByteArray,
QString* p_TextStatus);