深入理解USB硬件ID读取实践

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:USB读取器工具能够帮助获取连接到计算机的USB设备的唯一硬件标识符,包括制造商、产品ID和设备版本等信息。在编程中,特别是在设备驱动开发或自动化测试领域,读取并解析USB硬件ID是关键技能。本实践教程将指导如何使用Visual Studio 2010,结合Windows API和MFC库,通过注册设备通知、枚举设备、遍历设备、获取设备属性、解析硬件ID以及显示或存储信息等步骤,完成USB硬件ID的读取。该项目还适用于学习Windows编程和设备驱动开发,加深对系统底层交互的理解,并解决设备识别、驱动程序安装等问题。 USB读取器,读取USB硬件ID

1. USB硬件标识符读取介绍

在现代计算环境中,USB设备的普及使其成为连接各种外围设备的首选接口。硬件标识符是识别和区分不同USB设备的关键信息,其包括设备ID、供应商ID、序列号等。理解这些标识符的重要性在于,它们可以帮助开发者实现设备的精确控制和管理。读取这些信息不仅对于驱动开发至关重要,而且对于普通应用程序来说,也是实现复杂任务如设备自动安装、更新、故障排除等的基础。在本章中,我们将探究USB硬件标识符的读取方法,并讨论其在不同场景下的应用,为后续章节的深入分析打下坚实的基础。

2. Windows设备枚举机制

2.1 设备枚举基本原理

2.1.1 Windows设备驱动模型概述

Windows设备驱动模型(Windows Driver Model,WDM)是微软为了统一不同设备驱动编写方式而设计的一个模型。它定义了一套标准的编程接口和一组驱动程序必须实现的功能,使得不同的硬件设备可以被操作系统以一致的方式管理。WDM模型包括总线驱动、功能驱动和过滤驱动等类型,它们各自承担不同的角色。

在设备枚举过程中,WDM允许操作系统动态地加载和卸载设备驱动,这为即插即用设备提供了基础支持。当一个设备被添加到系统中时,系统会自动检测并识别它,然后加载相应的驱动程序来管理该设备。

2.1.2 设备枚举过程中的关键概念

设备枚举涉及多个关键步骤,包括检测设备、获取设备信息、加载驱动、设备启动等。其中,设备识别号(Device Instance ID)是一个关键概念,它唯一标识系统中的设备实例。此外,设备接口(Device Interface)用于向应用程序提供一个稳定的、与硬件无关的方式来识别和使用设备。

枚举过程的一个核心组件是设备堆栈,这是一个由多个驱动程序组成的逻辑链,其中每个驱动程序负责设备的不同方面。操作系统通过这个堆栈对设备进行管理和通信。

2.2 设备枚举中的事件驱动

2.2.1 设备插入和移除的通知机制

Windows操作系统使用设备通知机制来管理设备的插入和移除。当一个新设备被添加到系统中时,系统会通过特定的消息或回调函数通知相关的软件组件。这一过程是通过设备通知消息实现的,它允许应用程序、服务或驱动程序在设备状态发生变化时做出响应。

2.2.2 设备状态变化的处理流程

设备状态变化时,系统首先创建一个通知消息,并将其发送给所有注册了设备变化通知的应用程序或服务。这些应用程序可以注册以接收特定类型的设备通知,如设备插入、移除、设备可用性变化等。

当接收到通知后,应用程序或服务可以通过调用相应的Win32 API函数来查询设备的状态,获取详细信息,并据此更新其内部状态或执行必要的操作。例如,当一个USB存储设备被插入时,文件系统可能会启动挂载该设备的操作。

以下是使用Win32 API RegisterDeviceNotification 注册设备通知的示例代码:

#include <windows.h>

DWORD WINAPI RegisterNotificationFunction(LPVOID lpParam) {
    DEV_BROADCAST_HANDLE dbh;
    ZeroMemory(&dbh, sizeof(dbh));
    dbh.dbch_size = sizeof(dbh);
    dbh.dbch_devicetype = DBT_DEVTYP_HANDLE;

    // 注册设备通知句柄
    HDEVNOTIFY hNotify = RegisterDeviceNotification(
        (HWND)lpParam,  // 接收通知的窗口句柄
        &dbh,            // 指向通知结构的指针
        DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES
    );

    if (hNotify == NULL) {
        // 错误处理逻辑
    }

    // 等待通知或执行其他操作...

    // 取消注册通知句柄
    UnregisterDeviceNotification(hNotify);

    return 0;
}

