这里的 WinDDK 版本是 Windows Server 2003 DDK, 可以编 Win98/2000/XP/2003 的驱动程序
#include <vcl.h> //用 C++ Builder 6.0 访问 USB 驱动程序 -- Victor Chen
#include <dir.h>
#include <setupapi.h>
#include "C:/WINDDK/3790/inc/ddk/w2k/usbdi.h"
#include "C:/WINDDK/3790/inc/ddk/w2k/devioctl.h"
#include <initguid.h>
//---------------------------------------------------------------------------
// 下面必须为驱动程序的 GUID 值, 这里我乱写的数
DEFINE_GUID(USB_DRIVER_GUID, 0x12345678,0xabcd,0x1122,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00);
//---------------------------------------------------------------------------
HANDLE OpenOneDevice(HDEVINFO hDvcInfo, PSP_INTERFACE_DEVICE_DATA DvcInfoData, char *sDevNameBuf)
{
HANDLE hOut = INVALID_HANDLE_VALUE;
ULONG iReqLen = 0;
SetupDiGetInterfaceDeviceDetail(hDvcInfo, DvcInfoData, NULL, 0, &iReqLen, NULL);
ULONG iDevDataLen = iReqLen; //sizeof(SP_FNCLASS_DEVICE_DATA) + 512;
PSP_INTERFACE_DEVICE_DETAIL_DATA pDevData = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(iDevDataLen);
pDevData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
if(SetupDiGetInterfaceDeviceDetail(hDvcInfo, DvcInfoData, pDevData, iDevDataLen, &iReqLen, NULL))
{
strcpy(sDevNameBuf, pDevData->DevicePath);
hOut = CreateFile(pDevData->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
}
free(pDevData);
return hOut;
}
//---------------------------------------------------------------------------
HANDLE OpenUsbDevice(const GUID *pGuid, char *sDevNameBuf)
{
HANDLE hOut = INVALID_HANDLE_VALUE;
HDEVINFO hDevInfo = SetupDiGetClassDevs(pGuid, NULL, NULL, DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);
SP_INTERFACE_DEVICE_DATA deviceInfoData;
deviceInfoData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);
ULONG nGuessCount = MAXLONG;
for(ULONG iDevIndex=0; iDevIndex<nGuessCount; iDevIndex++)
{
if(SetupDiEnumDeviceInterfaces(hDevInfo, 0, pGuid, iDevIndex, &deviceInfoData))
{
if((hOut=OpenOneDevice(hDevInfo, &deviceInfoData, sDevNameBuf)) != INVALID_HANDLE_VALUE)
break;
}
else if(GetLastError() == ERROR_NO_MORE_ITEMS) //No more items
{
break;
}
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return hOut;
}
//---------------------------------------------------------------------------
bool GetUsbDeviceFileName(const GUID *pGuid, char *sDevNameBuf)
{
HANDLE hDev = OpenUsbDevice(pGuid, sDevNameBuf);
if(hDev != INVALID_HANDLE_VALUE)
{
CloseHandle(hDev);
return true;
}
return false;
}
//---------------------------------------------------------------------------
HANDLE OpenMyDevice()
{
char DeviceName[MAXPATH] = "";
return OpenUsbDevice(&USB_DRIVER_GUID, DeviceName);
}
//---------------------------------------------------------------------------
HANDLE OpenMyDevPipe(const char *PipeName)
{
char DeviceName[MAXPATH] = "";
if(GetUsbDeviceFileName(&USB_DRIVER_GUID, DeviceName))
{
strcat(DeviceName,"\\");
strcat(DeviceName,PipeName);
return CreateFile(DeviceName, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
}
return INVALID_HANDLE_VALUE;
}
//---------------------------------------------------------------------------
//打开 USB 口读写, 由驱动程序的 Pipe 名确定
HANDLE hPipe = OpenMyDevPipe("MyPipe1"); //驱动程序里面的 Pipe 名, 对应访问某个端点的 I/O, 这里我乱写的, 需要与驱动一致
if(hPipe != INVALID_HANDLE_VALUE) //打开 Pipe 成功
{
ReadFile(hPipe, Buffer, BufSize, &nBytesRead, NULL); //从 hPipe 里读取数据到 Buffer 里
//WriteFile(hPipe, Buffer, BytesToWrite, &nBytesWritten, NULL); //把 Buffer 里面的 BytesToWrite 字节写入 hPipe
CloseHandle(hPipe);
}
//使用 DeviceIoControl 访问 USB 设备
HANDLE hDevice = OpenMyDevice();
if(hDevice != INVALID_HANDLE_VALUE) //打开设备成功
{
//这些 DeviceIoControl 功能都是由设备定义的, 具体看设备和驱动的资料
if(DeviceIoControl(hDevice, IOCTL_READ_xxxx, &IOBlock, sizeof(IOBLOCK), &c, 1, &nBytes, NULL))
{
//成功
}
CloseHandle(hDevice);
}
//如果你想打開一個USB管道,你首先要知道這種USB設備的GUID和管理道名稱,
//獲取句柄以后就可以使用ReadFile/WriteFile進行讀寫了﹗
//以下是代碼,請參考﹗﹗
// filename是管道名稱,如pipe01等
int TForm1::open_file( char *filename)
{
int successOpened = 0,i;
int NumOpened;
HANDLE h;
NumOpened=OpenAllUsbDevices((LPGUID) &GUID_CLASS_I82930_BULK);
if(NumOpened<=0)
return 0;
for(i=0;i<NumOpened;i++){
strcat (outnamebuf[i],"\\");
strcat (outnamebuf[i],filename);
h= CreateFile(outnamebuf[i],
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);
if (h == INVALID_HANDLE_VALUE) {
handUsbArray[i]=NULL;
} else {
handUsbArray[i]=h;
successOpened++;
}
}
return successOpened;
}
int TForm1::OpenAllUsbDevices(LPGUID pGuid) //打開所有的GUID為pGuid的USB器件
{ //輸出名存在outnamebuf中
ULONG NumberDevices;
HANDLE hOut = INVALID_HANDLE_VALUE; //HANDLE Phout[8];
HDEVINFO hardwareDeviceInfo;
SP_INTERFACE_DEVICE_DATA deviceInfoData;
ULONG i,flag=1,j;
ULONG NumDevicesOpened=0;
BOOLEAN done;
PUSB_DEVICE_DESCRIPTOR usbDeviceInst;
PUSB_DEVICE_DESCRIPTOR *UsbDevices = &usbDeviceInst;
char DeviceName[256]=""; //器件名
*UsbDevices = NULL;
UsbDevicesOpened = 0; //打開器件數置零
hardwareDeviceInfo = SetupDiGetClassDevs (
pGuid,
NULL, // Define no enumerator (global)
NULL, // Define no
(DIGCF_PRESENT | // Only Devices present
DIGCF_INTERFACEDEVICE)); // Function class devices.
NumberDevices = 4;
done = FALSE;
deviceInfoData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);
i=0 ;
while (!done) {
NumberDevices *= 2;
if (*UsbDevices) {
*UsbDevices =
(struct _USB_DEVICE_DESCRIPTOR *)realloc (*UsbDevices, (NumberDevices *
sizeof (USB_DEVICE_DESCRIPTOR)));
} else {
*UsbDevices = (struct _USB_DEVICE_DESCRIPTOR *)calloc (NumberDevices,
sizeof (USB_DEVICE_DESCRIPTOR));
}
if (NULL == *UsbDevices) {
SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
return 0 ;
}
usbDeviceInst = *UsbDevices + i;
for (; i < NumberDevices; i++) {
if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo,
0,
pGuid,
i,
&deviceInfoData))
{
hOut = OpenOneDevice (hardwareDeviceInfo, &deviceInfoData, DeviceName);
if ( hOut != INVALID_HANDLE_VALUE )
{
handUsbArray[UsbDevicesOpened]=hOut;
if(!outnamebuf[UsbDevicesOpened])
{
return 0;
}
for(j=0;j<256;j++)
{
*(outnamebuf[UsbDevicesOpened]+j)=*(DeviceName+j);
*(DeviceName+j)=0;
}
UsbDevicesOpened++;
}
}
else
{
if(ERROR_NO_MORE_ITEMS == GetLastError())
{
done = TRUE;
break;
}
}
} //end for
} //end while
SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
free ( *UsbDevices );
return UsbDevicesOpened ;
}
//--------------------------------------------------------------------
//
//
//
//--------------------------------------------------------------------
HANDLE TForm1::OpenOneDevice (
IN HDEVINFO HardwareDeviceInfo,
IN PSP_INTERFACE_DEVICE_DATA DeviceInfoData,
IN char *devName
)
{
PSP_INTERFACE_DEVICE_DETAIL_DATA functionClassDeviceData = NULL;
ULONG predictedLength = 0;
ULONG requiredLength = 0;
HANDLE hOut = INVALID_HANDLE_VALUE;
SetupDiGetInterfaceDeviceDetail (
HardwareDeviceInfo,
DeviceInfoData,
NULL, // probing so no output buffer yet
0, // probing so output buffer length of zero
&requiredLength,
NULL);
predictedLength = requiredLength;
functionClassDeviceData =(struct _SP_DEVICE_INTERFACE_DETAIL_DATA_A *)
malloc (predictedLength);
functionClassDeviceData->cbSize = sizeof
(SP_INTERFACE_DEVICE_DETAIL_DATA);
if (! SetupDiGetInterfaceDeviceDetail (
HardwareDeviceInfo,
DeviceInfoData,
functionClassDeviceData,
predictedLength,
&requiredLength,
NULL)) {
free( functionClassDeviceData );
return INVALID_HANDLE_VALUE;
}
strcpy( devName,functionClassDeviceData->DevicePath) ;
hOut = CreateFile (
functionClassDeviceData->DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, // no SECURITY_ATTRIBUTES structure
OPEN_EXISTING, // No special create flags
0, // No special attributes
NULL); // No template file
if (INVALID_HANDLE_VALUE == hOut) {
}
free( functionClassDeviceData );
return hOut;
}
1.USB 设备硬件部分
a.这个硬件的标识是用的 Vender ID 和 Product ID, 即“厂家标识”和“产品标识”
b.这个硬件规定了各个 End Point (端点) 的性质, 读/写 及 类型 (Control/Interrupt/Bulk/Isochronous)
c.这个硬件的固件里面有 DeviceIoControl 的实现部分, 规定了这个函数的具体参数和动作
2.USB 设备驱动
①硬件接口
a.需要识别 Vender ID 和 Product ID
b.对每个 EndPoint 的每个 I/O 分配一个 Pipe, 并且起一个名字作为软件接口
c.做 DeviceIoControl 的接口
②软件接口
a.GUID, 驱动程序的标识, 每个驱动程序使用不同的 GUID, GUID 是识别驱动的, 与硬件无关 (驱动程序升级版本 GUID 不能修改)
b.硬件接口里面的 b: Pipe 名字是软件接口, 这个 Pipe 名字纯粹由驱动定义的, 和硬件无关, 升级驱动不能改 Pipe 的名字
c.硬件接口里面的 c 的各个参数也是软件的接口, 这些参数是由硬件带来的, 不是驱动规定的, 当然也可以在驱动里面转义, 隐藏设备的真实情况
③这个驱动程序是用 WinDDK 编译的, 可以用文本编辑器或其他开发工具的编辑器编程序代码, 然后调用 WinDDK 编译
3.读写 USB 口的程序
①与驱动的接口
a.利用驱动程序里面的 GUID 找出设备的文件名, 用 CreateFile 函数打开设备。我前面的程序里面的 OpenUsbDevice 就是这个作用
b.通过 a.得到的设备文件名和驱动程序里面的 Pipe 名打开 Pipe, 访问这个 Pipe 对应的 USB 端点 (读写数据)
c.使用 a.的 CreateFile 得到的句柄, 通过 DeviceIoControl 实现设备规定的动作
②有关需要的资料
a.Vender ID, Product ID 和 GUID 一般在驱动程序的 .inf 文件里面能看到, 如果找不到就需要和厂家联系
b.Pipe 的名字是驱动程序规定的, 需要有驱动程序的资料才能知道
c.DeviceIoControl 的参数需要有驱动程序的资料或者硬件资料才能知道
③这个程序一般用 C/C++ 直接编写, 如果使用其他语言(VB/PB等)需要调用 C/C++ 编的 DLL
USB 驱动程序可以到注册表里面找到:
"HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Enum\\USB\\Vid_厂家标识&Pid_产品标识\\驱动程序"
里面的 ClassGUID 就是驱动程序的 GUID 标识, 例如 {36FC9E60-C465-11CF-8056-444553540000}
相当于程序的: DEFINE_GUID(USB_DRIVER_GUID, 0x36FC9E60,0xC465,0x11CF,0x80,0x56,0x44,0x45,0x53,0x54,0x00,0x00);
另外在这个注册表键里面还可找到有关设备的其他描述, 例如 DeviceDesc = "USB Mass Storage Device" 等
如何根据驱动程序及其安装文件来访问该设备呢
我要讲一下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,DeviceDetailD
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:为前缀的地方都是注释部分
首先你要确定是设备是支持的USB协议还是HID协议,如果是USB设备,你需要安装对应的驱动,然后枚举USB设备,得到与此设备GUID一致的设备句柄
,然后打开设备(createfile),再通过DeviceIoControl搜索来向设备发送命令字,并在输入输出缓冲区里得到数据,如果是HID协议,先通过VID和
PID来枚举并打开设备句柄,通过调用HID.DLL的函数来完成通信。这些操作的前期都需要你知道此设备的一些工作字,也就是硬件开发时写到固件的
程序是如何去发送和接收,解析上位机数据的。
对USB的认识 http://blog.csdn.net/qwent/article/details/5346199