首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC++

VC++ 取得 USB 遊戲搖桿按鍵輸入:HID 介面完整實作

     編輯:VC++

前言

本文示範如何使用 Windows HID (Human Interface Device) API 列舉系統 USB 裝置,識別遊戲搖桿,並即時讀取其輸入報告資料。編譯環境需 Windows 2000/XP + VC++ 6.0 + Win2K DDK。

核心流程

  1. 呼叫 HidD_GetHidGuid 取得 HID 類別 GUID
  2. SetupDiGetClassDevs 列舉所有 HID 裝置介面
  3. 迴圈 SetupDiEnumDeviceInterfaces 取得各裝置介面資料
  4. SetupDiGetDeviceInterfaceDetail 取得裝置路徑
  5. CreateFile 開啟裝置控制代碼
  6. HidD_GetAttributes、HidD_GetManufacturerString、HidD_GetProductString 取得廠商/產品資訊
  7. HidD_GetPreparsedData + HidP_GetCaps 取得輸入報告長度
  8. 迴圈 ReadFile 讀取輸入報告,解析按鍵狀態

完整程式碼

#include <windows.h>
#include <setupapi.h>
#include <hidsdi.h>
#include <hidpi.h>
#include <stdio.h>
#include <tchar.h>

#pragma comment(lib, "setupapi.lib")
#pragma comment(lib, "hid.lib")

extern "C" { /* DDK 標頭檔已包含必要宣告 */ }

void main()
{
    GUID HidGuid;
    HidD_GetHidGuid(&HidGuid);
    _tprintf(_T("HID Class GUID: %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n"),
        HidGuid.Data1, HidGuid.Data2, HidGuid.Data3,
        HidGuid.Data4[0], HidGuid.Data4[1], HidGuid.Data4[2],
        HidGuid.Data4[3], HidGuid.Data4[4], HidGuid.Data4[5],
        HidGuid.Data4[6], HidGuid.Data4[7]);

    HDEVINFO hDevInfo = SetupDiGetClassDevs(&HidGuid, NULL, NULL,
        DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE) return;

    SP_DEVICE_INTERFACE_DATA did = { sizeof(SP_DEVICE_INTERFACE_DATA) };
    for (DWORD idx = 0; ; idx++)
    {
        if (!SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &HidGuid, idx, &did))
        {
            if (GetLastError() == ERROR_NO_MORE_ITEMS) break;
            continue;
        }

        _tprintf(_T("\nFound USB Device #%d:\n"), idx);

        // 取得裝置路徑
        DWORD len = 0;
        SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, NULL, 0, &len, NULL);
        PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(len);
        pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
        SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, pDetail, len, NULL, NULL);
        _tprintf(_T("Device Path: %s\n"), pDetail->DevicePath);

        // 開啟裝置
        HANDLE hDev = CreateFile(pDetail->DevicePath, GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
        if (hDev == INVALID_HANDLE_VALUE) { free(pDetail); continue; }

        // 取得裝置屬性
        HIDD_ATTRIBUTES attr = { sizeof(HIDD_ATTRIBUTES) };
        HidD_GetAttributes(hDev, &attr);
        _tprintf(_T("Vendor ID: 0x%04X\nProduct ID: 0x%04X\nVersion: 0x%04X\n"),
            attr.VendorID, attr.ProductID, attr.VersionNumber);

        WCHAR wStr[256]; TCHAR tStr[256];
        HidD_GetManufacturerString(hDev, wStr, sizeof(wStr));
        wcstombs(tStr, wStr, 256); _tprintf(_T("Manufacturer: %s\n"), tStr);
        HidD_GetProductString(hDev, wStr, sizeof(wStr));
        wcstombs(tStr, wStr, 256); _tprintf(_T("Product: %s\n"), tStr);

        // 取得輸入報告長度
        PHIDP_PREPARSED_DATA pPreparsed;
        HidD_GetPreparsedData(hDev, &pPreparsed);
        HIDP_CAPS caps;
        HidP_GetCaps(pPreparsed, &caps);
        _tprintf(_T("Input Report Length: %d bytes\n"), caps.InputReportByteLength);

        // 讀取輸入報告迴圈
        BYTE* pReport = new BYTE[caps.InputReportByteLength];
        DWORD bytesRead;
        do
        {
            ReadFile(hDev, pReport, caps.InputReportByteLength, &bytesRead, NULL);
            if (bytesRead == caps.InputReportByteLength)
            {
                for (UINT i = 0; i < bytesRead; i++)
                    _tprintf(_T("%02X%s"), pReport[i], (i == bytesRead-1) ? _T("\r") : _T("-"));
                
                // 範例:偵測特定按鍵 (第 n-1 位元組 = 0x20) 退出
                if (bytesRead >= 2 && pReport[bytesRead-2] == 0x20)
                { _tprintf(_T("\n")); break; }
            }
            Sleep(10);
        } while (bytesRead == caps.InputReportByteLength);

        delete[] pReport;
        CloseHandle(hDev);
        free(pDetail);
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);
}

關鍵點說明

  • SetupDiGetDeviceInterfaceDetail 需兩次呼叫:第一次取得緩衝區大小,第二次填入資料
  • pDetail->cbSize 必須正確設定,否則 API 失敗
  • 輸入報告格式依裝置而異,需參考裝置 HID 描述符或實驗觀察
  • 範例以 0x20 作為退出條件,實際應解析具體按鍵位元
  • 需連結 setupapi.lib、hid.lib 並包含 DDK 標頭檔

延伸應用

掌握此架構後,可進一步:

  • 建立對應裝置的按鍵映射表
  • 實作搖桿震動回饋 (Output Report)
  • 整合至遊戲引擎輸入系統
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved