当前位置: 首页 > news >正文

windows USB 了解

GUID

GUID 是一个 128 位的数字,在全球范围内是独一无二的,常被用于标识软件组件、设备接口等,以保证在不同系统和环境中能唯一识别特定对象。

DEFINE_GUID(GUID_DEVINTERFACE_USCUSTOMKEYS, 0x12345678, 0x1234, 0x5678, 0x12, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78);

DEFINE_GUID:这是一个宏,在 Windows 编程里用于定义 GUID。它在 <guiddef.h> 头文件中被定义,此宏会创建一个 GUID 类型的全局变量,并且对其进行初始化。

GUID_DEVINTERFACE_USCUSTOMKEYS:这是所定义的 GUID 的名称。在后续代码里,可借助这个名称来引用该 GUID。

0x12345678, 0x1234, 0x5678, 0x12, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78:这些是构成 128 位 GUID 的具体数值。GUID 通常会被拆分成几个部分,以便于表示和处理。

枚举设备

#include <windows.h>
#include <iostream>
#include <setupapi.h>
#include <devguid.h>
#include <regstr.h>
#include <tchar.h>

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

// 这里使用 USB 设备类的 GUID 作为示例,你可以根据需要替换为其他设备类的 GUID
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);

int main() {
    HDEVINFO hDevInfo;
    SP_DEVICE_INTERFACE_DATA deviceInterfaceData = { 0 };
    PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL;
    DWORD requiredSize = 0;
    DWORD index = 0;

    // 获取指定设备类的设备信息集
    hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE) {
        std::cerr << "SetupDiGetClassDevs failed with error: " << GetLastError() << std::endl;
        return 1;
    }

    // 初始化 SP_DEVICE_INTERFACE_DATA 结构体
    deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

    // 枚举设备接口
    while (SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, index, &deviceInterfaceData)) {
        // 获取所需的缓冲区大小
        SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL);

        // 分配缓冲区
        deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, requiredSize);
        if (deviceInterfaceDetailData == NULL) {
            std::cerr << "LocalAlloc failed with error: " << GetLastError() << std::endl;
            SetupDiDestroyDeviceInfoList(hDevInfo);
            return 1;
        }
        deviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

        // 获取设备接口的详细信息
        if (!SetupDiGetDeviceInterfaceDetail(hDevInfo, &deviceInterfaceData, deviceInterfaceDetailData, requiredSize, NULL, NULL)) {
            std::cerr << "SetupDiGetDeviceInterfaceDetail failed with error: " << GetLastError() << std::endl;
            LocalFree(deviceInterfaceDetailData);
            index++;
            continue;
        }

        // 打印设备接口的详细信息
        std::wcout << L"Device Path: " << deviceInterfaceDetailData->DevicePath << std::endl;

        // 释放缓冲区
        LocalFree(deviceInterfaceDetailData);
        index++;
    }

    // 检查枚举是否因错误而终止
    if (GetLastError() != ERROR_NO_MORE_ITEMS) {
        std::cerr << "SetupDiEnumDeviceInterfaces failed with error: " << GetLastError() << std::endl;
    }

    // 销毁设备信息集
    SetupDiDestroyDeviceInfoList(hDevInfo);

    return 0;
}    

SetupDiGetClassDevs

HDEVINFO SetupDiGetClassDevs(
  const GUID          *ClassGuid,
  PCTSTR              Enumerator,
  HWND                hwndParent,
  DWORD               Flags
);

功能:此函数会创建一个包含指定设备类的设备信息集,该集合可用来枚举和操作设备。
参数:
ClassGuid:指向设备类的 GUID(全局唯一标识符)的指针,指定要枚举的设备类。若为 NULL,则会返回所有设备类的设备。
Enumerator:指定设备枚举器名称的字符串,如 USB 或 PCI。若为 NULL,则不限制枚举器。
hwndParent:与设备安装程序相关的父窗口句柄,通常设为 NULL。
Flags:控制函数行为的标志位,常用标志有:
DIGCF_PRESENT:仅返回当前存在的设备。
DIGCF_DEVICEINTERFACE:返回支持指定设备接口类的设备。
返回值:若函数调用成功,会返回设备信息集的句柄;若失败,则返回 INVALID_HANDLE_VALUE。

SetupDiEnumDeviceInterfaces

