设备接口
在WDM驱动中,一般很少用到设备名,也很少用到符号链接,而是用一个设备接口
来指定设备。设备接口其实就是一组全局标识,它是一个128位组成的数字,并能保证在
全世界范围内不会产生冲突。
引入设备接口的主要原因是避免设备名冲突,例如,不同的网卡厂商可能都将自己的
设备名命名为“NetCardDevice",当用户插入两块网卡时,就会引起命名冲突。而引入设
备接口的概念,各个网卡厂商用自己定义的128位数字指定自己的设备,从小概率意义上,
保证了各个设备的设备接口不会一样。
WDM驱动中设置接口
在WDM驱动程序中使用设备接口,首先在创建设备的时候,不能指定设备名,这样
系统会为设备自动创建一个设备名, 该设备名就是一个数字,新设备加入的时候,数字依
次递增。
另外不用IoCreateSymbolicLink创建符号链接,而是用loRegisterDeviceInterface为设备创
建设备接口。应用程序通过设备接口来寻找到设备.
//需要包含此头文件
#include <initguid.h>
//在VS中使用生成GUID 选第二种方式
DEFINE_GUID(MY_WDM_DEVICE, 0xe57c50f, 0xccc, 0x4ad2, 0xa8, 0x95, 0x93, 0xc5, 0xed, 0x11, 0x98, 0x60);
NTSTATUS IoRegisterDeviceInterface(
IN PDEVICE OBJECT PhysicalDeviceobject, //物理设备对象 也就是pdo
IN CONST GUID *InterfaceClassGuid, //设备接口的指针,这里是GUID类型的指针
IN PUNICODE STRING ReferenceString OPTIONAL, //一般设置为NULL
OUT PUNICODE_STRING SymbolicLinkName //将GUID输出一串UNICODE字符串
)
创建设备接口
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
{
//判断代码是否运行在分页内存中
PAGED_CODE();
KdPrint(("Enter HelloWDMAddDevice\n"));
NTSTATUS status;
PDEVICE_OBJECT fdo;
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,//没有指定设备名
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo);
if( !NT_SUCCESS(status))
return status;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
//附加设备到pdo上
pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
//创建设备接口
status = IoRegisterDeviceInterface(PhysicalDeviceObject, &MY_WDM_DEVICE, NULL, &pdx->interfaceName);
if( !NT_SUCCESS(status))
{
IoDeleteDevice(fdo);
return status;
}
DbgPrint("SymbolLIinkName=%wZ\n", &pdx->interfaceName);
//设置接口状态
IoSetDeviceInterfaceState(&pdx->interfaceName, TRUE);
if( !NT_SUCCESS(status))
{
if( !NT_SUCCESS(status))
{
return status;
}
}
fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
KdPrint(("Leave HelloWDMAddDevice\n"));
return STATUS_SUCCESS;
}
其实pdx->interfaceName就是暴露给应用程序的符号链接,也就是符号链
接。而这种新符号链接由以下四部分组成:
(1)第一部分是何种总线的设备,如图13-3中的设备就是根总线上的设备,即ROOT。
(2)第二部分是类设备的名称,如图13-3中的设备就是ZHANGFANDEVICE.
(3)第三部分是这种设备的第几个设备,
(4)第四部分是指定的设备接口的GUID。
在后面的讲述中可以看出,应用程序就是通过这组符号链接找到设备的。
应用程序寻找接口
在应用程序中寻找设备,是通过设备接口和设备号决定的。这里的设备号是指如果PC
中安装了两个相同驱动程序的网卡,第一 个网卡就是第0号网卡,而第二个网卡就是第1
号网卡。
在应用程序中主要是通过SetupDiXX系列函数得到设备接口,SetupDiXX用法十分灵
活,具体内容可以查看MSDN的相关资料,下面列出 了寻找接口的程序。
SetupDiGetClassDevs
用来查询与指定参数匹配的所有已安装设备
#include "stdafx.h"
#include<windows.h>
#include <stdio.h>
#include <InitGuid.h>
#include <SetupAPI.h>
#pragma comment(lib,"SetupAPI.lib")
//使用VS的生成GUID工具,选择第二种方式生成。
DEFINE_GUID(MY_WDM_DEVICE, 0xe57c50f, 0xccc, 0x4ad2, 0xa8, 0x95, 0x93, 0xc5, 0xed, 0x11, 0x98, 0x60);
HANDLE GetDeviceViaInterface(const GUID* pGuid)
{
//SetupDiGetClassDevs 通过GUID找到对应的设备接口
HDEVINFO info = SetupDiGetClassDevs(pGuid,
NULL, //总线类型 虚拟设备不用管这个值
NULL, //父窗口
DIGCF_PRESENT | //设备在系统中
DIGCF_INTERFACEDEVICE); //查找的是接口
if(info==INVALID_HANDLE_VALUE)
{
printf("No HDEVINFO available for this GUID\n");
return NULL;
}
// Get interface data for the requested instance
SP_INTERFACE_DEVICE_DATA IfData;
ifdata.cbSize = sizeof(IfData);
//枚举设备接口 一个驱动设备可以创建多个接口 插入一个相同设备就会创建一个设备接口.
// i 指定了多个设备中的其中一个设备序号 如果没有这个序号就会枚举失败
for(int i=0;;i++)
{
if(!SetupDiEnumDeviceInterfaces(info, NULL, pGuid, i, &IfData))
{
if (i==0)
{
printf("No SP_INTERFACE_DEVICE_DATA available for this GUID instance\n");
}
SetupDiDestroyDeviceInfoList(info);
return NULL;
}
DWORD ReqLen;
//获取指定接口的详细信息 主要是获得ReqLen
SetupDiGetDeviceInterfaceDetail(info, &IfData, NULL, 0, &ReqLen, NULL);
//分配符号链接名长度的内存
PSP_INTERFACE_DEVICE_DETAIL_DATA IfDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)(new char[ReqLen]);
if( IfDetail==NULL)
{
SetupDiDestroyDeviceInfoList(info);
return NULL;
}
// Get symbolic link name
ifDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
if( !SetupDiGetDeviceInterfaceDetail(info, &IfData, IfDetail, ReqLen, NULL, NULL))
{
SetupDiDestroyDeviceInfoList(info);
delete ifDetail;
return NULL;
}
printf("Symbolic link is %S\n",ifDetail->DevicePath);
}
// Open file
/*HANDLE rv = CreateFile( ifDetail->DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if( rv==INVALID_HANDLE_VALUE) rv = NULL;
delete ifDetail;
SetupDiDestroyDeviceInfoList(info);*/
return 0;
}
int main()
{
GetDeviceViaInterface(&MY_WDM_DEVICE);
getchar();
return 0;
}