上述代码展示了如何创建一个处理函数 RegisterNotificationFunction ,该函数注册设备通知,并在接收到通知后等待进一步的操作。需要注意的是,函数中的错误处理逻辑非常重要,它能够帮助开发者捕获并处理可能发生的错误情况。

在本章节中,我们介绍了Windows设备枚举机制的基础原理和事件驱动的概念。通过对设备驱动模型的理解,以及设备通知机制的分析,我们可以进一步深入探讨如何使用Visual Studio和WinAPI来实现USB设备的识别和管理。接下来的章节将更详细地探讨这些实现的细节和技巧。

3. 使用VS2010和WinAPI实现

3.1 开发环境搭建与配置

3.1.1 安装Visual Studio 2010

对于任何Windows平台下的开发工作,Visual Studio是不可或缺的集成开发环境(IDE)。在本节中,我们将介绍如何安装Visual Studio 2010以及配置开发环境。

  1. 下载安装包 :首先,需要从Microsoft官方网站获取Visual Studio 2010的安装包,或者使用安装介质(如DVD)启动安装程序。
  2. 启动安装向导 :运行安装程序后,会自动启动安装向导。根据向导的提示完成安装的初步设置。
  3. 选择组件 :在组件选择步骤中,选择适合开发C++应用程序和Windows应用程序的组件,如Visual C++开发环境、调试工具等。
  4. 配置环境 :根据个人需要配置开发环境的选项,包括是否安装额外的工具和语言包。
  5. 完成安装 :完成上述步骤后,点击“安装”按钮开始安装过程,并等待安装完成。

在安装过程中,确保你的计算机满足Visual Studio 2010的最小系统要求,以避免安装失败或运行不畅的问题。

3.1.2 配置Windows SDK和开发工具链

安装完Visual Studio 2010之后,下一个重要步骤是配置Windows SDK和开发工具链,以便进行WinAPI的开发。

  1. 下载Windows SDK :访问Microsoft官方网站下载最新版本的Windows SDK,它包含了开发Windows应用程序所需的头文件、库文件和工具。
  2. 安装SDK :运行下载的SDK安装程序,选择与Visual Studio 2010兼容的版本进行安装。根据安装向导进行操作,确保安装路径指向Visual Studio的安装目录。
  3. 集成到Visual Studio :安装完毕后,打开Visual Studio 2010,选择“工具”菜单中的“选项”对话框,导航到“项目和解决方案” -> “VC++目录”,在“包含文件”和“库文件”中添加Windows SDK的相应路径。
  4. 配置工具链 :在“配置属性” -> “常规”设置中,确保目标平台和目标架构与你的开发环境相匹配。
  5. 验证安装 :创建一个新的WinAPI项目,检查是否能够正常编译和运行。如果在编译过程中出现找不到库文件或头文件的错误,返回上一步检查路径配置是否正确。

完成这些步骤后,你的开发环境就配置完毕,可以开始基于WinAPI的USB硬件标识符读取项目开发了。

3.2 WinAPI中设备管理函数的运用

3.2.1 DeviceIoControl函数概述

DeviceIoControl是Windows API中的一个核心函数,它允许应用程序与设备驱动程序进行通信。通过发送特定的控制代码,应用程序可以查询或控制设备的行为。