BOOL SetupDiEnumDeviceInterfaces(
  HDEVINFO                 DeviceInfoSet,
  PSP_DEVINFO_DATA         DeviceInfoData,
  const GUID               *InterfaceClassGuid,
  DWORD                    MemberIndex,
  PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
);

功能:对设备信息集中的设备接口进行枚举。
参数:
DeviceInfoSet:由 SetupDiGetClassDevs 函数返回的设备信息集的句柄。
DeviceInfoData:指向 SP_DEVINFO_DATA 结构体的指针,用于指定特定设备。若为 NULL,则枚举所有设备。
InterfaceClassGuid:指向设备接口类的 GUID 的指针,指定要枚举的接口类。
MemberIndex:要枚举的设备接口的索引,从 0 开始。
DeviceInterfaceData:指向 SP_DEVICE_INTERFACE_DATA 结构体的指针,用于接收枚举到的设备接口信息。
返回值:若函数调用成功,返回 TRUE;若失败或没有更多设备接口可供枚举,返回 FALSE,此时可通过 GetLastError 函数获取错误代码。若错误代码为 ERROR_NO_MORE_ITEMS,则表示枚举结束。

SetupDiGetInterfaceDeviceDetail

BOOL SetupDiGetInterfaceDeviceDetail(
  HDEVINFO                         DeviceInfoSet,
  PSP_DEVICE_INTERFACE_DATA        DeviceInterfaceData,
  PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData,
  DWORD                            DeviceInterfaceDetailDataSize,
  PDWORD                           RequiredSize,
  PSP_DEVINFO_DATA                 DeviceInfoData
);

功能:获取指定设备接口的详细信息,像设备路径之类的。
参数:
DeviceInfoSet:由 SetupDiGetClassDevs 函数返回的设备信息集的句柄。
DeviceInterfaceData:指向 SP_DEVICE_INTERFACE_DATA 结构体的指针,包含要获取详细信息的设备接口信息。
DeviceInterfaceDetailData:指向 SP_DEVICE_INTERFACE_DETAIL_DATA 结构体的指针,用于接收设备接口的详细信息。
DeviceInterfaceDetailDataSize:DeviceInterfaceDetailData 结构体的大小。
RequiredSize:指向 DWORD 类型变量的指针,用于接收所需的缓冲区大小。
DeviceInfoData:指向 SP_DEVINFO_DATA 结构体的指针,用于接收设备的详细信息。若为 NULL,则不接收该信息。
返回值:若函数调用成功,返回 TRUE;若失败,返回 FALSE,可通过 GetLastError 函数获取错误代码。若所需的缓冲区大小超过 DeviceInterfaceDetailDataSize,则返回 FALSE,且 RequiredSize 会包含所需的缓冲区大小。

SetupDiDestroyDeviceInfoList

BOOL SetupDiDestroyDeviceInfoList(
  HDEVINFO DeviceInfoSet
);

功能:销毁由 SetupDiGetClassDevs 函数创建的设备信息集,释放相关资源。
参数:
DeviceInfoSet:要销毁的设备信息集的句柄。
返回值:若函数调用成功,返回 TRUE;若失败,返回 FALSE,可通过 GetLastError 函数获取错误代码。
总结
这些函数通常按以下顺序使用:
调用 SetupDiGetClassDevs 函数创建设备信息集。
利用 SetupDiEnumDeviceInterfaces 函数枚举设备信息集中的设备接口。
针对每个枚举到的设备接口,调用 SetupDiGetInterfaceDeviceDetail 函数获取详细信息。
最后调用 SetupDiDestroyDeviceInfoList 函数销毁设备信息集,释放资源。

总结

这些函数通常按以下顺序使用:
调用 SetupDiGetClassDevs 函数创建设备信息集。
利用 SetupDiEnumDeviceInterfaces 函数枚举设备信息集中的设备接口。
针对每个枚举到的设备接口,调用 SetupDiGetInterfaceDeviceDetail 函数获取详细信息。
最后调用 SetupDiDestroyDeviceInfoList 函数销毁设备信息集,释放资源。

注意

需要注意环境的编码格式,根据实际使用选择
在这里插入图片描述

开打管道

m_hReadPipe = CreateFile((LPCSTR)m_ReadPipe, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0);

