usbview 源码结构分析


usbview 是微软随wdk 一起发布的一个usb设备查看源码,能够对系统中全部的USB设备进行枚举,可以查看每个设备的详细信息。既使没有安装相应的驱动也可以。

如何编译请参考:

http://blog.csdn.net/chenyujing1234/article/details/7577320

这篇文章对于这个程序的大体逻辑已经分析的差不多了,因为本人工作需也对该程序进行了深入的研究,花费了很多的时间,这里把我的一些个人所得记录下来,以助他人。

知识在于共享嘛!

一、整体介绍

微软已经对该程序进行了更新,代码位于

http://code.msdn.microsoft.com/windowshardware/USBView-sample-application-e3241039/

但是这个代码只能在vs2012及以上中使用,且只支持window7以上操作系统。

本文所用的代码可以在winDDK  7600的安装目录下找到。 src/usb下面。

程序用C写成,且年代较为久远,约为2001年左右完成后经部分修改的,所以到处带着C时代的痕迹。ALLOC到处都有,内存管理异常繁琐,那时候的程序员真的好累啊!


二、核心代码介绍

最核心的函数就是

EnumerateHubPorts (
    HTREEITEM   hTreeParent,
    HANDLE      hHubDevice,
    ULONG       NumPorts
)

对USB设备的枚举都在这个函数中实现


        success = DeviceIoControl(hHubDevice,
                                  IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
                                  connectionInfoEx,
                                  nBytesEx,
                                  connectionInfoEx,
                                  nBytesEx,
                                  &nBytesEx,
                                  NULL);

用这个函数取得设备连接的信息,保存在 connectionInfoEx结构中


如果有设备连接这个端口,并且程序设置获取配置描述性信息,则

        if (gDoConfigDesc &&
            connectionInfoEx->ConnectionStatus == DeviceConnected)
        {
            configDesc = GetConfigDescriptor(hHubDevice,
                                             index,
                                             0);
        }
gDoConfigDesc是全局变量,是由程序一个菜单项控制是否枚举设备的配置信息,默认为false。


GetConfigDescriptor (
    HANDLE  hHubDevice,
    ULONG   ConnectionIndex,
    UCHAR   DescriptorIndex
)
这个函数是核心中的核心,因为我们想要得到的信息基本上就靠这个了。

开始申请了一段内存,它的大小是 2个结构体大小之和。       

    UCHAR   configDescReqBuf[sizeof(USB_DESCRIPTOR_REQUEST) +
                             sizeof(USB_CONFIGURATION_DESCRIPTOR)];

看下面就明白了,前面保存的是 REQUEST,后面保存的是 CONFIGURATION_DESCRIPTOR类型

    configDescReq = (PUSB_DESCRIPTOR_REQUEST)configDescReqBuf;
    configDesc = (PUSB_CONFIGURATION_DESCRIPTOR)(configDescReq+1);

再看下一段代码,对configDescReq进行赋值,这个是什么呢,就是对USB设备进行查询的请求。

参考:http://www.usb.org/developers/docs/usb_31_031114.zip   9.2节

通过DeviceIOControl函数发送给USB设备,然后设备会进行回复,回复的信息保存在ConfigDescReq中。

   //
    // USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
    // IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
    //
    // USBD will automatically initialize these fields:
    //     bmRequest = 0x80
    //     bRequest  = 0x06
    //
    // We must inititialize these fields:
    //     wValue    = Descriptor Type (high) and Descriptor Index (low byte)
    //     wIndex    = Zero (or Language ID for String Descriptors)
    //     wLength   = Length of descriptor buffer
    //
    configDescReq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8)
                                        | DescriptorIndex;

    configDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));

    // Now issue the get descriptor request.
    //
    success = DeviceIoControl(hHubDevice,
                              IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
                              configDescReq,
                              nBytes,
                              configDescReq,
                              nBytes,
                              &nBytesReturned,
                              NULL);

通过这个函数得到 回复信息的长度。

   nBytes = sizeof(USB_DESCRIPTOR_REQUEST) + configDesc->wTotalLength;
然后再ALLOC 一块内存,用来保存配置信息

    configDescReq = (PUSB_DESCRIPTOR_REQUEST)ALLOC(nBytes);

