用户操作
[即时聊天] [发私信] [加为好友]
-ID:lfchen
2418次访问,排名2万外,好友0人,关注者87人。
lfchen的文章
原创 1 篇
翻译 0 篇
转载 8 篇
评论 2 篇
最近评论
footway:编译时报
uuid.lib(cguid_i.obj) : fatal error LNK1103: debugging information corrupt; recompile module

为什么?
文章分类
收藏
    相册
    danyueer
    灌水乐园
    我的图片
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    转载 实战 DeviceIoControl 系列 之五:列举已安装的存储设备 收藏

    新一篇: 实战 DeviceIoControl 系列 之六:访问物理端口  | 旧一篇: 实战DeviceIoControl 系列 之四:获取硬盘的详细信息

    实战 DeviceIoControl 之五:列举已安装的存储设备


    Q
    前几次我们讨论的都是设备名比较清楚的情况,有了设备名(路径),就可以直接调用CreateFile打开
    设备,进行它所支持的I/O操作了。如果事先并不能确切知道设备名,如何去访问设备呢?
    A
    访问设备必须用设备句柄,而得到设备句柄必须知道设备路径,这个套路以你我之力是改变不了的。
    每个设备都有它所属类型的GUID,我们顺着这个GUID就能获得设备路径。
    GUID
    是同类或同种设备的全球唯一识别码,它是一个128 bit(16字节)的整形数,真实面目为
    typedef struct _GUID
    {
         unsigned long   Data1;
         unsigned short Data2;
         unsigned short Data3;
         unsigned char   Data4[8];
    } GUID, *PGUID;
    例如,Disk类的GUID“53f56307-b6bf-11d0-94f2-00a0c91efb8b”,在我们的程序里可以定义为
    const GUID DiskClassGuid = {0x53f56307L, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb,
    0x8b)};
    或者用一个宏来定义
    DEFINE_GUID(DiskClassGuid, 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb,
    0x8b);
    通过GUID找出设备路径,需要用到一组设备管理的API函数
    SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetInterfaceDeviceDetail,
    SetupDiDestroyDeviceInfoList, 
    以及结构SP_DEVICE_INTERFACE_DATA, SP_DEVICE_INTERFACE_DETAIL_DATA
    有关信息请查阅MSDN,这里就不详细介绍了。 实现GUID到设备路径的代码如下:
    // 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(lpGuid,   // 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;
    }
    调用GetDevicePath函数时要注意,pszDevicePath是个指向字符串指针的指针,例如可以这样
     int i;
     char* szDevicePath[MAX_DEVICE];  //
    设备路径
     //
    分配需要的空间
     for(i=0; i<MAX_DEVICE; i++) szDevicePath[i] = new char[256];  //
    取设备路径
     nDevice = ::GetDevicePath((LPGUID)&DiskClassGuid, szDevicePath);
     //
    逐一获取设备信息
     for(i=0; i<nDevice; i++)
     {
      //
    打开设备
      hDevice = ::OpenDevice(szDevicePath[i]);
      if(hDevice != INVALID_HANDLE_VALUE)
      {
       ... ...  // I/O
    操作
      ::CloseHandle(hDevice);
      }
     }
     //
    释放空间
     for(i=0;i<MAX_DEVICE;i++) delete []szDevicePath[i];
    本例的Project中除了要包含winioctl.h外,还要包含initguid.hsetupapi.h,以及连接setupapi.lib
     
    Q
    得到设备路径后,就可以到下一步,用CreateFile打开设备,然后用DeviceIoControl 进行读写了吧?
    A
    是的。尽管该设备路径与以前我们接触的那些不太一样。本是“\\\\.\\PhysicalDrive0”,现在鸟枪换炮,
    变成了类似这样的一副尊容:
    “\\\\?\\ide#diskmaxtor_2f040j0__________________________vam51jj0#31465634475345582020202
    02020202020202020#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}”

    其实这个设备名在注册表的某处可以找到,例如在Win2000中这个名字位于
    HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Services\\Disk\\Enum\\0

    只不过“#”换成了“\\”。分析一下这样的设备路径,你会发现很有趣的东西,它们是由接口类型、产品
    型号、固件版本、序列号、计算机名、GUID等信息组合而成的。当然,它是没有规范的,不能指望从这
    里面得到你希望知道的东西。
    CreateFile打开设备后,对于存储设备,IOCTL_DISK_GET_DRIVE_GEOMETRY
    IOCTL_STORAGE_GET_MEDIA_TYPES_EX
    I/O控制码照常使用。
    今天我们讨论一个新的控制码:IOCTL_STORAGE_QUERY_PROPERTY,获取设备属性信息,希望得到
    系统中所安装的各种固定的和可移动的硬盘、优盘和CD/DVD-ROM/R/W的接口类型、序列号、产品ID
    等信息。
    // IOCTL
    控制码
    #define IOCTL_STORAGE_QUERY_PROPERTY   CTL_CODE(IOCTL_STORAGE_BASE, 0x0500,
    METHOD_BUFFERED, FILE_ANY_ACCESS) //
    存储设备的总线类型
    typedef enum _STORAGE_BUS_TYPE {
         BusTypeUnknown = 0x00,
         BusTypeScsi,
         BusTypeAtapi,
         BusTypeAta,
         BusType1394,
         BusTypeSsa,
         BusTypeFibre,
         BusTypeUsb,
         BusTypeRAID,
         BusTypeMaxReserved = 0x7F
    } STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
    //
    查询存储设备属性的类型
    typedef enum _STORAGE_QUERY_TYPE {
         PropertyStandardQuery = 0,           //
    读取描述
         PropertyExistsQuery,                 //
    测试是否支持
         PropertyMaskQuery,                   //
    读取指定的描述
         PropertyQueryMaxDefined  //
    验证数据
    } STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
    //
    查询存储设备还是适配器属性
    typedef enum _STORAGE_PROPERTY_ID {
         StorageDeviceProperty = 0,   //
    查询设备属性
         StorageAdapterProperty   //
    查询适配器属性
    } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
    //
    查询属性输入的数据结构
    typedef struct _STORAGE_PROPERTY_QUERY {
     STORAGE_PROPERTY_ID PropertyId;  //
    设备/适配器
     STORAGE_QUERY_TYPE QueryType;  //
    查询类型 
         UCHAR AdditionalParameters[1];  //
    额外的数据(仅定义了象征性的1个字节)
    } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
     
    //
    查询属性输出的数据结构
    typedef struct _STORAGE_DEVICE_DESCRIPTOR {
         ULONG Version;  //
    版本
         ULONG Size;   //
    结构大小
         UCHAR DeviceType;  //
    设备类型
         UCHAR DeviceTypeModifier; // SCSI-2