一、目的:
    当計算機有一個或者多個 U 盤插入時,系統識別后,該應用可以自動獲取 U 盤的相關信息,如生産廠商信息,產品名,版本號, VID&PID ,產品序列號等等一些 U 盤的相關信息。
二、實現思路:
    1、通過獲得U盤盤符獲得設備句柄
    首先必須檢測當前系統連接的 U 盤設備,在這一方面最便捷的方法是掃描當前系統的各個驅動器,判斷當前系統所連設備屬性是否為 DRIVE_REMOVABLE ,如果是,表明設備是可移動設備,儅我們排除軟盤后,剩下的就是 U 盤設備了。
// 得到 U 盘盘 符;
LPTSTR lpDrives = new TCHAR[MAX_PATH];
DWORD dwLen = ::GetLogicalDriveStrings(MAX_PATH, lpDrives);
CString sDrives[26] = {""};
for(DWORD nIndex = 0; nIndex < dwLen / 4; nIndex++)
{
    if(::GetDriveType(lpDrives + nIndex * 4) == DRIVE_REMOVABLE)
               sDrives[nIndex] += (CString)(lpDrives + nIndex * 4);
               if(sDrives[nIndex]!= ” A:// ” && sDrives[nIndex]!= ” B:// ” )
{
   // …具體操作 代碼 ;
}
}
delete lpDrives;
   
    獲得上面這段代碼,獲得了具體的形如“ H:/ ”的設備盤符,並且將這一組盤符存儲在 sDevice[26] 這樣一個字符串數組中。然後通過具體操作獲得的盤符經過一定的處理將其轉化為形如 ”?//H:” 的設備路徑,然後再通過 CreateFile 獲得這個設備的句柄:
    HANDLE hDeviceHandle = CreateFile(PATH,
               GENERIC_READ | GENERIC_WRITE,
               FILE_SHARE_READ | FILE_SHARE_WRITE,
               NULL,
               OPEN_EXISTING,
               0,
               NULL);
    注意,這裡的第三項參數必須是 FILE_SHARE_READ | FILE_SHARE_WRITE, 因爲 U 盤設備是共享設備,第五個參數必須是 OPEN_EXISTING ,打開現有設備。 hDeviceHandle 就是我們所需要的 U 盤句柄了。
     獲得這個句柄之後我們將用到一個比較關鍵的函數 DeviceIoControl ,儅我們對系統的各種設備進行操作時,這個函數是經常要用到的。這個函數中控制碼的種類很多,這裡我們主要討論利用 IOCTL_STORAGE_QUERY_PROPERTY 這個控制碼,获取设备属性信息,得到系统中所安装的各种固定的和可移动的硬盘、优盘和 CD/DVD-ROM/R/W 的接口类型、序列号、产品 ID 等信息。
    這裡給出這樣一個函數:
// 取 设备 属性信息
// ( in ) hDevice -- 设备 句柄
// ( out ) pDevDesc -- 输 出的 设备 描述和属性信息 缓 冲区指 针 ( 包含 连 接在一起的两部分 )
BOOL GetDriveProperty(HANDLE hDevice, PSTORAGE_DEVICE_DESCRIPTOR pDevDesc)
{
    STORAGE_PROPERTY_QUERY Query;    // 查询输 入参数
    DWORD dwOutBytes;                // IOCTL 输 出数据 长 度
    BOOL bResult;                    // IOCTL 返回 值
 
    // 指定 查询 方式
    Query.PropertyId = StorageDeviceProperty;
    Query.QueryType = PropertyStandardQuery;
 
    // 用 IOCTL_STORAGE_QUERY_PROPERTY 取 设备 属性信息
    bResult = ::DeviceIoControl(hDevice, // 设备 句柄
        IOCTL_STORAGE_QUERY_PROPERTY,    // 取 设备 属性信息
        &Query, sizeof(STORAGE_PROPERTY_QUERY),    // 输 入数据 缓 冲区
        pDevDesc, pDevDesc->Size,        // 输 出数据 缓 冲区
        &dwOutBytes,                     // 输 出数据 长 度
        (LPOVERLAPPED)NULL);             // 用同步 I/O   
 
    return bResult;
}
注意,使用這個函數需要引入 winioctl.h 。在這個頭文件中聲明了 STORAGE_DEVICE_DESCRIPTOR 這樣一個結構体:
// 查询 属性 输 出的数据 结 构
typedef struct _STORAGE_DEVICE_DESCRIPTOR {
    ULONG Version;                    // 版本
    ULONG Size;                       // 结 构大小
    UCHAR DeviceType;                 // 设备类 型
    UCHAR DeviceTypeModifier;         // SCSI-2 额 外的 设备类 型
    BOOLEAN RemovableMedia;           // 是否可移 动
    BOOLEAN CommandQueueing;          // 是否支持命令 队 列
    ULONG VendorIdOffset;             // 厂家 设 定 值 的偏移
    ULONG ProductIdOffset;            // 产 品 ID 的偏移
    ULONG ProductRevisionOffset;      // 产 品 版本的偏移
    ULONG SerialNumberOffset;         // 序列号的偏移
    STORAGE_BUS_TYPE BusType;         // 总线类 型
    ULONG RawPropertiesLength;        // 额 外的属性数据 长 度
    UCHAR RawDeviceProperties[1];     // 额 外的属性数据 ( 仅 定 义 了象征性的 1 个字 节 )
} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
調用以上列出的 GetDriveProperty 時要注意聲明一個 STORAGE_DEVICE_DESCRIPTOR 變量,並且初始化,可以這樣初始化:
PSTORAGE_DEVICE_DESCRIPTOR DeviceDesc;
DeviceDesc=(PSTORAGE_DEVICE_DESCRIPTOR)new BYTE[sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1];
DeviceDesc->Size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512 - 1;
    
通過這樣我們獲得一個設備對應的屬性數據結構,這樣,我們就可以獲取相關的信息了。
(其中我在 Vista+VS2005+WinSDK 下只能獲得 ProductIdOffset , ProductIdOffset , ProductRevisionOffset 等信息,而 SerialNumberOffset 等信息返回字符串為空串,而且這裡也無法知道 U 盤的 VID&PID ,所以以上這兩個無法獲得的信息我們可以通過其它方式獲得,具體見方法 2 。)
後來我又在 win200+VC6+DDK 的環境下進行同樣的實驗,發現利用這種方法也同樣是無法獲得 SerialNumberOffset ,利用這種方法只能獲得硬盤、光驅等硬件設備的 SerialNumberOffset ,而對於 usb 設備,包括通過 usb 連接到電腦上的移動硬盤,也無法通過此种方法獲得。
2、另一種方法是通過設備的GUID號直接找到U盤設備。
    實現過程中我們將用到一些 WinSDK 的函數, SetupDiGetClassDevs , SetupDiEnumDeviceInterfaces , SetupDiGetInterfaceDeviceDetail 。具體用法,請參照相關的文檔,這裡不作詳細介紹
另外, MicroSoft 定義了一些設備類的 GUID 。
如:
DEFINE_GUID(DiskClassGuid, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
DEFINE_GUID(CdRomClassGuid, 0x53f56308L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
DEFINE_GUID(TapeClassGuid, 0x53f5630bL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
DEFINE_GUID(FloppyClassGuid, 0x53f56311L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
我們同樣可以找到對於 USB 設備的 GUID 號
DEFINE_GUID(UsbClassGuid, 0xa5dcbf10L, 0x6530, 0x11d2, 0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed )
這樣我們可以通過以下這樣一個函數來實現:
// SetupDiGetInterfaceDeviceDetail 所需要的 输 出 长 度,定 义 足 够 大
#define INTERFACE_DETAIL_SIZE    (1024)
 
// 根据 GUID 获 得 设备 路径
// lpGuid: GUID 指 针
// pszDevicePath: 设备 路径指 针 的指 针
// 返回 : 成功得到的 设备 路径个数,可能不止 1 个
int GetDevicePath(LPGUID lpGuid, LPTSTR* pszDevicePath)
{
    HDEVINFO hDevInfoSet;    // 设备 信息集句柄 ;
    SP_DEVICE_INTERFACE_DATA ifdata;
    PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;
    int nCount;
    BOOL bResult;
 
    // 取得一个 该 GUID 相关的 设备 信息集句柄
    hDevInfoSet = ::SetupDiGetClassDevs(UsbClassGuid,     // class GUID
        NULL,                    // 无关 键 字
        NULL,                    // 不指定父窗口句柄
        DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);    // 目前存在的 设备
 
    // 失 败 ...
    if (hDevInfoSet == INVALID_HANDLE_VALUE)
    {
        return 0;
    }
 
    // 申 请设备 接口数据空 间
    pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT, INTERFACE_DETAIL_SIZE);
 
    pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
 
    nCount = 0;
    bResult = TRUE;
 
    // 设备 序号 =0,1,2... 逐一 测试设备 接口,到失 败为 止
    while (bResult)
    {
        ifdata.cbSize = sizeof(ifdata);
 
        // 枚 举 符合 该 GUID 的 设备 接口
        bResult = ::SetupDiEnumDeviceInterfaces(
            hDevInfoSet,     // 设备 信息集句柄
            NULL,            // 不需 额 外的 设备 描述
            lpGuid,          // GUID
            (ULONG)nCount,   // 设备 信息集里的 设备 序号
            &ifdata);        // 设备 接口信息
 
        if (bResult)
        {
            // 取得 该设备 接口的 细节 ( 设备 路径 )
            bResult = SetupDiGetInterfaceDeviceDetail(
                hDevInfoSet,    // 设备 信息集句柄
                &ifdata,        // 设备 接口信息
                pDetail,        // 设备 接口 细节 ( 设备 路径 )
                INTERFACE_DETAIL_SIZE,   // 输 出 缓 冲区大小
                NULL,           // 不需 计 算 输 出 缓 冲区大小 ( 直接用 设 定 值 )
                NULL);          // 不需 额 外的 设备 描述
            if (bResult)
            {
                // 复制 设备 路径到 输 出 缓 冲区
                ::strcpy(pszDevicePath[nCount], pDetail->DevicePath);
               // 调 整 计 数 值
                nCount++;
            }
        }
    }
    // 释 放 设备 接口数据空 间
    ::GlobalFree(pDetail);
    // 关 闭设备 信息集句柄
    ::SetupDiDestroyDeviceInfoList(hDevInfoSet);
    return nCount;
}
以上這個函數中除了要包含 winioctl.h 外,還要包含 initguid.h , setupapi.h ,以及連接 setupapi.lib 。
不一樣的是 ,以上這樣一個函數讓我們獲得了一個形如 "//?/usb#vid_0ef5&pid_2202#2004063008241001#{a5dcbf10-6530-11d2-901f-00c04fb951ed}" 的設備路徑,從以上這一串路徑我們不難找到,已經有 VID&PID, 產品序列號等信息。如果只是需要這兩個信息的話,我們可以通過提取這段字符串的内容,直接就達到獲得 VID&PID 和產品序列號的目的。
當然,這樣一個路徑,同樣也可以由方法 1 中的 CreateFile 打開獲得句柄。
    之後的做法與方法 1 中的一樣,通過 DeviceIoControl 函數獲得相關的信息。
3、通過HidD_GetAttributes获得其基本属性信息  
  HID 設備是微軟定義的標準人機接口範圍 。不用查找設備具體的 GUID ,直接使用 API   HidD_GetHidGuid(&guidHID) 即可得到 GUID 。有了 GUID 通過方法 2 獲得其設備句柄,通 过 HidD_GetAttributes 获 得其基本属性信息。當然,這樣獲得的基本屬性可能不全面,可以結合方法 1 、 2 來綜合獲取想要得到的信息。具體實現方法這裡不做詳細介紹。這個方法我沒有試過,但應該可以行的通。
通過以上三种方法基本上可以獲得我們所需的所有關於 U 盤的所有信息了。
 
4、通過所獲得的U盤盤符找到與其對應的相關信息
從以上的這幾種方法來看,由於每個方法都是通過對於系統的設備進行掃描然後得出 U 盤盤符、 PID/VID ,序列號 … 等。而且由於這些信息是通過多種方法獲得的,儅計算機連接多個 usb 設備時,那麽以上的這些信息就無法正確的對應起來,尤其是 U 盤盤符與其唯一標識 Serial Number 無法對應,這樣很不利于我們正確的選擇目標 usb 設備。下面我們來看如何通過 U 盤盤符找到對應的設備序列號。
   第一步,我們仍可以按照方法 1 种所提到的通過遍歷盤符屬性來找到所有連接到機器上的 U 盤盤符,然後 CreateFile 獲得設備句柄。
   第二步,在應用 DeviceIoControl 函數的時候,我們需要引入一個新的查詢方式 IOCTL_STORAGE_GET_DEVICE_NUMBER ,如
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
// 用 IOCTL_STORAGE_GET_DEVICE_NUMBER 取 设备 號
    bResult = ::DeviceIoControl(hDevice, // 设备 句柄
        IOCTL_STORAGE_GET_DEVICE_NUMBER,    // 取 设备 属性信息
        NULL, 0,                          // 输 入数据 缓 冲区
        sdn , sdn ->Size,        // 输 出数据 缓 冲区
        & dwBytesReturned ,                  // 输 出数据 地址
        (LPOVERLAPPED)NULL);             // 用同步 I/O 
    DeviceNumber = pDevDesc.DeviceNumber;
   這樣我們可以獲得該盤符對應的設備號,同樣我們可以通過 QueryDosDevice 來找到該盤符對應的 dos 設備名:
QueryDosDevice(szDevicePath,         // 設備路徑,如:“ F: ”
szDosDeviceName,     // 查詢返回的 dos 設備名
MAX_PATH);          
到現在爲止,我們通過盤符獲得了兩個信息: DeviceNumber , szDosDeviceName 。
第三步用下面這樣一個函數來找到相應的設備序列號:
DEVINST GetDrivesDevInstByDiskNumber (long DiskNumber, char *szDosDeviceName)
{
…// 函數的前半部分與方法 2 种提到的方法相同目的是爲了獲得一個 DivicePath ;
…// 注意這裡用到的 GUID 應該是 DiskClassGuid ;
HANDLE hDrive = CreateFile(pspdidd->DevicePath,
                                   0,
                                   FILE_SHARE_READ | FILE_SHARE_WRITE,
                                   NULL, OPEN_EXISTING, NULL, NULL);
if ( hDrive != INVALID_HANDLE_VALUE )
{
       STORAGE_DEVICE_NUMBER sdn;
       DWORD dwBytesReturned = 0;
       // 通過這樣一個句柄,用同樣的方法也可以得到一個設備號。
res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER,
                                                                             NULL, 0, &sdn, sizeof(sdn),
                                                                             &dwBytesReturned, NULL);
       if ( res )
 {
          // 這句是關鍵,通過這兩种方法獲得的 .DeviceNumber ,進行比較,以 DeviceNumber 作 // 爲橋梁,找到了對應的設備
if ( DiskNumber == (long)sdn.DeviceNumber )  
 {
          CloseHandle(hDrive);
          SetupDiDestroyDeviceInfoList(hDevInfo);
          return spdd.DevInst;           // 這裡是來返回一個 DeviceInstance 。        
   }
}
CloseHandle(hDrive);
}
Return 0;
}            
調用以上這個函數,我們獲得了一個 DEVINST ,這樣我們就可以通過 DDK 中 CM_Get_Device_ID 來獲得設備 ID :
char Buf[MAX_PATH];
CM_Get_Device_ID(DevInst,Buf,MAX_PATH,0);
其中的 Buf 中返回的就是 DeviceInstanceID ,這是一個形如“ USBSTOR/DISK&VEN________&PROD_FREEDIK-LWFORMAT&REV_2.23/2004063008241001 ”的字符串,我們可以看到字符串的最後一串數字就是我們想要得到的 SerialNumber 。
通過這樣一個方法,我們可以通過所獲得的 U 盤盤符找到與其對應的相關信息。因爲我們一旦獲得了 SerialNumber 這樣一個唯一的標識,我們就可以以它為橋梁,找到相關的所有信息了。
參考:
http://dev.csdn.net/develop/article/17/17209.shtm
http://www.arm8.com/cv/1/7/211.html
http://www.codeproject.com/system/RemoveDriveByLetter.asp

windows如何获得U盘的详细信息相关推荐

  1. 显示计算机名在桌面壁纸,Windows桌面壁纸自动显示计算机详细信息小工具–BgInfo...

    Windows桌面壁纸自动显示计算机详细信息小工具–BgInfo 这个东西是微软官方出的,他会读取系统信息,然后更改现在正在使用的桌面图片,写入图片后自动退出程序.所以说基本没有占用. 本站备份程序下 ...

  2. windows查看内存的频率等详细信息

    在Windows下如果想要查看内存的频率等详细信息,可以按下WIN键+R组合键,打开运行,输入cmd,回车,进入命令提示符窗口,在其中输入wmic memorychip.注意,wmic和memoryc ...

  3. 如何获得U盘的详细信息

    一.目的:     当計算機有一個或者多個 U 盤插入時,系統識別后,該應用可以自動獲取 U 盤的相關信息,如生産廠商信息,產品名,版本號, VID&PID ,產品序列號等等一些 U 盤的相關 ...

  4. QT windows 应用程序 exe ,设置详细信息并解决中文乱码问题

    原博主:https://blog.csdn.net/xiezhongyuan07/article/details/87691490 1.新创建一个.rc文件,随意命名,例如叫app.rc 并编辑 1 ...

  5. 如何显示Windows 10登录过程详细信息

    不知大家是否注意到 Windows 10 在用户登录过程中只显示"欢迎"二字,而不再像以前 Windows 那样把登录过程的详细信息给显示出来.对于管理员或高级用户来说,在不激活 ...

  6. 1,我们无法创建新的分区,也找不到现有的分区。有关详细信息,请参阅安装日志文件。2,Windows 检测到EFI系统分区格式为NTFS。将EFI系统分区格式化为FAT32,然后重新启动安装。

    使用U盘重装win10系统时的两个错误.(我的是新买的固态硬盘作为系统盘,所以才重装系统) 1,我们无法创建新的分区,也找不到现有的分区.有关详细信息,请参阅安装日志文件. 2,Windows 检测到 ...

  7. 查看Windows服务器的CPU详细信息

    查看Windows服务器的CPU详细信息 让我们来看看Win32_Processor类的几个关键属性: AddressWidth On a 32-bit operating system, the v ...

  8. Windows 软件授权管理工具检验Windows7激活状态和许可证详细信息

    可用选项: -ipk <产品密钥> 安装产品密钥(替换现有密钥) -upk 卸载产品密钥 -ato 激活 Windows -dli [激活 ID | All] 显示许可证信息(默认: 当前 ...

  9. Powershell / windows终端 无法加载文件 因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170

    问题描述 在 powershell 或者 windows 终端中运行脚本文件时,提示"无法加载文件 因为在此系统上禁止运行脚本.有关详细信息,请参阅 https:/go.m icrosoft ...

最新文章

  1. 【linux基于Postfix和Dovecot邮件系统的搭建】
  2. 【实用】面对枯燥的源码,如何才能看得下去?
  3. Django+Linux+Uwsgi+Nginx项目部署文档
  4. RabbitMQ发布订阅实战-实现延时重试队列
  5. dji大疆机器人冬令营_2019RoboMaster高中生机器人冬令营火热进行中
  6. android.provider.documentscontract cannot be resolved.
  7. 如何限制用户的内存使用量
  8. Android接入热敏打印机,Android 关于佳博和汉印蓝牙热敏打印机开发
  9. 前端技术周刊 2018-09-10:Redux Mobx
  10. 序列化 自定义名字_反序列化漏洞(Web漏洞及防御)
  11. CentOS安装tengine(淘宝服务器)
  12. ASM1117-3.3V稳压芯片的典型电路图及分析
  13. 英特尔服务器主板型号哪里看,intel cpu型号参数如何查看
  14. c++读写json,JsonCpp配置
  15. win10关闭自带的杀毒
  16. 鼠标移动效果html5,js实现鼠标左右移动,图片也跟着移动效果
  17. Linux系统地址栏,Linux下Chrome地址栏输入卡顿该怎么办?
  18. linux如何配置ipv6DNS,linuxipv6dns服务器配置.doc
  19. 租用游艇问题(pta)
  20. js 模块defin化讲解

热门文章

  1. 机器学习真能产生智能决策吗?
  2. 解决win7无法进入睡眠状态的问题
  3. 腾讯云tcp考试多久出结果?腾讯云tcp考试考点和复习介绍
  4. 在vue3+vite中引入高德开放平台API实现边界范围多边形的绘制
  5. 了解如何执行在Linux上运行的应用程序
  6. 伪代码书写规则(转)
  7. android项目源码解析04:新浪微博客户端源码解析
  8. 营收、净利双增,用友网络的“云转型”迎来了“龙抬头”?
  9. 计算机垃圾回收的过程,谈谈.net对象生命周期(垃圾回收)
  10. 《图书馆笔记本防盗器》工程测试版发布!