设备管理通过设备接口打开设备详细步骤XXXXWord下载.docx
《设备管理通过设备接口打开设备详细步骤XXXXWord下载.docx》由会员分享,可在线阅读,更多相关《设备管理通过设备接口打开设备详细步骤XXXXWord下载.docx(17页珍藏版)》请在冰豆网上搜索。
默认函数调用采用标准调用(_stdcall)
/MLd
/W3:
采用第三级警告模式
/WX:
将警告信息转换为错误信息,最大程度保证代码可靠
/Z7:
用Z7模式产生调试信息?
/Od:
关闭调试模式,VC的调试命令不能调试内核下的程序
/DWIN32=100/D_X86_=1/DWINVER=0x500/DDBG=1:
定义4个宏(不知道为什么)
/Fo"
MyDriver_Check/:
MyDriver_Check/为OutputDirectories中“创建”的文件夹,存放中间生成的目标代码路径
/Fd"
:
MyDriver_Check/为存放.PDB文件的文件夹
/FD:
生成文件依奈
/c:
只进行编译,不连接
图6
选择Link选项卡,将原有的ProjectOptions内容全部删除,替换成如下内容:
wdm.lib/nologo/base:
"
0x10000"
/stack:
0x400000,0x1000/entry:
DriverEntry"
/subsystem:
console/incremental:
no/pdb:
MyDriver_Check/GuidOpen.pdb"
/debug/machine:
I386/nodefaultlib/out:
MyDriver_Check/GuiOpen.sys"
/pdbtype:
sept/subsystem:
native/driver/SECTION:
INIT,D/IGNORE:
4078
wdm.lib:
链接WDM库
链接时不显示版本信息
/base:
:
加载驱动时,设定加载到虚拟内存的地址
/stack:
0x400000,0x1000:
设定函数使用堆栈的地址与大小
/entry:
入口函数的地址(为符合标准函数调用的)
/subsystem:
console:
设置子系统
/incremental:
no:
非递曾式链接
/pdb:
设置pdb文件的文件名为GuidOpen,保存于MyDriver_Check文件夹下面C/C++属性页中的设置一样。
/debug:
以Debug方式链接
/machine:
I386:
产生代码为386兼容的平台下的
/nodefaultlib:
不使用默认的库
/out:
MyDriver_Check/GuidOpen.sys"
输出2进制的代码的文件名,保存于MyDriver_Check文件夹下与C/C++属性页中的设置一样。
/pdbtype:
sept:
设置pdb文件的类型
native:
子系统为内核系统
/driver:
编译驱动
/SECTION:
INIT,D:
将INIT的段设置为可抛弃的
/IGNORE:
4078:
忽略4078号警告错误
图7
(5).修改VC的lib目录和include目录。
Tools->
Options->
Directories属性页下的
Showdirectoriesfor
切换到Includefie
添加DDK的头文件(安装的ddk的目录文件夹)\Inc\w2k
(ddk的目录文件夹)\Inc\ddk\wdm\w2k
置于最上面
添加库文件(安装的ddk的目录文件夹)\lib\w2k\i386
2.驱动程序说明
(1)重要驱动程序中重要的数据结构
驱动对象(DRIVER_OBJECT)在驱动加载时被内核中的对象管理程序所创建,由内核中的I/O管理器负责加载。
typedefstruct_DRIVER_OBJECT{
CSHORTType;
CSHORTSize;
PDEVICE_OBJECTDeviceObject;
ULONGFlags;
PVOIDDriverStart;
ULONGDriverSize;
PVOIDDriverSection;
PDRIVER_EXTENSIONDriverExtension;
UNICODE_STRINGDriverName;
PUNICODE_STRINGHardwareDatabase;
PFAST_IO_DISPATCHFastIoDispatch;
PDRIVER_INITIALIZEDriverInit;
PDRIVER_STARTIODriverStartIo;
PDRIVER_UNLOADDriverUnload;
PDRIVER_DISPATCHMajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1];
}DRIVER_OBJECT;
typedefstruct_DRIVER_OBJECT*PDRIVER_OBJECT;
DeviceObject:
每个驱动程序会有一个或多个设备对象。
设备对象是由程序员自己创建的,而非操作系统完成,在驱动被卸载时,遍历每个设备对象,并将其删除。
设备对象(DEVICE_OBJECT)
typedefstruct_DEVICE_OBJECT{
struct_DRIVER_OBJECT*DriverObject;
struct_DEVICE_OBJECT*NextDevice;
struct_DEVICE_OBJECT*AttachedDevice;
struct_IRP*CurrentIrp;
struct_DEVOBJ_EXTENSION*DeviceObjectExtension;
......................
}DEVICE_OBJECT;
typedefstruct_DEVICE_OBJECT*PDEVICE_OBJECT;
//ntndis
设备扩展是由程序员制定内容和大小,由I/O管理器创建的,并且保存在非分页内存中。
(2)WDM驱动程序基本结构,在WDM驱动程序中,完成一个设备操作,至少需要两个设备对象共同完成,一个是物理设备对象(PhysicalDeviceObject)PDO和功能设备对象(FunctionDeviceObject)FDO。
当PC插入一个设备时,PDO会自动创建。
确切的说是由总线驱动创建的,PDO不能单独操作设备,需要配合FDO一起使用。
系统会检测到新设备,要求安装驱动程序,需要安装的驱动程序指的就是WDM程序,此驱动程序负责创建FDO,并且附加到PDO上。
(3)驱动程序分析
头文件中,除了生命函数之外,还要定义一个设备扩展。
typedefstruct_DEVICE_EXTENSION
{
PDEVICE_OBJECTfdo;
PDEVICE_OBJECTNextStackDevice;
UNICODE_STRINGinterfaceName;
//设备接口
}DEVICE_EXTENSION,*PDEVICE_EXTENSION;
当驱动程序被加载时,首先进入DriverEntry函数。
DriverEntry主要是对驱动程序进行初始化,它是由系统进程所调用的,在Windows中有个特殊的进程叫做系统进程,打开进程管理器,里面有一个名为System的进程就是系统进程。
系统进程在系统启动的时候就被创建了。
驱动加载时,系统进程启动新的线程,调用执行体组建中的对象管理器,创建一个驱动对象。
这个驱动对象是一个DRIVER_OBJECT的结构体。
另外,系统进程调用执行体组建中的配置管理器程序,查询词驱动程序对应的注册表项。
DriverEntry函数由两个参数PDRIVER_OBJECTpDeriverObject是刚才被创建的驱动对象的指针,和PUNICODE_STRINGpRegistryPath,设备服务键的的键名字符串的指针。
在这个函数中,主要是对系统进程创建的驱动对象进行初始化。
DriverEntry函数中,它对驱动对象的初始化一般是对例程的设置,卸载例程,AddDevice和IRP派遣函数。
具体代码如下:
pDriverObject->
DriverExtension->
AddDevice=GuidOpenAddDevice;
MajorFunction[IRP_MJ_PNP]=GuidOpenPnp;
MajorFunction[IRP_MJ_DEVICE_CONTROL]=
MajorFunction[IRP_MJ_CREATE]=
MajorFunction[IRP_MJ_CLOSE]=
MajorFunction[IRP_MJ_READ]=
MajorFunction[IRP_MJ_WRITE]=GuidOpenDispatchRoutine;
DriverUnload=GuidOpenUnload;
另外,设备服务键的键名有时候需要保存下来,因为这个字符串不是长期存在的,如果以后想使用这个UNICODE字符串,就必须先把它复制到安全的地方,这个字符串的内容一般是\REGISTRY\MACHINE\SYSTEM\ControlSet\Service\[服务名].DriverEntry的返回值是NTSTATUS,是被定义的为32位的无符号长整形。
0~0X7FFFFFFF被认为是正确的。
而0X80000000~0XFFFFFFFF被认为是错误的。
接着驱动程序进入GuidOpenAddDevice例程。
它的主要任务是创建设备对象(功能设备对象)并将其附加到PDO之上。
它有两个参数PDRIVER_OBJECTDriverObject,驱动对象和PDEVICE_OBJECTPhysicalDeviceObject设备对象,就是底层总线驱动创建的PDO对象。
创建设备对象,用IoCreatDevice(
INPDRIVER_OBJECTDriverObject,//系统创建的驱动对象
INULONGDeviceExtensionSize,//程序员自己定义的(在头文件中)设备扩展的大小
INPUNICODE_STRINGDeviceNameOPTIONAL,//设备名,可以为空,此时,I/O管理器会自动以一个数字作为该设备对象的名称
INDEVICE_TYPEDeviceType,//设备类型
INULONGDeviceCharacteristics,//对设备的进一步描述
INBOOLEANExclusize,//是否一次只能进行一次IRP处理
OUTPDEVICE_OBJECT*DeviceObject)//新创建的设备对象
具体代码如下
PDEVICE_OBJECTfdo;
status=IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,//没有指定设备名
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&
fdo);
此时,功能设备对象,创建完毕,接着是将,功能设备对象FDO附加到物理设备对象上PDO。
利用函数PDEVICE_OBJECTIoAttachDeviceToDeviceStack(
INPDEVICE_OBJECTSourceDevice,//新创建的功能设备对象FDO
INPDEVICE_OBJECTTargetDevice//物理设备对象PDO
);
该函数调用成功的话返回一个设备对象,附加设备对象的设备对象,例如在这里,返回的是PDO,如果,ThereturneddeviceobjectpointercandifferfromTargetDeviceifTargetDevicehadadditionaldriverslayeredontopofit.如果该函数运行失败,则返回NULL。
具体程序,我们将该函数返回的设备对象,保存在,设备扩展中,这样一来,我们就需要先得到新创建的功能设备对象的设备扩展。
//得到设备扩展
PDEVICE_EXTENSIONpdx=(PDEVICE_EXTENSION)fdo->
DeviceExtension;
pdx->
fdo=fdo;
//将FDO附加到PDO上
pdx->
NextStackDevice=IoAttachDeviceToDeviceStack(fdo,PhysicalDeviceObject);
WDM驱动程序,设备名无法被用户模式下的应用程序查询到,应用程序可以通过符号链接,设备名或者是设备接口来访问设备。
本文只介绍设备接口方式。
设备接口就是一组全局标志,他是一个128位组成的数字,并能保证在全世界范围内不会冲突。
VC中有一个创建GUID的工具,叫guidgen.exe,它在D:
\ProgramFiles\MicrosoftVisualStudio\Common\Tools中,运行它,它为用户提供了四种方式产生guid,其实它们都是128位的,只是输出的形式不同而已,一般选择第二种,单击NewGUID会产生新的的guid,单击Copy将这个guid复制到新建的guid.h头文件中。
DEFINE_GUID(<
<
name>
>
0x5dada759,0xde9a,0x45e2,0x8f,0xb4,0x1a,0xa8,0x8b,0x1d,0xe7,0x8);
需要将《name》换成自己为这个接口而起的名字,例如MY_WDM_DEVICE.另外需要注意,在创建guid时,程序中应该包含头文件#include<
initguid.h>
,其中initguid.h只能在一个.cpp中包含,DEFINE_GUID()宏定义了,如果包含了一个initguid.h,那么定义一个guid,如果没有包含一个initguid.h,则定义一个externguid指向定义的那个guid,所以项目中必须有一个文件.cpp包含initguid.h,否则会出错。
但如果包含了多个的initguid.h,也会出错否则会出现错误。
unresolvedexternalsymbol_MY_WDM_DEVICE
创建设备接口用函数NTSTATUS
IoRegisterDeviceInterface(
INPDEVICE_OBJECTPhysicalDeviceObject,
INCONSTGUID*InterfaceClassGuid,
INPUNICODE_STRINGReferenceStringOPTIONAL,
OUTPUNICODE_STRINGSymbolicLinkName//将GUID输出一串UNICODE字符串
具体代码创建设备接口
status=IoRegisterDeviceInterface(PhysicalDeviceObject,&
MY_WDM_DEVICE,NULL,&
interfaceName);
其中pdx->
interfaceName就是暴露给应用程序的符号链接。
包括四部分如图8
图8
(1)何种总线设备,例如ROOT
(2)类设备的名称,LIUYOUJINDEVICE
(3)这种设备的第几个设备#0000
(4)制定的设备接口GUID。
设置接口
IoSetDeviceInterfaceState(&
interfaceName,TRUE);
//设置操作模式
fdo->
Flags|=DO_BUFFERED_IO|DO_POWER_PAGABLE;
fdo->
Flags&
=~DO_DEVICE_INITIALIZING;
实现即插即用
即插即用IRP即IRP_MJ_PNP,它一般是由即插即用管理器发送给WDM驱动程序的。
不同情况下,即插即用管理器会发送不同子类型的IRP_MJ_PNPIRP。
在IRP_MJ_PNP派遣函数中要处理不同子功能代码的IRP,本程序采用函数指针的方法。
首先初始化一个函数指针组成的数组,然后在派遣函数中判断是那种子功能代码。
根据这个子功能代码区寻找行的函数指针,再通过指针找到针对具体子功能代码所作的操作函数。
加载驱动时,所用到的各个IRP_MJ_PNP子功能代码。
1.953DefaultEnterDefaultPnpHandler
11.953DefaultLeaveDefaultPnpHandler
11.953DefaultEnterGuidOpenPnp
11.953DefaultPNPRequest(IRP_MN_FILTER_RESOURCE_REQUIREMENTS)修改I/O资源需求列表
11.953DefaultEnterDefaultPnpHandler
11.953DefaultLeaveGuidOpenPnp
11.953DefaultPNPRequest(IRP_MN_START_DEVICE)配置并初始化设备
11.953DefaultEnterHandleStartDevice
11.953DefaultLeaveHandleStartDevice
11.953DefaultPNPRequest(IRP_MN_QUERY_CAPABILITIES)取设备能力
11.953DefaultPNPRequest(IRP_MN_QUERY_PNP_DEVICE_STATE)取设备状态
11.953DefaultPNPRequest(IRP_MN_QUERY_DEVICE_RELATIONS)给出与制定特征相关的设备列表
对IRP_MN_DEVICE的处理
3.应用程序说明
创建一个对话框类型的驱动程序框架。
(1)首先需要将驱动程序中的guid.h文件copy到应用程序中,并且添加到应用程序的工程里。
(2)应用程序的Porject|Setting|Link的Object/librarymodules里要添加setupapi.lib库,否则会有类似这样的错误:
GuidOpen_AppDlg.obj:
unresolvedexternalsymbol__imp__SetupDiGetDeviceInterfaceDetailA@24
errorLNK2001:
unresolvedexternalsymbol__imp__SetupDiDestroyDeviceInfoList@4
unresolvedexternalsymbol__imp__SetupDiEnumDeviceInterfaces@20
unresolvedexternalsymbol__imp__SetupDiGetClassDevsA@16
程序中应该包含setupapi.h头文件,否则,会出现这样的错误:
errorC2065:
'
HDEVINFO'
:
undeclaredidentifier
errorC2146:
syntaxerror:
missing'
;
'
beforeidentifier'
info'
error