CreateFile 是 Windows API 中的一个函数,用于创建或打开文件、目录、物理磁盘、卷、控制台缓冲区、通信资源、邮件槽、管道等对象。此函数会返回一个句柄,后续可使用该句柄对这些对象进行操作。

  1. (LPCSTR)m_ReadPipe
    含义:这是要打开的对象的名称,在该代码里为管道的名称。m_ReadPipe 应当是一个字符串类型的变量,代表管道的名称。(LPCSTR) 是一个强制类型转换,将 m_ReadPipe 转换为 LPCSTR 类型(即指向以 null 结尾的多字节字符串的指针)。
    注意:在 Unicode 字符集环境下,建议使用 CreateFileW 函数并传入 LPCWSTR 类型的参数,以避免字符编码问题。

  2. GENERIC_READ | GENERIC_WRITE
    含义:这是访问权限标志,用于指定对打开对象的访问方式。
    GENERIC_READ:表示对对象具有读权限,也就是可以从对象中读取数据。
    GENERIC_WRITE:表示对对象具有写权限,即能够向对象中写入数据。
    GENERIC_READ | GENERIC_WRITE:表示同时具有读和写的权限。

  3. FILE_SHARE_READ | FILE_SHARE_WRITE
    含义:这是共享模式标志,用于指定其他进程对该对象的访问权限。
    FILE_SHARE_READ:允许其他进程以读权限打开该对象。
    FILE_SHARE_WRITE:允许其他进程以写权限打开该对象。
    FILE_SHARE_READ | FILE_SHARE_WRITE:表示其他进程可以同时以读和写的权限打开该对象。

  4. NULL
    含义:这是指向 SECURITY_ATTRIBUTES 结构体的指针,用于指定对象的安全属性。若为 NULL,则表示使用默认的安全属性,并且该对象的句柄不能被子进程继承。

  5. OPEN_EXISTING
    含义:这是创建方式标志,用于指定如果对象不存在时的处理方式。OPEN_EXISTING 表示仅当对象已经存在时才打开它,如果对象不存在,函数将失败并返回 INVALID_HANDLE_VALUE。

  6. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED
    含义:这是文件属性和标志的组合。
    FILE_ATTRIBUTE_NORMAL:表示文件没有特殊的属性,是最常见的文件属性。
    FILE_FLAG_OVERLAPPED:表示以异步(重叠)方式打开文件。在异步模式下,对文件的读写操作会立即返回,而不会等待操作完成,可通过 OVERLAPPED 结构体来处理操作结果。

  7. 0
    含义:这是模板文件的句柄,用于指定一个模板文件的句柄,新创建的文件将继承该模板文件的属性。若为 0,则表示不使用模板文件。

返回值
若函数调用成功,会返回一个有效的句柄,该句柄可用于后续对管道的读写操作。
若失败,会返回 INVALID_HANDLE_VALUE,可通过 GetLastError 函数获取具体的错误代码。

不要忘记还有 CloseHandle(m_hReadPipe);
CloseHandle 是 Windows API 中的一个函数,它的主要作用是关闭对象的句柄。在 Windows 操作系统里,许多系统资源(像文件、管道、线程、进程等)都会用句柄来标识,当不再需要使用这些资源时,就需要调用 CloseHandle 函数来关闭对应的句柄,从而释放系统资源。

异步读取

#include <windows.h>
#include <iostream>
#include <string>

const char* WRITE_PIPE_NAME = "\\\\.\\pipe\\Pipe0";
const char* READ_PIPE_NAME = "\\\\.\\pipe\\Pipe1";
const int BUFFER_SIZE = 1024;
const int WRITE_INTERVAL_MS = 3000;
const int MAIN_LOOP_INTERVAL_MS = 100; // 主循环间隔设为 100 毫秒

void WriteAsync(HANDLE hPipe, int writeCount, HANDLE hWriteEvent) {
    OVERLAPPED overlapped = { 0 };
    overlapped.hEvent = hWriteEvent;

    std::string message = "Data write number " + std::to_string(writeCount);
    DWORD bytesWritten;
    if (!WriteFile(hPipe, message.c_str(), static_cast<DWORD>(message.length()), NULL, &overlapped)) {
        if (GetLastError() != ERROR_IO_PENDING) {
            std::cerr << "WriteFile failed: " << GetLastError() << std::endl;
            return;
        }
    }

    DWORD waitResult = WaitForSingleObject(hWriteEvent, INFINITE);
    if (waitResult == WAIT_OBJECT_0) {
        if (!GetOverlappedResult(hPipe, &overlapped, &bytesWritten, FALSE)) {
            std::cerr << "GetOverlappedResult failed: " << GetLastError() << std::endl;
        } else {
            std::cout << "Asynchronous write completed. Bytes written: " << bytesWritten << std::endl;
        }
        ResetEvent(hWriteEvent);
    } else {
        std::cerr << "WaitForSingleObject failed: " << waitResult << std::endl;
    }
}

