• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 224  文章 - 41  trackbacks - 0
            <2010年6月>
            303112345
            6789101112
            13141516171819
            20212223242526
            27282930123
            45678910

            享受編程

            常用鏈接

            留言簿(11)

            隨筆分類(159)

            隨筆檔案(224)

            文章分類(2)

            文章檔案(4)

            經(jīng)典c++博客

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            原文地址:http://www.cnblogs.com/SunYu/archive/2010/04/29/1723977.html
            codeproject:http://www.codeproject.com/KB/system/HwDetect.aspx

             

            簡介

            現(xiàn)在對于IT的安全來說,熱插撥設(shè)備是個(gè)很大的威脅。在這篇文章中,我將試著開發(fā)一個(gè)用戶應(yīng)用程序來檢測本機(jī)系統(tǒng)上的設(shè)備改變。例如:插入一個(gè)USB設(shè)備、Ipod、USB無線網(wǎng)卡等等。這個(gè)程序同樣也可以停用任何支持插拔的設(shè)備。在文章的后面,我會(huì)簡述一下程序的工作原理和它的局限性。

            怎么來檢測硬件設(shè)備的改變?

            事實(shí)上,Windows操作系統(tǒng)會(huì)對上層程序發(fā)送WM_DEVICECHANGE消息來通知設(shè)備的改變。我們所要作的僅僅是添加一個(gè)句柄來處理這個(gè)事件。

            Collapse

            BEGIN_MESSAGE_MAP(CHWDetectDlg, CDialog)
                // ... other handlers
                ON_MESSAGE(WM_DEVICECHANGE, OnMyDeviceChange)
            END_MESSAGE_MAP()
             
            LRESULT CHWDetectDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam)
            {
                // for more information, see MSDN help of WM_DEVICECHANGE
                // this part should not be very difficult to understand
                if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) {
                    PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam;
                    switch( pHdr->dbch_devicetype ) {
                        case DBT_DEVTYP_DEVICEINTERFACE:
                            PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
                            // do something...
                            break;
             
                        case DBT_DEVTYP_HANDLE:
                            PDEV_BROADCAST_HANDLE pDevHnd = (PDEV_BROADCAST_HANDLE)pHdr;
                            // do something...
                            break;
             
                        case DBT_DEVTYP_OEM:
                            PDEV_BROADCAST_OEM pDevOem = (PDEV_BROADCAST_OEM)pHdr;
                            // do something...
                            break;
             
                        case DBT_DEVTYP_PORT:
                            PDEV_BROADCAST_PORT pDevPort = (PDEV_BROADCAST_PORT)pHdr;
                            // do something...
                            break;
             
                        case DBT_DEVTYP_VOLUME:
                            PDEV_BROADCAST_VOLUME pDevVolume = (PDEV_BROADCAST_VOLUME)pHdr;
                            // do something...
                            break;
                    }
                }
                return 0;
            }

            然而默認(rèn)情況下,Windows操作系統(tǒng)發(fā)送WM_DEVICECHANGE有些限制:

            1 只有頂層窗體的程序才能收到這個(gè)消息

            2 僅僅串口、磁盤發(fā)生改變,才對每個(gè)程序廣播這個(gè)消息

            的確不錯(cuò),至少你可以知道移動(dòng)U盤、移動(dòng)硬盤、光盤被安裝或彈出了,通過DEV_BROADCAST_VOLUME.dbcv_unitmask你也可以獲得其對應(yīng)的盤符。但實(shí)際上,你不知道底層處理的是哪個(gè)物理設(shè)備實(shí)際上被安裝到了系統(tǒng)中。

            API:RegisterDeviceNotification()

            所以,你不得不調(diào)用RegisterDeviceNotification()API來注冊其他類型的設(shè)備改變,或是你的程序僅僅是一個(gè)服務(wù)程序、沒有頂層窗體的程序。例如:如下的例子是用來注冊一個(gè)設(shè)備類型的接口的:

            Collapse

            1.  DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
            2.  ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
            3.  NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
            4.  NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
            5.  // assume we want to be notified with USBSTOR
            6.  // to get notified with all interface on XP or above
            7.  // ORed 3rd param with DEVICE_NOTIFY_ALL_INTERFACE_CLASSES and dbcc_classguid will be ignored
            8.  NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USBSTOR;
            9.  HDEVNOTIFY hDevNotify = RegisterDeviceNotification(this->GetSafeHwnd(),
                    amp;NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
            10. if( !hDevNotify ) {
            11.     // error handling...
            12.     return FALSE;
            13. }

            請注意第8行,NotificationFilter.dbcc_classguid關(guān)注的就是你關(guān)心的一類設(shè)備。

            參考這個(gè)blog: Doron Holan's blog

            一個(gè)支持即插即用的設(shè)備,有2個(gè)不同的GUID相關(guān),一個(gè)設(shè)備接口GUID, 一個(gè)是設(shè)備類GUID

            設(shè)備類GUID:定義了廣泛意義上一類設(shè)備的GUID,如果你打開設(shè)備管理器[我的電腦右鍵—>設(shè)備管理器],默認(rèn)的是按照“類型”排列的,每一個(gè)“類型”就是一個(gè)設(shè)備類,同時(shí)每一個(gè)設(shè)備類有一個(gè)唯一的ID就是設(shè)備類GUID。設(shè)備GUID定義了此類設(shè)備的圖標(biāo)、默認(rèn)的安全設(shè)置、安裝屬性(例如用戶不能手動(dòng)安裝這類設(shè)備,而必須通過PNP來遍歷),以及其他的設(shè)置信息。設(shè)備類GUID沒有定義對應(yīng)的I/O接口(請參考術(shù)語表),而更像是設(shè)備的分組。我認(rèn)為一個(gè)比較好的例子是端口類。串口COM和并口LPT 都是端口類的一部分,但其各有各的I/O接口,而且彼此互不兼容.一個(gè)設(shè)備僅僅屬于一個(gè)設(shè)備類。我們可以通過設(shè)備驅(qū)動(dòng)的INF文件的開頭來查看該設(shè)備的設(shè)備類GUID。

            設(shè)備接口GUID:定義了相互關(guān)聯(lián)I/O接口的GUID,每一個(gè)接口GUID的具體實(shí)例都支持基本的I/O設(shè)置。設(shè)備接口GUID也是對應(yīng)的驅(qū)動(dòng)程序基于PNP狀態(tài)來注冊、啟用、禁用設(shè)備。如果需要,一個(gè)設(shè)備甚至可以注冊多個(gè)同樣GUID的實(shí)例(假使每個(gè)都有相同的名字)[注:在實(shí)際的程序中,多次插拔USB口,確實(shí)會(huì)驅(qū)出相同的串口,例如port12,port12,port12…],盡管在現(xiàn)實(shí)世界中完全不需要這樣。一個(gè)簡單的I/O關(guān)聯(lián)接口是鍵盤設(shè)備,每個(gè)鍵盤設(shè)備的接口GUID必須相同。

            可以通過如下的注冊表路徑來查看當(dāng)前設(shè)備類GUID 設(shè)備接口GUID

            • \\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class
            • \\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses

            常用設(shè)備的接口GUID如下:

            設(shè)備接口名稱

            GUID

            USB Raw Device/USB設(shè)備

            {a5dcbf10-6530-11d2-901f-00c04fb951ed}

            Disk Device/磁盤設(shè)備

            {53f56307-b6bf-11d0-94f2-00a0c91efb8b}

            Network Card/網(wǎng)卡

            {ad498944-762f-11d0-8dcb-00c04fc3358c}

            Human Interface Device (HID)/人機(jī)界面設(shè)備

            {4d1e55b2-f16f-11cf-88cb-001111000030}

            Palm/手持設(shè)備

            {784126bf-4190-11d4-b5c2-00c04f687a67}

            DEV_BROADCAST_DEVICEINTERFACE的解碼

            如下是修改處理捕獲對應(yīng)事件的函數(shù):

            Collapse

            LRESULT CHWDetectDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam)
            {
                ....
                ....
                if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam )
                {
                    PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam;
                    switch( pHdr->dbch_devicetype )
                    {
                        case DBT_DEVTYP_DEVICEINTERFACE:
                            PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
                            UpdateDevice(pDevInf, wParam);
                            break;
                ....
                ....
            }

            從MSDN中,我們知道

            Collapse

            typedef struct _DEV_BROADCAST_DEVICEINTERFACE {
                DWORD dbcc_size;
                DWORD dbcc_devicetype;
                DWORD dbcc_reserved;
                GUID dbcc_classguid;
                TCHAR dbcc_name[1];
            } DEV_BROADCAST_DEVICEINTERFACE *PDEV_BROADCAST_DEVICEINTERFACE;

            我們似乎可以通過dbcc_name知道那個(gè)設(shè)備安裝到了當(dāng)前系統(tǒng)。J,答案是不對,dbcc_name僅僅是操作系統(tǒng)內(nèi)部使用來做為ID的,其實(shí)不易讀的,例如下面的這個(gè)dbcc_name:

             

            \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}

            • \\?\USB: USB 意思是這是一個(gè)USB設(shè)備類
            • Vid_04e8&Pid_053b: Vid/Pid 是一個(gè)廠商ID和產(chǎn)品ID(但這是由設(shè)備類指定的,USB設(shè)備類使用VID/PID,不同的設(shè)備類使用不同的命名約定)
            • 002F9A9828E0F06: 不清楚是怎么生成的,是唯一設(shè)備ID
            • {a5dcbf10-6530-11d2-901f-00c04fb951ed}:設(shè)備接口類GUID

            現(xiàn)在,我們來解出設(shè)備描述信息或是設(shè)備別名,有2種辦法:

            1 直接讀注冊表, \\HKLM\SYSTEM\CurrentControlSet\Enum\USB\Vid_04e8&Pid_503b\0002F9A9828E0F06

            2 使用 SetupDiXxx 系列API

            API:SetupDiXxx()

            Windows定義了一組API,讓用戶通過編程的辦法來獲取對應(yīng)的硬件設(shè)備信息。例如,我們可以通過dbcc_name來獲得設(shè)備描述信息或是設(shè)備別名。下面是這個(gè)辦法都具體步驟:

            1 首先通過SetupDiGetClassDevs()來獲得設(shè)備信息集 HDEVINFO,這個(gè)操作等同于是一個(gè)獲取目錄句柄的過程。

            2 接著使用SetupDiEnumDeviceInfo()來遍歷出這個(gè)設(shè)備信息集內(nèi)的所有設(shè)備,這個(gè)操作等同于把目錄列表的過程。對于每個(gè)遍歷出的,我們可以獲得SP_DEVINFO_DATA,這個(gè)等同于是文件句柄。

            3 在上面的枚舉過程中,使用SetupDiGetDeviceInstanceId()來讀取每個(gè)設(shè)備的實(shí)例ID,這個(gè)操作等同于是讀文件的屬性,一個(gè)設(shè)備的實(shí)例ID類似這個(gè):”USB\Vid_04e8&Pid_503b\0002F9A9828E0F06”,和dbcc_name非常像。

            4 如果設(shè)備的實(shí)例ID等同于dbcc_name,則通過SetupDiGetDeviceRegistryProperty()來獲取設(shè)備描述信息或是設(shè)備別名信息。

            程序如下:

            Collapse

            void CHWDetectDlg::UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam)
            {
                // dbcc_name:
                // \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
                // convert to
                // USB\Vid_04e8&Pid_503b\0002F9A9828E0F06
                ASSERT(lstrlen(pDevInf->dbcc_name) > 4);
                CString szDevId = pDevInf->dbcc_name+4;
                int idx = szDevId.ReverseFind(_T('#'));
                ASSERT( -1 != idx );
                szDevId.Truncate(idx);
                szDevId.Replace(_T('#'), _T('\\'));
                szDevId.MakeUpper();
             
                CString szClass;
                idx = szDevId.Find(_T('\\'));
                ASSERT(-1 != idx );
                szClass = szDevId.Left(idx);
             
                // if we are adding device, we only need present devices
                // otherwise, we need all devices
                DWORD dwFlag = DBT_DEVICEARRIVAL != wParam
                    ? DIGCF_ALLCLASSES : (DIGCF_ALLCLASSES | DIGCF_PRESENT);
                HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL, szClass, NULL, dwFlag);
                if( INVALID_HANDLE_VALUE == hDevInfo )
                {
                    AfxMessageBox(CString("SetupDiGetClassDevs(): ")
                        + _com_error(GetLastError()).ErrorMessage(), MB_ICONEXCLAMATION);
                    return;
                }
             
                SP_DEVINFO_DATA* pspDevInfoData =
                    (SP_DEVINFO_DATA*)HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA));
                pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
                for(int i=0; SetupDiEnumDeviceInfo(hDevInfo,i,pspDevInfoData); i++)
                {
                    DWORD DataT ;
                    DWORD nSize=0 ;
                    TCHAR buf[MAX_PATH];
             
                    if ( !SetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize) )
                    {
                        AfxMessageBox(CString("SetupDiGetDeviceInstanceId(): ")
                            + _com_error(GetLastError()).ErrorMessage(), MB_ICONEXCLAMATION);
                        break;
                    }
             
                    if ( szDevId == buf )
                    {
                        // device found
                        if ( SetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData,
                            SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buf, sizeof(buf), &nSize) ) {
                            // do nothing
                        } else if ( SetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData,
                            SPDRP_DEVICEDESC, &DataT, (PBYTE)buf, sizeof(buf), &nSize) ) {
                            // do nothing
                        } else {
                            lstrcpy(buf, _T("Unknown"));
                        }
                        // update UI
                        // .....
                        // .....
                        break;
                    }
                }
             
                if ( pspDevInfoData ) HeapFree(GetProcessHeap(), 0, pspDevInfoData);
                SetupDiDestroyDeviceInfoList(hDevInfo);
            }

            禁用設(shè)備

            假使你有一個(gè)正確的HDEVINFO和SP_DEVINFO_DATA(實(shí)際上,我們保持dbcc_name座位樹節(jié)點(diǎn)的tag,當(dāng)右鍵單擊某一個(gè)節(jié)點(diǎn)的時(shí)候,可以通過調(diào)用SetupDiGetClassDevs和SetupDiEnumDeviceInfo來獲得所需東西),按照如下的步驟即可禁用一個(gè)設(shè)備:

            1 給SP_PROPCHANGE_PARAMS結(jié)構(gòu)體賦上正確的值

            2 把上面賦完值的SP_PROPCHANGE_PARAMS作為參數(shù)傳入到SetupDiSetClassInstallParams()

            3 調(diào)用SetupDiCallClassInstaller(),傳遞參數(shù)DIF_PROPEFRTYCHANGE

            實(shí)際上,DIF也是按位做與運(yùn)算后兼容的,你也可以去傳遞不同的DIF參數(shù)來調(diào)用SetupDiSetClassInstallParams()。 更多信息,請參考MSDN”Handling DIF Codes”

            Collapse

            SP_PROPCHANGE_PARAMS spPropChangeParams ;
            spPropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
            spPropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE ;
            spPropChangeParams.Scope = DICS_FLAG_GLOBAL ;
            spPropChangeParams.HwProfile = 0; // current hardware profile
            spPropChangeParams.StateChange = DICS_DISABLE
             
            if( !SetupDiSetClassInstallParams(hDevInfo, &spDevInfoData,
                // note we pass spPropChangeParams as SP_CLASSINSTALL_HEADER
                // but set the size as sizeof(SP_PROPCHANGE_PARAMS)
                (SP_CLASSINSTALL_HEADER*)&spPropChangeParams, sizeof(SP_PROPCHANGE_PARAMS)) )
            {
                // handle error
            }
            else if(!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, hDevInfo, &spDevInfoData))
            {
                // handle error
            }
            else
            {
                // ok, show disable success dialog
                // note, after that, the OS will post DBT_DEVICEREMOVECOMPLETE for the disabled device
            }

            附錄:

            我使用這個(gè)程序,已經(jīng)多次測試了USB的無線網(wǎng)卡的,插入、拔出測試。

            局限性:

            1 明顯的,必須先運(yùn)行該程序,才能檢測硬件設(shè)備。例如:設(shè)備在操作系統(tǒng)啟動(dòng)前就已經(jīng)連接,或者在這個(gè)程序運(yùn)行前的連接都不會(huì)被檢測。但這個(gè)問題,可以通過保存當(dāng)前系統(tǒng)配置到遠(yuǎn)程計(jì)算機(jī)上,等啟動(dòng)完這個(gè)程序后再堅(jiān)持不同的配置來解決

            2 我們可以禁用設(shè)備,換而言之這也是我們所有能做到。我們不能訪問設(shè)備底層控制。 我認(rèn)為可以通過重新用基于內(nèi)核的過濾驅(qū)動(dòng)來實(shí)現(xiàn),則可以解決這個(gè)問題。

            posted on 2010-09-13 21:27 漂漂 閱讀(11385) 評論(0)  編輯 收藏 引用 所屬分類: 深入vc++c#開發(fā)
            久久国产精品一区| 色综合久久中文综合网| 久久亚洲精品无码aⅴ大香| 亚洲人成电影网站久久| 久久av无码专区亚洲av桃花岛| 亚洲AV无码久久| 精品久久久久久国产牛牛app| 久久性精品| 国产人久久人人人人爽| 综合久久给合久久狠狠狠97色| 久久久久亚洲AV无码专区体验| 久久成人国产精品一区二区| 久久这里只有精品首页| 国产精品成人无码久久久久久| 久久亚洲精品无码aⅴ大香| 久久国产精品偷99| 国产精品青草久久久久婷婷 | 久久久国产精品| 亚洲第一极品精品无码久久| 欧美与黑人午夜性猛交久久久 | 久久精品夜夜夜夜夜久久| 青青热久久国产久精品 | 香蕉久久一区二区不卡无毒影院| 亚洲国产精品成人久久蜜臀| 久久久国产精品网站| 久久久老熟女一区二区三区| 亚洲精品美女久久777777| 久久激情亚洲精品无码?V| 伊人久久综合热线大杳蕉下载| 久久人人爽人人爽人人片AV不| 久久久久国产精品人妻| 色天使久久综合网天天| 欧美亚洲另类久久综合婷婷| 久久强奷乱码老熟女网站| 狠狠综合久久综合中文88 | 久久久久亚洲AV成人网人人网站 | 区久久AAA片69亚洲| 少妇熟女久久综合网色欲| 久久精品国产亚洲av麻豆蜜芽| yy6080久久| 久久久无码人妻精品无码|