函数原型
BOOL DeviceIoControl(
  HANDLE       hDevice,             // 设备句柄
  DWORD        dwIoControlCode,     // 控制代码
  LPVOID       lpInBuffer,          // 输入缓冲区指针
  DWORD        nInBufferSize,       // 输入缓冲区大小
  LPVOID       lpOutBuffer,         // 输出缓冲区指针
  DWORD        nOutBufferSize,      // 输出缓冲区大小
  LPDWORD      lpBytesReturned,     // 实际传输字节数
  LPOVERLAPPED lpOverlapped        // 重叠操作结构指针
);
参数说明
  • hDevice :已打开的设备句柄,必须具有 GENERIC_READ GENERIC_WRITE 访问权限。
  • dwIoControlCode :设备驱动程序定义的控制代码,用于指示要执行的操作。
  • lpInBuffer :指向输入缓冲区的指针。
  • nInBufferSize :输入缓冲区的大小。
  • lpOutBuffer :指向输出缓冲区的指针。
  • nOutBufferSize :输出缓冲区的大小。
  • lpBytesReturned :指向一个 DWORD 的指针,用来接收操作实际传输到输出缓冲区的字节数。
  • lpOverlapped :指向 OVERLAPPED 结构的指针,用于执行异步操作。
使用示例
// 示例:查询设备的硬件ID
HANDLE hDevice = CreateFile(L"\\\\.\\DeviceName", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
    // 错误处理逻辑
}

DWORD bytesReturned = 0;
BOOL result = DeviceIoControl(hDevice, MyCustomIoControlCode, NULL, 0, buffer, sizeof(buffer), &bytesReturned, NULL);
if (result == FALSE) {
    // 错误处理逻辑
}

// 关闭句柄
CloseHandle(hDevice);

3.2.2 SetupDiGetClassDevs函数详解

SetupDiGetClassDevs 函数用于获取一个设备信息集,该集合包含了指定类的设备。开发者可以使用这个函数来枚举系统中所有的USB设备。

函数原型
HDEVINFO SetupDiGetClassDevs(
  CONST GUID*   ClassGuid,          // 指向设备类GUID的指针
  PZZERO_NULLterminatedCHAR       Enumerator,        // 设备枚举器字符串
  HWND         hwndParent,          // 父窗口句柄
  DWORD        Flags                // 枚举标志
);
参数说明
  • ClassGuid :指向设备类GUID的指针。例如,如果你想要枚举所有的USB设备,那么可以使用 GUID_DEVINTERFACE_USB_DEVICE
  • Enumerator :指定设备枚举器字符串,如果为 NULL ,则枚举所有设备。
  • hwndParent :父窗口句柄,用于所有涉及用户交互的对话框。如果不需要用户交互,则设为 NULL
  • Flags :枚举标志,可以是 DIGCF_DEVICEINTERFACE DIGCF_PRESENT 等,用于控制枚举的范围和细节。
使用示例
// 枚举所有的USB设备
GUID usbDeviceGuid = {0x88BAE032, 0x5A81, 0x49f1, {0xbc, 0x5e, 0xc8, 0xdd, 0x84, 0x4a, 0xd6, 0x7d}};
HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&usbDeviceGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (deviceInfoSet == INVALID_HANDLE_VALUE) {
    // 错误处理逻辑
}

// 遍历枚举到的设备
// ...

// 清理资源
SetupDiDestroyDeviceInfoList(deviceInfoSet);

通过上面的代码段,我们获得了系统中所有当前连接的USB设备的信息集。在这个信息集上,我们可以执行进一步的操作,比如查询设备的详细信息等。

4. 注册设备通知步骤

在本章节中,我们将深入探讨Windows操作系统中设备通知的概念及其注册过程。设备通知机制为应用程序提供了监听系统中设备状态变化的能力。我们将从设备通知的基本概念讲起,进而详细说明注册设备通知的具体步骤。

4.1 设备通知的基本概念

设备通知机制是Windows系统提供的一种方式,允许应用程序接收设备事件,如设备插入或移除。这种机制基于事件驱动模型,大大增强了应用程序与硬件设备交互的灵活性。

4.1.1 设备通知机制的工作原理

设备通知的核心在于DevBroadcastDeviceInterface结构体,该结构体定义了设备接口的格式,使得系统能够识别并发送相关事件。应用程序通过DevBROADCAST_DEVICEINTERFACE结构体中包含的信息来注册对特定设备类型或特定设备的通知。

4.1.2 设备通知的数据结构

DevBroadcastDeviceInterface结构体包含多个字段,重要字段包括设备的GUID、设备的类型、设备的名称等。这个结构体是构建通知消息的基础,系统会根据该结构体中的信息,向已注册的应用程序发送相应的通知消息。

4.2 注册设备通知的过程