void ReadAsync(HANDLE hPipe, HANDLE hReadEvent) {
    OVERLAPPED overlapped = { 0 };
    overlapped.hEvent = hReadEvent;

    char buffer[BUFFER_SIZE];
    DWORD bytesRead;
    if (!ReadFile(hPipe, buffer, BUFFER_SIZE, NULL, &overlapped)) {
        if (GetLastError() != ERROR_IO_PENDING) {
            std::cerr << "ReadFile failed: " << GetLastError() << std::endl;
            return;
        }
    }

    DWORD waitResult = WaitForSingleObject(hReadEvent, INFINITE);
    if (waitResult == WAIT_OBJECT_0) {
        if (!GetOverlappedResult(hPipe, &overlapped, &bytesRead, FALSE)) {
            std::cerr << "GetOverlappedResult failed: " << GetLastError() << std::endl;
        } else if (bytesRead > 0) {
            buffer[bytesRead] = '\0';
            std::cout << "Asynchronous read completed. Bytes read: " << bytesRead << std::endl;
            std::cout << "Read data: " << buffer << std::endl;
        }
        ResetEvent(hReadEvent);
    } else {
        std::cerr << "WaitForSingleObject failed: " << waitResult << std::endl;
    }
}

int main() {
    HANDLE hWritePipe = CreateFile(
        WRITE_PIPE_NAME,
        GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL
    );

    if (hWritePipe == INVALID_HANDLE_VALUE) {
        std::cerr << "CreateFile for write pipe failed: " << GetLastError() << std::endl;
        return 1;
    }

    HANDLE hReadPipe = CreateFile(
        READ_PIPE_NAME,
        GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_OVERLAPPED,
        NULL
    );

    if (hReadPipe == INVALID_HANDLE_VALUE) {
        std::cerr << "CreateFile for read pipe failed: " << GetLastError() << std::endl;
        CloseHandle(hWritePipe);
        return 1;
    }

    HANDLE hWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hWriteEvent == NULL) {
        std::cerr << "CreateEvent for write failed: " << GetLastError() << std::endl;
        CloseHandle(hWritePipe);
        CloseHandle(hReadPipe);
        return 1;
    }

    HANDLE hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (hReadEvent == NULL) {
        std::cerr << "CreateEvent for read failed: " << GetLastError() << std::endl;
        CloseHandle(hWriteEvent);
        CloseHandle(hWritePipe);
        CloseHandle(hReadPipe);
        return 1;
    }

    int writeCount = 1;
    DWORD lastWriteTime = GetTickCount();

    while (true) {
        DWORD currentTime = GetTickCount();
        if (currentTime - lastWriteTime >= WRITE_INTERVAL_MS) {
            WriteAsync(hWritePipe, writeCount++, hWriteEvent);
            lastWriteTime = currentTime;
        }

        ReadAsync(hReadPipe, hReadEvent);
        Sleep(MAIN_LOOP_INTERVAL_MS);
    }

    CloseHandle(hWriteEvent);
    CloseHandle(hReadEvent);
    CloseHandle(hWritePipe);
    CloseHandle(hReadPipe);
    return 0;
}    

Pipe0 每 3 秒写入一次数据,同时在主循环中以更短的时间间隔尝试从 Pipe1 读取数据,且写入和读取操作分别使用独立的事件对象。

CreateEvent

创建或打开一个命名或未命名的事件对象,事件对象是一种同步对象,用于线程间或进程间的同步,可用来通知其他线程某个操作已经完成。

常用于异步操作中,当异步操作完成时,将事件对象设置为有信号状态,通知其他线程操作已完成。

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,
  BOOL                  bManualReset,
  BOOL                  bInitialState,
  LPCTSTR               lpName
);

