更改 USBController 类以接受来自 COM 设备的 HID 类设备的输入

问题描述

我有 2 个存储库,它们组成了我正在处理的项目。 1 是 Windows software,另一个"firmware",用于我从 ItsyBitsy 微控制器构建的设备。我已尽力浏览文档,但不知下一步应该做什么。

该设备与我相信的所有 AdaFruit 微控制器一样,认用作 COM 设备,但该设备的用途要求其驱动方式类似于鼠标或键盘(作为 HID 设备)。更改外部设备的功能似乎很容易,但我不知道从哪里开始连接到 Windows 软件中的设备。我希望对我现有的代码进行一些细微的修改可以帮助我解决这个问题。

这是我当前的代码

#include "stdafx.h"
#include "USBController.h"
//#include <objbase.h>
//#include <initguid.h>
#include <Setupapi.h>
#include <ntddser.h>
#pragma comment(lib,"Setupapi.lib")

HANDLE CUSBController::OpenComPort(const CString& PortSpecifier)
{
    HANDLE hPort = CreateFile(PortSpecifier,GENERIC_READ,NULL,OPEN_EXISTING,NULL);
    if (hPort == INVALID_HANDLE_VALUE)
        return INVALID_HANDLE_VALUE;
    PurgeComm(hPort,PURGE_RXCLEAR);
    DCB dcb = { 0 };
    if (!GetCommState(hPort,&dcb))
    {
        CloseHandle(hPort);
        return INVALID_HANDLE_VALUE;
    }
    dcb.Baudrate = CBR_9600;
    dcb.ByteSize = 8;
    dcb.Parity = nopARITY;
    dcb.StopBits = OnesTOPBIT;
    if (!SetCommState(hPort,&dcb))
    {
        CloseHandle(hPort);
        return INVALID_HANDLE_VALUE;
    }

    SetCommMask(hPort,EV_RXCHAR | EV_ERR); //receive character event

    // Read this carefully because timeouts are important
    // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-commtimeouts
    COMMTIMEOUTS timeouts = { 0 };

    return hPort;
}
void CUSBController::CloseComPort(HANDLE hPort)
{
    PurgeComm(hPort,PURGE_RXCLEAR);
    CloseHandle(hPort);
}
bool CUSBController::IsComPortOpened(HANDLE hPort)
{
    return hPort != INVALID_HANDLE_VALUE;
}
int CUSBController::ReadByte(HANDLE hPort)
{
    int retVal;

    BYTE Byte;
    DWORD dwBytesTransferred;
    if (FALSE == ReadFile(hPort,&Byte,1,&dwBytesTransferred,0)) //read 1
        retVal = 0x101;
    retVal = Byte;

    return retVal;
}

/*************************************************************************
* Serial port enumeration routines
*
* The EnumSerialPort function will populate an array of SSerInfo structs,* each of which contains @R_33_4045@ion about one serial port present in
* the system. Note that this code must be linked with setupapi.lib,* which is included with the Win32 SDK.
*
* by Zach Gorman <[email protected]>
*
* copyright (c) 2002 Archetype Auction Software,Inc. All rights reserved.
*
* Redistribution and use in source and binary forms,with or without
* modification,are permitted provided that the following condition is
* met: Redistributions of source code must retain the above copyright
* notice,this condition and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXpressed OR IMPLIED
* WARRANTIES,INCLUDING,BUT NOT LIMITED TO,THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND fitness FOR A PARTIculaR PURPOSE ARE
* disCLaimED. IN NO EVENT SHALL ARCHETYPE AUCTION SOFTWARE OR ITS
* AFFILIATES BE LIABLE FOR ANY DIRECT,INDIRECT,INCIDENTAL,SPECIAL,* EXEMPLARY,OR CONSEQUENTIAL damAGES (INCLUDING,* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,DATA,OR
* PROFITS; OR BUSInesS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY,WHETHER IN CONTRACT,STRICT LIABILITY,OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE,EVEN IF ADVISED OF THE POSSIBILITY OF SUCH damAGE.
************************************************************************/

// The following define is from ntddser.h in the DDK. It is also
// needed for serial port enumeration.
/*#ifndef GUID_CLASS_COMPORT
DEFINE_GUID(GUID_CLASS_COMPORT,0x86e0d1e0L,0x8089,0x11d0,0x9c,0xe4,\
    0x08,0x00,0x3e,0x30,0x1f,0x73);
#endif*/