注册设备通知是让应用程序能够接收设备事件的过程。Windows通过RegisterDeviceNotification API来实现这一功能,接下来,我们将具体介绍如何使用这一API。

4.2.1 使用RegisterDeviceNotification API

RegisterDeviceNotification API是注册设备通知的主要方式。应用程序使用该API函数时,需要提供一个NotificationFilter结构体作为参数,指明哪些类型的设备事件感兴趣。这个过程涉及到多个步骤,比如创建窗口消息处理函数、准备设备通知结构体等。

下面是一个简单的示例代码,展示如何使用RegisterDeviceNotification API来注册设备通知:

#include <windows.h>
#include <dbt.h>   // 设备通知的结构体定义

// 消息处理函数,用于接收设备通知消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    if (uMsg == WM_DEVICECHANGE) {
        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
        switch (wParam) {
            case DBT_DEVICEARRIVAL:
                // 设备插入的处理逻辑
                break;
            case DBT_DEVICEREMOVECOMPLETE:
                // 设备移除的处理逻辑
                break;
        }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int main() {
    HWND hNotifyWindow = CreateWindow("STATIC", "NotifyWindow", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL);
    WNDCLASS wc = {0};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = "MyNotifyWindowClass";
    RegisterClass(&wc);

    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
    ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    // 这里可以指定一个特定的设备接口GUID
    // NotificationFilter.dbcc_classguid = ...;

    HDEVNOTIFY hDeviceNotify = RegisterDeviceNotification(hNotifyWindow, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
    // ... 后续代码处理注册和消息循环
}

4.2.2 处理设备通知消息

当设备事件发生时,系统会发送WM_DEVICECHANGE消息到应用程序。应用程序需要在消息处理函数中判断消息类型,如设备插入DBT_DEVICEARRIVAL或设备移除DBT_DEVICEREMOVECOMPLETE。根据事件类型,应用程序可以执行相应的逻辑,比如更新用户界面或记录日志。

以上代码段仅展示了如何注册设备通知和处理设备插入和移除事件的基本逻辑。在实际应用中,您可能需要根据具体需求进行更加复杂的处理,例如过滤特定硬件ID的设备通知、优化事件处理逻辑等。

在下一章,我们将继续深入探讨如何使用Windows的SetupDi系列API来枚举和遍历设备。通过本章节的内容,我们希望您能够理解设备通知的概念,并掌握如何通过Windows API注册设备通知以及处理设备事件消息。

5. 枚举设备与遍历方法

5.1 枚举设备的核心API介绍

5.1.1 SetupDiEnumDeviceInterfaces函数解析

在Windows编程中,枚举系统中的设备接口是一个常见的任务。 SetupDiEnumDeviceInterfaces 函数是用于枚举系统中特定设备接口的一个关键API。它会返回与特定硬件ID或兼容ID相关联的设备接口列表。

在使用该函数前,开发者需要首先获取设备接口类GUID,然后创建一个设备接口详细信息的数据结构体。之后,通过调用 SetupDiEnumDeviceInterfaces ,可以遍历并获取所有的设备接口实例。

以下是 SetupDiEnumDeviceInterfaces 的基本使用示例代码:

BOOL EnumerateDeviceInterfaces(HDEVINFO DeviceInfoSet,
                               PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) {
    DWORD dwMemberIndex = 0;
    SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {0};

    while (SetupDiEnumDeviceInterfaces(DeviceInfoSet, 
                                       NULL, 
                                       &InterfaceClassGuid, 
                                       dwMemberIndex, 
                                       &deviceInterfaceData)) {
        // ... 处理每一个枚举到的设备接口
        dwMemberIndex++;
    }

    return TRUE;
}

在这段代码中, DeviceInfoSet 是通过 SetupDiGetClassDevs 函数获得的设备信息集。 DeviceInterfaceData 是一个指向 SP_DEVICE_INTERFACE_DATA 结构的指针,用于保存枚举出的设备接口数据。函数会遍历所有与 InterfaceClassGuid (设备接口类GUID)相关联的设备接口,并执行指定的操作。

5.1.2 SetupDiGetDeviceInterfaceDetail函数应用

在枚举到设备接口后,往往需要获取更多关于该接口的详细信息。 SetupDiGetDeviceInterfaceDetail 函数可以获取设备接口的详细信息,返回一个指向 SP_DEVICE_INTERFACE_DETAIL_DATA 结构的指针,该结构包含了设备接口的详细信息。

下面是一段使用 SetupDiGetDeviceInterfaceDetail 的示例代码:

BOOL GetDeviceInterfaceDetails(HDEVINFO DeviceInfoSet,
                               PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
                               PSP_DEVICE_INTERFACE_DETAIL_DATA *pDeviceInterfaceDetailData) {
    SP_DEVINFO_DATA DeviceInfoData = {0};
    DWORD RequiredSize = 0;
    BOOL Result = FALSE;

    if (SetupDiGetDeviceInterfaceDetail(DeviceInfoSet,
                                        DeviceInterfaceData,
                                        NULL,
                                        0,
                                        &RequiredSize,
                                        &DeviceInfoData)) {
        // 如果成功,RequiredSize变量将被设置为所需缓冲区的大小
    }

    if (RequiredSize > 0) {
        PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = (PVOID)LocalAlloc(LMEM_FIXED, RequiredSize);

        if (DeviceInterfaceDetailData != NULL) {
            DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
            Result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet,
                                                      DeviceInterfaceData,
                                                      DeviceInterfaceDetailData,
                                                      RequiredSize,
                                                      &RequiredSize,
                                                      &DeviceInfoData);
            // ... 处理获取到的设备接口详细信息

            LocalFree(DeviceInterfaceDetailData);
        }
    }

    return Result;
}

