First of all,我要講一下Windows對每檢測到一個新設備的處理過程:
1.首先Windows將各種設備分成不同的設備類,比如說USB Storage存儲類設備,而這些類設備都有一個GUID,它們位於註冊表中HKEY_LOCAL_MACHINE/ControlSet001/Control/Class下,在這個鍵下你看到的以128位長度結點名稱爲結點都是設備類。
2.當檢測到一個新設備時,Windows OS就會到KEY_LOCAL_MACHINE/ControlSet001下去搜索,如果此類設備已經註冊,那麼就此子鍵下增加一個子鍵,這個子鍵的名稱是順序遞增的,如果當前該類設備中最大子鍵名稱爲0005,那麼新設備的就是0006,0000,0001等就是設備的序列號。如果發現這個設備沒有註冊,那麼OS就會以該設備對應的驅動程序安裝文件.inf中的ClassGuid爲名稱來創建一個鍵,並將此被檢測到的設備的序列號爲0,在該鍵下創建一個子鍵,並且命名爲0000來存儲該設備的相關信息,之所以命名爲0000,因爲該設備是該類設備的第一個。
3.有些設備,如我們常用的COM1,COM2,當我們要訪問它時可以用CreateFile("COM1"。。。)就可以打開串口,這裏的COM1,COM2是符號名,有些設備也用到了符號名,比如說有些USB設備虛擬成一個COM口,如COM3,COM4,COM5等,在程序中我們只需要對COM3,COM4,COM5進行訪問,就相當對該設備進行訪問。
在沒有符號名的情況下,我們如何根據設備驅動程序以及設備安裝文件.inf來對設備進行訪問呢?我們可以這樣:
1.首先,我們在這個設備的符號名中找到設備類的ClassGuid,這是一定可以找到的。
2.然後我們到HKEY_LOCAL_MACHINE/ControlSet001/Control/Class下去找這個設備類,在找到後,我們再找它的子鍵,找到該設備對應的序列號,如它到底是0000還是0001,在得到這兩個數據即ClassGuid和設備序列號後,就好辦了。
下面我寫一段代碼,用來訪問我機器上的一USB設備,並在listbox中列出當前機器上此設備類下的所有設備。
extern "C"
{
#include "setupapi.h"
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
//First of all,I will enumurate all the devices under the specified deviceclass
HKEY m_hKey,m_hSubKey;
long m_lResult=0;//using for return value
int m_nKeyIndex=0,m_nValueIndex=0;
char cKeyName[255],cValue[255];
unsigned char pbData[255];
BOOL bOutter=TRUE,bInter=TRUE;
char *cRoot="SYSTEM//ControlSet001//Control//Class//{4D36E96D-E325-11CE-BFC1-08002BE10318}";
AnsiString m_sAttached("");
m_lResult=::RegOpenKeyEx(HKEY_LOCAL_MACHINE,cRoot,0,KEY_ALL_ACCESS,&m_hKey);
if(m_lResult!=ERROR_SUCCESS) return FALSE;
//Enum Keys
while(bOutter){
m_lResult=::RegEnumKey(m_hKey,m_nKeyIndex,cKeyName,255);
bInter=TRUE;
if(m_lResult!=ERROR_SUCCESS) bOutter=FALSE;
else{
m_lResult=::RegOpenKeyEx(m_hKey,cKeyName,0,KEY_ALL_ACCESS,&m_hSubKey);
if(m_lResult!=ERROR_SUCCESS){
::RegCloseKey(m_hKey);
return FALSE;}
while(bInter){
unsigned long m_nDataSize=255;
unsigned long m_nValueNameSize=255;
unsigned long m_nType;
m_lResult=::RegEnumValue(m_hSubKey,m_nValueIndex,cValue,&m_nValueNameSize,0,&m_nType,pbData,&m_nDataSize);
if(m_lResult!=ERROR_SUCCESS) bInter=FALSE;
else{
if(!strcmp(cValue,"AttachedTo")){
m_sAttached=(AnsiString)(char*)pbData;
}
if(!strcmp(cValue,"DriverDesc")){
m_lstDevice->Items->Add((AnsiString)(char*)pbData+" "+m_sAttached);
}
m_nValueIndex++;
}
}
m_nValueIndex=0;
m_nKeyIndex++;
}
}
::RegCloseKey(m_hKey);
::RegCloseKey(m_hSubKey);
file://Next Step,I will access one of the device.I know its device serialno:0001
DWORD ReqLength;
DWORD Flags=DIGCF_PRESENT|DIGCF_DEVICEINTERFACE;
GUID CardGuid={4D36E96D-E325-11CE-BFC1-08002BE10318};
HANDLE hCard=0;
PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceDetailData;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
DeviceInterfaceData.cbSize=sizeof(SP_DEVICE_INTERFACE_DATA);
hCard=SetupDiGetClassDevs(&CardGuid,NULL,NULL,Flags);
if(hCard==INVALID_HANDLE_VALUE){
::MessageBox(0,"Invalid Parameters!","Error",MB_OK|MB_ICONERROR);
return;}
BOOL status=SetupDiEnumDeviceInterfaces(hCard,NULL,&CardGuid,Index,&DeviceInterfaceData,&ReqLength,
NULL);//Index即設備的序號,這裏的Index爲1.
if(!status){
::MessageBox(0,"Failed to enumurate the specified
device!","Error",MB_OK+MB_ICONERROR);
::CloseHandle(hCard);
return;}
SetupDiGetInterfaceDeviceDetail(hCard,&DeviceInterfaceData,NULL,0,&ReqLength,NULL);
DeviceDetailData=(PSP_INTERFACE_DEVICE_DETAIL_DATA)new char[ReqLength];
if(DeviceDetailData){
::MessageBox("ERROR NOT ENOUGH MEMORY!","Error",MB_OK+MB_ICONERROR);
::CloseHandle(hCard);
return;}
status=SetupDiGetInterfaceDeviceDetail(hCard,&DeviceInterfaceData,DeviceDetailData,ReqLength,&ReqLength,NULL);
if(!status){
::MessageBox(0,"Failed to get interface detailed data","Error",MB_OK+MB_ICONERROR);
delete DeviceDetailData;
DeviceDetailData=NULL;
return;}
ShowMessage(DeviceDetailData->DevicePath());//在這裏得到DevicePath就像得到符號名一樣,那麼接着下來,你你就可以象對串口操作一樣來寫程序,即是說:
HANDLE hUSB=::CreateFile(DeviceDetailData.DevicePath(),..............);
file://ReadFile,WriteFile and so on.....
}
所有以file:爲前綴的地方都是註釋部分,特此聲明,以免誤解,這是CSDN文檔編輯器自動加的。
因最近一直在忙於學習驅動開發,現正在用DriverStudio進行開發一個USB Modem的驅動程序,有一點學習心得,特此交流一下。