//---------------------------------------------------------------
// Routine for enumerating the available serial ports.
// Throws a CString on failure,describing the error that
// occurred. If bIgnoreBusyPorts is TRUE,ports that can't
// be opened for read/write access are not included.
void  CUSBController::EnumSerialPorts(CArray<SSerInfo,SSerInfo&>& asi,BOOL bIgnoreBusyPorts)
{
    // Clear the output array
    asi.RemoveAll();

    // Use different techniques to enumerate the available serial
    // ports,depending on the OS we're using
    OsveRSIONINFO vi;
    vi.dwOsversionInfoSize = sizeof(vi);
    if (!::GetVersionEx(&vi)) {
        CString str;
        str.Format("Could not get OS version. (err=%lx)",GetLastError());
        throw str;
    }
    // Handle windows 9x and NT4 specially
    if (vi.dwMajorVersion < 5) {
        if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
            EnumPortsWNt4(asi);
        else
            EnumPortsW9x(asi);
    }
    else {
        // Win2k and later support a standard API for
        // enumerating hardware devices.
        EnumPortsWdm(asi);
    }

    for (int ii = 0; ii < asi.GetSize(); ii++)
    {
        SSerInfo& rsi = asi[ii];
        if (bIgnoreBusyPorts) {
            // Only display ports that can be opened for read/write
            HANDLE hCom = CreateFile(rsi.strDevPath,GENERIC_READ | GENERIC_WRITE,/* comm devices must be opened w/exclusive-access */
                NULL,/* no security attrs */
                OPEN_EXISTING,/* comm devices must use OPEN_EXISTING */
                0,/* not overlapped I/O */
                NULL  /* hTemplate must be NULL for comm devices */
            );
            if (hCom == INVALID_HANDLE_VALUE) {
                // It can't be opened; remove it.
                asi.RemoveAt(ii);
                ii--;
                continue;
            }
            else {
                // It can be opened! Close it and add it to the list
                ::CloseHandle(hCom);
            }
        }

        // Come up with a name for the device.
        // If there is no friendly name,use the port name.
        if (rsi.strFriendlyName.IsEmpty())
            rsi.strFriendlyName = rsi.strPortName;

        // If there is no description,try to make one up from
        // the friendly name.
        if (rsi.strPortDesc.IsEmpty()) {
            // If the port name is of the form "Acme Port (COM3)"
            // then strip off the " (COM3)"
            rsi.strPortDesc = rsi.strFriendlyName;
            int startdex = rsi.strPortDesc.Find(" (");
            int enddex = rsi.strPortDesc.Find(")");
            if (startdex > 0 && enddex ==
                (rsi.strPortDesc.GetLength() - 1))
                rsi.strPortDesc = rsi.strPortDesc.Left(startdex);
        }
    }
}