在这段代码中,首先会调用 SetupDiGetDeviceInterfaceDetail 函数一次,目的是为了获取所需的缓冲区大小。然后根据获取到的大小分配足够的内存,并再次调用该函数来填充实际的设备接口详细信息。最终,通过这些信息,开发者可以进一步了解设备接口的特定细节,如设备路径等。

5.2 枚举设备的高级技巧

5.2.1 如何处理枚举过程中出现的异常

在使用设备枚举API时,可能遇到各种异常,如访问权限被拒绝、设备不存在或系统资源不足等问题。处理这些异常需要编写健壮的错误处理逻辑。

例如,当API调用失败时,可以通过 GetLastError 函数获取错误代码,进而判断错误的原因。下面是一段用于处理错误的示例代码:

DWORD error = GetLastError();
if (error != ERROR_INSUFFICIENT_BUFFER) {
    // 如果返回的错误码不是ERROR_INSUFFICIENT_BUFFER,那么可能是其他类型的错误
    // 此时应根据错误码进行进一步的错误处理逻辑
}

5.2.2 提高设备枚举效率的方法

提高设备枚举的效率可以考虑以下几个方面:

  1. 减少不必要的枚举操作 :只枚举所需设备类别的设备,而不是枚举所有设备。
  2. 并行处理 :在可能的情况下使用多线程并行执行枚举操作。
  3. 缓存机制 :对于不变的设备信息,可以缓存到本地磁盘或内存中,避免重复枚举。
  4. 使用设备监视器 :利用 RegisterDeviceNotification API 注册设备通知,当设备状态变化时,只枚举变化的设备,而不是每次都进行全量枚举。

例如,使用设备监视器可以这样实现:

DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = {0};
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;

HDEVNOTIFY hDevNotify = RegisterDeviceNotification(
    hRecipient,
    &NotificationFilter,
    DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);

通过上述方法,可以显著提升设备枚举的效率,减少系统资源的消耗,提升用户体验。

6. 获取设备硬件ID过程

6.1 硬件ID的结构与意义

硬件标识符(Hardware ID)是每个物理设备在系统中独特的识别字符串,它包含了设备的详细信息,如制造商、设备类型和设备的版本号等。硬件ID是设备枚举过程中的关键数据之一,开发者可以通过这些ID来识别和区分系统中的硬件设备。

6.1.1 硬件ID的组成和识别要点