lpEventAttributes:指向 SECURITY_ATTRIBUTES 结构体的指针,该结构体决定返回的句柄是否可被子进程继承。若为 NULL,则句柄不能被继承。
bManualReset:指定事件对象的复位方式。若为 TRUE,则为手动复位事件,需要调用 ResetEvent 函数将其设置为无信号状态;若为 FALSE,则为自动复位事件,当一个等待线程被释放后,系统会自动将其设置为无信号状态。
bInitialState:指定事件对象的初始状态。若为 TRUE,则初始状态为有信号状态;若为 FALSE,则初始状态为无信号状态。
lpName:指定事件对象的名称,若为 NULL,则创建一个未命名的事件对象。
返回值
若函数成功,返回事件对象的句柄;若失败,返回 NULL,可通过 GetLastError 函数获取错误代码。

WaitForSingleObject

等待指定的对象(如事件、互斥体、信号量等)变为有信号状态,或者等待指定的时间间隔过去。
在异步操作中,用于等待异步操作完成的通知(即事件对象变为有信号状态)。

DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD  dwMilliseconds
);

参数
hHandle:要等待的对象的句柄。
dwMilliseconds:等待的时间间隔,以毫秒为单位。若为 INFINITE,则表示无限期等待,直到对象变为有信号状态。

返回值
返回一个指示等待结果的 DWORD 值,常见的返回值有:
WAIT_OBJECT_0:表示等待的对象变为有信号状态。
WAIT_TIMEOUT:表示等待的时间间隔已过,对象仍未变为有信号状态。

ResetEvent

BOOL ResetEvent(
  HANDLE hEvent
);

功能
将指定的事件对象设置为无信号状态。
参数
hEvent:要设置的事件对象的句柄。
返回值
若函数成功,返回 TRUE;若失败,返回 FALSE,可通过 GetLastError 函数获取错误代码。
使用场景
对于手动复位事件,在处理完事件通知后,需要调用该函数将事件对象重置为无信号状态,以便下次使用。

GetOverlappedResult

BOOL GetOverlappedResult(
  HANDLE       hFile,
  LPOVERLAPPED lpOverlapped,
  LPDWORD      lpNumberOfBytesTransferred,
  BOOL         bWait
);

功能
获取指定的异步(重叠)操作的结果,包括操作是否成功以及传输的字节数。
参数
hFile:进行异步操作的对象的句柄,如文件、管道等。
lpOverlapped:指向 OVERLAPPED 结构体的指针,该结构体包含了异步操作的相关信息。
lpNumberOfBytesTransferred:指向 DWORD 变量的指针,用于接收异步操作传输的字节数。
bWait:指定函数是否等待异步操作完成。若为 TRUE,则函数会等待操作完成;若为 FALSE,则若操作尚未完成,函数会立即返回 FALSE。
返回值
若函数成功,返回 TRUE;若失败,返回 FALSE,可通过 GetLastError 函数获取错误代码。
使用场景
在异步操作中,当事件对象变为有信号状态,表示异步操作可能已经完成,此时可调用该函数获取操作的具体结果。

相关文章:

  • 阿里云Tair KVCache:打造以缓存为中心的大模型Token超级工厂
  • NX二次开发刻字功能——布尔运算
  • Spring项目中使用EasyExcel实现Excel 多 Sheet 导入导出功能(完整版)
  • 泛目录和泛站有什么不同?什么是无极泛目录?
  • IP属地和发作品的地址不一样吗
  • 生成AI基本原理简介
  • 深入理解 Linux 内核中的 GPU 子系统:从 DRM 到 NXP 驱动架构全解读
  • Java构造函数与普通函数
  • 洛谷P1706 全排列题解
  • 【redis】持久化之RDB与AOF
  • 前后前缀
  • Windows下在IntelliJ IDEA 使用 Git 拉取、提交脚本出现换行符问题
  • 比较4点结构和4次函数
  • C++编程语言:抽象机制:一个矩阵的设计(Bjarne Stroustrup)
  • 探索:如何构建一个自我的AI辅助的开发环境?
  • DeepSeek API集成开发指南——Flask示例实践
  • Python实现将字典中键相同的值合并
  • python学习笔记(1)
  • 二层综合实验
  • Pycharm运行时报“Empty suite”,可能是忽略了这个问题
  • 白鲨抢下世界杯首张入场券,透过ACL看CFPL的成色
  • 以色列“全面接管”加沙“雷声大雨点小”:援助政策引内讧,美欧失去耐心
  • 西域都护府博物馆今日在新疆轮台县开馆
  • 水果预包装带来的环境成本谁来分担?
  • “80后”北大硕士罗婕履新甘肃宁县县委常委、组织部部长
  • 视频丨为救心梗同学缺席职教高考的小伙姜昭鹏完成补考