// Helpers for EnumSerialPorts
void CUSBController::EnumPortsWdm(CArray<SSerInfo,SSerInfo&>& asi)
{
    CString strErr;
    // Create a device @R_33_4045@ion set that will be the container for 
    // the device interfaces.
    GUID* guidDev = (GUID*)&GUID_CLASS_COMPORT;

    HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
    SP_DEVICE_INTERFACE_DETAIL_DATA* pDetData = NULL;

    try {
        hDevInfo = SetupDiGetClassDevs(guidDev,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
        );

        if (hDevInfo == INVALID_HANDLE_VALUE)
        {
            strErr.Format("SetupDiGetClassDevs Failed. (err=%lx)",GetLastError());
            throw strErr;
        }

        // Enumerate the serial ports
        BOOL bOk = TRUE;
        SP_DEVICE_INTERFACE_DATA ifcData;
        DWORD dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256;
        pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA*) new char[dwDetDataSize];
        // This is required,according to the documentation. Yes,// it's weird.
        ifcData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
        pDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

        for (DWORD ii = 0; bOk; ii++) {
            bOk = SetupDiEnumDeviceInterfaces(hDevInfo,guidDev,ii,&ifcData);
            if (bOk) {
                // Got a device. Get the details.
                SP_DEVINFO_DATA devdata = { sizeof(SP_DEVINFO_DATA) };
                bOk = SetupDiGetDeviceInterfaceDetail(hDevInfo,&ifcData,pDetData,dwDetDataSize,&devdata);
                if (bOk) {
                    CString strDevPath(pDetData->DevicePath);
                    // Got a path to the device. Try to get some more info.
                    CHAR fname[256];
                    CHAR desc[256];
                    BOOL bSuccess = SetupDiGetDeviceRegistryProperty(hDevInfo,&devdata,SPDRP_FRIENDLYNAME,(PBYTE)fname,sizeof(fname),NULL);
                    bSuccess = bSuccess && SetupDiGetDeviceRegistryProperty(hDevInfo,SPDRP_DEVICEDESC,(PBYTE)desc,sizeof(desc),NULL);
                    BOOL bUsbDevice = FALSE;
                    CHAR Locinfo[256];
                    if (SetupDiGetDeviceRegistryProperty(hDevInfo,SPDRP_LOCATION_@R_33_4045@ION,(PBYTE)Locinfo,sizeof(Locinfo),NULL))
                    {
                        // Just check the first three characters to determine
                        // if the port is connected to the USB bus. This isn't
                        // an infallible method; it would be better to use the
                        // BUS GUID. Currently,Windows doesn't let you query
                        // that though (SPDRP_BUSTYPEGUID seems to exist in
                        // documentation only).
                        bUsbDevice = (strncmp(Locinfo,"USB",3) == 0);
                    }
                    if (bSuccess) {
                        // Add an entry to the array
                        SSerInfo si;
                        si.strDevPath = strDevPath;
                        si.strFriendlyName = fname;
                        si.strPortDesc = desc;
                        si.bUsbDevice = bUsbDevice;
                        asi.Add(si);
                    }

                }
                else {
                    strErr.Format("SetupDiGetDeviceInterfaceDetail Failed. (err=%lx)",GetLastError());
                    throw strErr;
                }
            }
            else {
                DWORD err = GetLastError();
                if (err != ERROR_NO_MORE_ITEMS) {
                    strErr.Format("SetupDiEnumDeviceInterfaces Failed. (err=%lx)",err);
                    throw strErr;
                }
            }
        }
    }
    catch (CString strCatchErr) {
        strErr = strCatchErr;
    }

    if (pDetData != NULL)
        delete[](char*)pDetData;
    if (hDevInfo != INVALID_HANDLE_VALUE)
        SetupDiDestroyDeviceInfoList(hDevInfo);

    if (!strErr.IsEmpty())
        throw strErr;
}
void CUSBController::EnumPortsWNt4(CArray<SSerInfo,SSerInfo&>& asi)
{
    // NT4's driver model is totally different,and not that
    // many people use NT4 anymore. Just try all the COM ports
    // between 1 and 16
    SSerInfo si;
    for (int ii = 1; ii <= 16; ii++) {
        CString strPort;
        strPort.Format("COM%d",ii);
        si.strDevPath = CString("\\\\.\\") + strPort;
        si.strPortName = strPort;
        asi.Add(si);
    }
}
void CUSBController::EnumPortsW9x(CArray<SSerInfo,SSerInfo&>& asi)
{
    // Look at all keys in HKLM\Enum,searching for subkeys named
    // *PNP0500 and *PNP0501. Within these subkeys,search for
    // sub-subkeys containing value entries with the name "PORTNAME"
    // Search all subkeys of HKLM\Enum\USBPORTS for PORTNAME entries.

    // First,open HKLM\Enum
    HKEY hkEnum = NULL;
    HKEY hkSubEnum = NULL;
    HKEY hkSubSubEnum = NULL;

    try {
        if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Enum",KEY_READ,&hkEnum) != ERROR_SUCCESS)
            throw CString("Could not read from HKLM\\Enum");

        // Enumerate the subkeys of HKLM\Enum
        char acSubEnum[128];
        DWORD dwSubEnumIndex = 0;
        DWORD dwSize = sizeof(acSubEnum);
        while (RegEnumKeyEx(hkEnum,dwSubEnumIndex++,acSubEnum,&dwSize,NULL) == ERROR_SUCCESS)
        {
            HKEY hkSubEnum = NULL;
            if (RegOpenKeyEx(hkEnum,&hkSubEnum) != ERROR_SUCCESS)
                throw CString("Could not read from HKLM\\Enum\\") + acSubEnum;

            // Enumerate the subkeys of HKLM\Enum\*\,looking for keys
            // named *PNP0500 and *PNP0501 (or anything in USBPORTS)
            BOOL bUsbDevice = (strcmp(acSubEnum,"USBPORTS") == 0);
            char acSubSubEnum[128];
            dwSize = sizeof(acSubSubEnum);  // set the buffer size
            DWORD dwSubSubEnumIndex = 0;
            while (RegEnumKeyEx(hkSubEnum,dwSubSubEnumIndex++,acSubSubEnum,NULL) == ERROR_SUCCESS)
            {
                BOOL bMatch = (strcmp(acSubSubEnum,"*PNP0500") == 0 ||
                    strcmp(acSubSubEnum,"*PNP0501") == 0 ||
                    bUsbDevice);
                if (bMatch) {
                    HKEY hkSubSubEnum = NULL;
                    if (RegOpenKeyEx(hkSubEnum,&hkSubSubEnum) != ERROR_SUCCESS)
                        throw CString("Could not read from HKLM\\Enum\\") +
                        acSubEnum + "\\" + acSubSubEnum;
                    SearchPnpKeyW9x(hkSubSubEnum,bUsbDevice,asi);
                    RegCloseKey(hkSubSubEnum);
                    hkSubSubEnum = NULL;
                }

                dwSize = sizeof(acSubSubEnum);  // restore the buffer size
            }

            RegCloseKey(hkSubEnum);
            hkSubEnum = NULL;
            dwSize = sizeof(acSubEnum); // restore the buffer size
        }
    }
    catch (CString strError) {
        if (hkEnum != NULL)
            RegCloseKey(hkEnum);
        if (hkSubEnum != NULL)
            RegCloseKey(hkSubEnum);
        if (hkSubSubEnum != NULL)
            RegCloseKey(hkSubSubEnum);
        throw strError;
    }

    RegCloseKey(hkEnum);
}
void CUSBController::SearchPnpKeyW9x(HKEY hkPnp,BOOL bUsbDevice,CArray<SSerInfo,SSerInfo&>& asi)
{
    // Enumerate the subkeys of the given PNP key,looking for values with
    // the name "PORTNAME"
    // First,open HKLM\Enum
    HKEY hkSubPnp = NULL;

    try {
        // Enumerate the subkeys of HKLM\Enum\*\PNP050[01]
        char acSubPnp[128];
        DWORD dwSubPnpIndex = 0;
        DWORD dwSize = sizeof(acSubPnp);
        while (RegEnumKeyEx(hkPnp,dwSubPnpIndex++,acSubPnp,NULL) == ERROR_SUCCESS)
        {
            HKEY hkSubPnp = NULL;
            if (RegOpenKeyEx(hkPnp,&hkSubPnp) != ERROR_SUCCESS)
                throw CString("Could not read from HKLM\\Enum\\...\\")
                + acSubPnp;

            // Look for the PORTNAME value
            char acValue[128];
            dwSize = sizeof(acValue);
            if (RegQueryValueEx(hkSubPnp,"PORTNAME",(BYTE*)acValue,&dwSize) == ERROR_SUCCESS)
            {
                CString strPortName(acValue);

                // Got the portname value. Look for a friendly name.
                CString strFriendlyName;
                dwSize = sizeof(acValue);
                if (RegQueryValueEx(hkSubPnp,"FRIENDLYNAME",&dwSize) == ERROR_SUCCESS)
                    strFriendlyName = acValue;

                // Prepare an entry for the output array.
                SSerInfo si;
                si.strDevPath = CString("\\\\.\\") + strPortName;
                si.strPortName = strPortName;
                si.strFriendlyName = strFriendlyName;
                si.bUsbDevice = bUsbDevice;

                // Overwrite duplicates.
                BOOL bDup = FALSE;
                for (int ii = 0; ii < asi.GetSize() && !bDup; ii++)
                {
                    if (asi[ii].strPortName == strPortName) {
                        bDup = TRUE;
                        asi[ii] = si;
                    }
                }
                if (!bDup) {
                    // Add an entry to the array
                    asi.Add(si);
                }
            }

            RegCloseKey(hkSubPnp);
            hkSubPnp = NULL;
            dwSize = sizeof(acSubPnp);  // restore the buffer size
        }
    }
    catch (CString strError) {
        if (hkSubPnp != NULL)
            RegCloseKey(hkSubPnp);
        throw strError;
    }
}

完整回购:https://github.com/Skewjo/SysLat_Software文件https://github.com/Skewjo/SysLat_Software/blob/master/USBController.h 实现文件https://github.com/Skewjo/SysLat_Software/blob/master/USBController.cpp

  • Windows 软件(特别是 USBController.cpp 中的代码)不被视为驱动程序……对吗?
    • 如果这是真的,那么除了软件之外,我是否还需要创建一个新的 Windows 驱动程序,还是应该使用某种现有的驱动程序?
  • Windows 软件的 COM 代码与 HID 代码有何不同?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)