硬件ID通常由多个部分组成,常见的包括设备的供应商ID(VID)、产品ID(PID)以及一个或多个序列号和版本信息。例如,一个典型的硬件ID可能是这样的格式: PCI\VEN_10EC&DEV_8168&SUBSYS_816810EC&REV_11

  • 供应商ID(VEN) :通常为16进制,由硬件制造商注册获得,用于唯一标识设备的制造商。
  • 产品ID(DEV) :由供应商为特定设备型号分配的唯一标识符。
  • 子系统ID(SUBSYS) :表示特定的子系统版本或特定的供应商组合。
  • 修订号(REV) :表示该设备硬件版本的修订。

开发者在识别硬件ID时应注意,不同的设备可能使用不同的标识符前缀。例如,PCI设备使用 PCI\ 前缀,而USB设备则使用 USB\ 前缀。

6.1.2 硬件ID与设备识别的关联

硬件ID对于设备识别至关重要,开发者可以利用这些ID来确定设备类型以及设备的特定配置。当设备插入系统时,Windows操作系统会将这些ID与已知设备的ID列表(通常存储在INF文件中)进行匹配,从而完成设备的安装和配置。

硬件ID通常被用于设备安装程序、驱动程序安装和设备管理。在开发中,理解硬件ID对于编写兼容多个硬件设备的应用程序和驱动程序至关重要。

6.2 硬件ID的读取实现

6.2.1 调用SetupDiGetDeviceRegistryProperty获取硬件ID

在Windows编程中,可以通过 SetupDiGetDeviceRegistryProperty 函数来查询设备的硬件ID。此函数是SetupAPI库中的一部分,专门用于访问设备的注册表属性,包括硬件ID。

下面是一个使用 SetupDiGetDeviceRegistryProperty 函数获取设备硬件ID的代码示例:

#include <windows.h>
#include <SetupAPI.h>
#include <stdio.h>

GUID deviceGuid;
SP_DEVINFO_DATA deviceInfoData;

// 初始化设备信息数据结构
ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA));
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

// 初始化设备GUID
GUIDFromString(L"{4D36E978-E325-11CE-BFC1-08002BE10318}", &deviceGuid);

// 获取设备信息集句柄
HDEVINFO deviceInfoSet = SetupDiGetClassDevs(&deviceGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);

if (deviceInfoSet != INVALID_HANDLE_VALUE) {
    // 枚举设备接口
    for (DWORD i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData); i++) {
        DWORD hardwareIDSize = 0;
        // 获取所需的缓冲区大小
        SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_HARDWAREID, NULL, NULL, 0, &hardwareIDSize);
        if (hardwareIDSize) {
            wchar_t* hardwareIDBuffer = new wchar_t[hardwareIDSize/sizeof(wchar_t)];
            // 实际获取硬件ID
            if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_HARDWAREID, NULL, (PBYTE)hardwareIDBuffer, hardwareIDSize, NULL)) {
                // 硬件ID现在位于hardwareIDBuffer中
                wprintf(L"Hardware ID: %ls\n", hardwareIDBuffer);
            }
            delete[] hardwareIDBuffer;
        }
    }
    // 清理设备信息集句柄
    SetupDiDestroyDeviceInfoList(deviceInfoSet);
}

上述代码首先通过 SetupDiGetClassDevs 函数获取了一个设备信息集句柄,然后枚举其中的所有设备。对每个设备,代码调用 SetupDiGetDeviceRegistryProperty 两次:第一次调用来查询所需的缓冲区大小,第二次调用来实际获取硬件ID字符串。

6.2.2 硬件ID的存储与管理

获取到硬件ID后,通常需要将其存储和管理以便后续使用。可以将硬件ID存储在文本文件、XML、数据库或者内存中。在存储时,需要考虑到硬件ID的唯一性、更新和查询效率等因素。

当处理多个硬件ID时,可以使用集合数据结构(如std::set或者std::unordered_set)来存储这些ID,以便进行快速查找和避免重复。在内存中,可以直接使用这些数据结构;而在持久化存储时,则需要根据存储媒介和数据大小选择合适的方法和格式。

在实际应用中,硬件ID的存储和管理策略将直接影响到整个系统的性能和用户体验。因此,开发者需要根据具体的应用场景和需求进行权衡选择。

7. 硬件ID字符串解析技术

7.1 字符串解析的基本方法

7.1.1 字符串处理函数的使用