再次DeviceIoControl将获取的配置信息保存在configDescReq中。注意这个内存最前面的是请求,真正的信息是在后面一段。这个地方直接将这个地址返回

然后对这块信息进行处理, 注意看 configDesc+1的地方,把请求的信息跳过了。

AreThereStringDescriptors函数只是比较简单地检查是否存在描述信息。

存在的话,就通过GetAllStringDescriptors获取相关信息

       if (configDesc != NULL &&
            AreThereStringDescriptors(&connectionInfoEx->DeviceDescriptor,
                                      (PUSB_CONFIGURATION_DESCRIPTOR)(configDesc+1)))
        {
            stringDescs = GetAllStringDescriptors(
                              hHubDevice,
                              index,
                              &connectionInfoEx->DeviceDescriptor,
                              (PUSB_CONFIGURATION_DESCRIPTOR)(configDesc+1));
        }

GetAllStringDescriptors这个函数值得研究,因为它要从configDesc中取出我们需要的信息

函数后面通过一个while循环获取配置信息和接口信息。

PSTRING_DESCRIPTOR_NODE
GetAllStringDescriptors (
    HANDLE                          hHubDevice,
    ULONG                           ConnectionIndex,
    PUSB_DEVICE_DESCRIPTOR          DeviceDesc,
    PUSB_CONFIGURATION_DESCRIPTOR   ConfigDesc
)
{
    PSTRING_DESCRIPTOR_NODE supportedLanguagesString;
    PSTRING_DESCRIPTOR_NODE stringDescNodeTail;
    ULONG                   numLanguageIDs;
    USHORT                  *languageIDs;

    PUCHAR                  descEnd;
    PUSB_COMMON_DESCRIPTOR  commonDesc;

    //
    // Get the array of supported Language IDs, which is returned
    // in String Descriptor 0
    //
    supportedLanguagesString = GetStringDescriptor(hHubDevice,
                                                   ConnectionIndex,
                                                   0,
                                                   0);

    if (supportedLanguagesString == NULL)
    {
        return NULL;
    }

    numLanguageIDs = (supportedLanguagesString->StringDescriptor->bLength - 2) / 2;

    languageIDs = &supportedLanguagesString->StringDescriptor->bString[0];

    stringDescNodeTail = supportedLanguagesString;

    //
    // Get the Device Descriptor strings
    //

    if (DeviceDesc->iManufacturer)
    {
        stringDescNodeTail = GetStringDescriptors(hHubDevice,
                                                  ConnectionIndex,
                                                  DeviceDesc->iManufacturer,
                                                  numLanguageIDs,
                                                  languageIDs,
                                                  stringDescNodeTail);
    }

    if (DeviceDesc->iProduct)
    {
        stringDescNodeTail = GetStringDescriptors(hHubDevice,
                                                  ConnectionIndex,
                                                  DeviceDesc->iProduct,
                                                  numLanguageIDs,
                                                  languageIDs,
                                                  stringDescNodeTail);
    }

    if (DeviceDesc->iSerialNumber)
    {
        stringDescNodeTail = GetStringDescriptors(hHubDevice,
                                                  ConnectionIndex,
                                                  DeviceDesc->iSerialNumber,
                                                  numLanguageIDs,
                                                  languageIDs,
                                                  stringDescNodeTail);
    }


    //
    // Get the Configuration and Interface Descriptor strings
    //

    descEnd = (PUCHAR)ConfigDesc + ConfigDesc->wTotalLength;

    commonDesc = (PUSB_COMMON_DESCRIPTOR)ConfigDesc;

    while ((PUCHAR)commonDesc + sizeof(USB_COMMON_DESCRIPTOR) < descEnd &&
           (PUCHAR)commonDesc + commonDesc->bLength <= descEnd)
    {
        switch (commonDesc->bDescriptorType)
        {
            case USB_CONFIGURATION_DESCRIPTOR_TYPE:
                if (commonDesc->bLength != sizeof(USB_CONFIGURATION_DESCRIPTOR))
                {
                    OOPS();
                    break;
                }
                if (((PUSB_CONFIGURATION_DESCRIPTOR)commonDesc)->iConfiguration)
                {
                    stringDescNodeTail = GetStringDescriptors(
                                             hHubDevice,
                                             ConnectionIndex,
                                             ((PUSB_CONFIGURATION_DESCRIPTOR)commonDesc)->iConfiguration,
                                             numLanguageIDs,
                                             languageIDs,
                                             stringDescNodeTail);
                }
                (PUCHAR)commonDesc += commonDesc->bLength;
                continue;

            case USB_INTERFACE_DESCRIPTOR_TYPE:
                if (commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR) &&
                    commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR2))
                {
                    OOPS();
                    break;
                }
                if (((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->iInterface)
                {
                    stringDescNodeTail = GetStringDescriptors(
                                             hHubDevice,
                                             ConnectionIndex,
                                             ((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->iInterface,
                                             numLanguageIDs,
                                             languageIDs,
                                             stringDescNodeTail);
                }
                (PUCHAR)commonDesc += commonDesc->bLength;
                continue;

            default:
                (PUCHAR)commonDesc += commonDesc->bLength;
                continue;
        }
        break;
    }

    return supportedLanguagesString;
}


OK这些信息获取完毕,再判断连接的是hub 还是设备, 如果是Hub ,则继续对 hub进行枚举


                if (EnumerateHub(hTreeParent, 
                             extHubName,
                             connectionInfoEx,
                             configDesc,
                             stringDescs,
                             deviceDesc) == FALSE) 

如果是设备,则建立一个数据结构 

info = (PUSBDEVICEINFO) ALLOC(sizeof(USBDEVICEINFO));

用来保存设备信息

            info->DeviceInfoType = DeviceInfo;
            info->ConnectionInfo = connectionInfoEx;
            info->ConfigDesc = configDesc;
            info->StringDescs = stringDescs;
再把设备加入到左侧的树中

            AddLeaf(hTreeParent, 
                            (LPARAM)info,
                            leafName,
                            icon);

info作为一个指针传递给 HTREEITEM  中的lParam,您点击左侧某个设备的时候,就调用display.c中的相关函数 把info中的信息再次解析成文本显示出来


三、我在修改代码中的几个错误

 1.c语言与c++有一些不同的地方。

C语言中可以强制进行指针类型转换,但是C++ 中指针类型转换后无法对指针进行赋值。

(PUCHAR)commonDesc += commonDesc->bLength;
这样的语句会报错,无法对左值赋值。因为这个地方的强制类型转换 将 commonDesc转换为 PUCHAR类型,成为一个值,而不是commonDesc这个变量,无法再进行+=操作。

在修改过程中,我使用了

commonDesc = reinterpret_cast<PUSB_COMMON_DESCRIPTOR> (commonDesc + commonDesc->bLength);
这个是不是看起来非常地正确,非也,必须把后面的commonDesc转换为PUCHAR类型才可以。

这个bug让我的程序出错花了2天才搞定。


2.内存管理

windows对代码质量的追求还是非常高的,这个程序里面用了非常多的ALLOC,要防止内存泄露非常困难。

在usbview.h中定义了

#if DBG

#define ALLOC(dwBytes) MyAlloc(_T(__FILE__), __LINE__, (dwBytes))
#define REALLOC(hMem, dwBytes) MyReAlloc((hMem), (dwBytes))
#define FREE(hMem)  MyFree((hMem))
#define CHECKFORLEAKS() MyCheckForLeaks()

#else
在debug.c中对以上有所实现,将内存分配和回收改成自定义的函数,可以用来对内存泄露进行检查。


在usbview.c 中

在RefreshTree的时候,调用下面的函数,WalTree中 CleanupItem是一个函数指针。

    if (ghTreeRoot)
    {
        WalkTree(ghTreeRoot, CleanupItem, 0);
        TreeView_DeleteAllItems(ghTreeWnd);
        ghTreeRoot = NULL;
    }
在enum.c中

VOID
CleanupItem (
    HWND      hTreeWnd,
    HTREEITEM hTreeItem
)

获取每个HTREEITEM中的 info指针,然后进行内存释放,确保不存在内存泄露。


附上我用vs2005 做的一个 usbview 的工程,需要安装winddk 7600. 可以编译通过。请各位自行调试查看。

我所做的只是新建一个vs工程,把usbview的文件都拖进来, 添加相应的头文件目录和链接库的目录。

http://download.csdn.net/download/v6543210/7649119


不得不说csdn 网站感觉非常的不稳定,上传非常的慢,有时候会出莫名其妙的错误。因为论坛老了?








  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路边闲人2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值