在处理硬件ID字符串时,我们需要掌握一系列的字符串处理函数来提取和分析字符串中的有用信息。Windows API提供了丰富的字符串处理函数,如 Strlen() , Strcmp() , Strcat() 等。例如,使用 Strcmp() 函数来比较两个字符串是否相等,这是区分不同硬件ID的基础。

#include <string.h>
#include <stdio.h>

int main() {
    char id1[] = "USB\\VID_04D8&PID_003C&REV_0200";
    char id2[] = "USB\\VID_04D8&PID_003C";

    if (Strcmp(id1, id2) == 0) {
        printf("The beginning of the IDs match.\n");
    } else {
        printf("The IDs are different.\n");
    }

    return 0;
}

7.1.2 正则表达式在字符串解析中的应用

正则表达式是处理和分析字符串的强大工具,它可以帮助开发者在硬件ID字符串中查找特定的模式或数据。例如,使用正则表达式来提取VID(Vendor ID)和PID(Product ID)。

#include <regex.h>

int main() {
    regex_t regex;
    int reti;
    char msgbuf[100];
    regmatch_t matches[3];
    const char *string = "USB\\VID_04D8&PID_003C&REV_0200";
    char pattern[] = "^USB\\\\VID_([0-9A-Fa-f]+).*";

    // Compile regular expression
    reti = regcomp(&regex, pattern, REG_EXTENDED);
    if (reti) {
        fprintf(stderr, "Could not compile regex\n");
        exit(1);
    }

    // Execute regular expression
    reti = regexec(&regex, string, 3, matches, 0);
    if (!reti) {
        char vendorID[50];
        // Retrieve the actual Vendor ID
        strncpy(vendorID, &string[matches[1].rm_so], matches[1].rm_eo - matches[1].rm_so);
        vendorID[matches[1].rm_eo - matches[1].rm_so] = 0;
        printf("Vendor ID: %s\n", vendorID);
    } else {
        regerror(reti, &regex, msgbuf, sizeof(msgbuf));
        fprintf(stderr, "Regex match failed: %s\n", msgbuf);
    }

    // Free the memory allocated to the pattern buffer
    regfree(&regex);

    return 0;
}

7.2 硬件ID解析的深度应用

7.2.1 硬件ID的分类和匹配技术

通过字符串解析技术,我们可以将硬件ID分类,并匹配设备。可以使用解析得到的VID和PID来确定设备的类型,并执行相应的操作。

// Pseudo code for matching VID and PID
char hardwareID[] = "USB\\VID_04D8&PID_003C&REV_0200";
char *vendorID, *productID;

// Parse hardwareID and retrieve VID and PID
vendorID = extractVendorID(hardwareID);
productID = extractProductID(hardwareID);

// CompareVID and PID with known values
if ((strcmp(vendorID, "04D8") == 0) && (strcmp(productID, "003C") == 0)) {
    printf("Device is recognized and can be processed accordingly.\n");
} else {
    printf("Device is not recognized.\n");
}

7.2.2 解析结果的应用实例

在实际应用中,根据解析结果可以对USB设备执行特定的操作。例如,可以开发一个程序,根据VID和PID来安装或更新驱动程序。

// Pseudo code for installing or updating driver based on parsed VID and PID
if (isKnownDevice(vendorID, productID)) {
    printf("Installing driver for known device...\n");
    installDriver(vendorID, productID);
} else {
    printf("Unknown device, driver installation not initiated.\n");
}

这种方法可以广泛应用于IT管理工具中,用于自动化设备驱动的安装和更新过程,极大地提高了效率。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:USB读取器工具能够帮助获取连接到计算机的USB设备的唯一硬件标识符,包括制造商、产品ID和设备版本等信息。在编程中,特别是在设备驱动开发或自动化测试领域,读取并解析USB硬件ID是关键技能。本实践教程将指导如何使用Visual Studio 2010,结合Windows API和MFC库,通过注册设备通知、枚举设备、遍历设备、获取设备属性、解析硬件ID以及显示或存储信息等步骤,完成USB硬件ID的读取。该项目还适用于学习Windows编程和设备驱动开发,加深对系统底层交互的理解,并解决设备识别、驱动程序安装等问题。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值