通过设备接口打开设备详细步骤 SS.docx
- 文档编号:10707886
- 上传时间:2023-02-22
- 格式:DOCX
- 页数:28
- 大小:25.82KB
通过设备接口打开设备详细步骤 SS.docx
《通过设备接口打开设备详细步骤 SS.docx》由会员分享,可在线阅读,更多相关《通过设备接口打开设备详细步骤 SS.docx(28页珍藏版)》请在冰豆网上搜索。
通过设备接口打开设备详细步骤SS
通过设备接口打开设备详细步骤
通过设备接口打开设备
骤 (XXXX329)
详细步
通过设备接口打开设备详细步骤
一、驱动程序
1.驱动程序框架的创建
( 1)用 VC 建立一个新工程。
在 VC IDE 环境中选择
File|New , 弹 出
New 对话框。
在
对话框中,选择
Project 选项卡。
在 Project 选项卡
中,选择Win32
Application。
设
置 工 程 名 为
OpenGuid. 如图 1 所示,单击 OK ,进入下一个对话框,
在对话框中选择一个空的工程。
如图2。
1
图 1
图 2
( 2).新建两
个文件
GuidOpen.h
和
GuidOpen.c
pp. 这两个文
件的具体写
法,详见程序编写。
也可以直接添加现成的已经写好的
文件,张帆这本书中,一般都是用的HelloWDM.h 和
HelloWDM.cpp.
( 3).增加新的编译版本,去掉Debug 和 Release 版本。
在 Build |Configuration 如图 3 和图 4。
2
图 3
图 4
( 4).修改工程属性。
选择 Project|Setting ,在弹出的对
3
话框中,选择 General 选项卡,将 Intermediate files 和
Output files 改为 MyDriver_Check ,这个名字英语
C/C++ 中, 所设置的 Fo 和 Fd 后面的文件名相一致 。
如
图 5。
图 5
将 C/C++ 选项卡中,原有的 Project Options 内容全部删
掉,换成一下内容。
/nologo /Gz /MLd /W3 /WX /Z7 /Od /D WIN32=100
/D_X86_=1 /D WINVER=0x500 /D DBG=1
/Fo"MyDriver_Check/" /Fd"MyDriver_Check/"/FD /c
其中:
/nologo :
表示不显示编译的版本信息
/Gz :
默认函数调用采用标准调用(_stdcall )
/MLd
/W3:
采用第三级警告模式
/WX :
将警告信
息转换为错误信
息,最大程度保
证代码可靠
/Z7 :
用 Z7 模式
产生调试信息?
4
/Od :
关闭调试模式, VC 的调试命令不能调试内核下的
程序
/D WIN32=100 /D_X86_=1 /D WINVER=0x500 /D
DBG=1 :
定义 4 个宏(不知道为什么)
/Fo"MyDriver_Check/:
MyDriver_Check/ 为 Output
Directories 中 “创建 ”的文件夹,存放中间生成的目标代
码路径
/Fd"MyDriver_Check/":
MyDriver_Check/为存放 .PDB
文件的文件夹
/FD :
生成文件依奈
/c:
只进行编译,不连接
图 6
选择 Link 选项卡 ,
将原有的 Project Options内容全部删除 ,替换成如下内
容:
wdm.lib /nologo /base:
"0x10000"
/stack:
0x400000,0x1000 /entry:
"DriverEntry"
/subsystem:
console /incremental:
no
/pdb:
"MyDriver_Check/GuidOpen.pdb"/debug
/machine:
I386 /nodefaultlib
5
/out:
" MyDriver_Check/GuiOpen.sys"/pdbtype:
sept
/subsystem:
native /driver /SECTION:
INIT,D
/IGNORE:
4078
其中:
wdm.lib :
链接 WDM 库
/nologo :
链接时不显示版本信息
/base:
"0x10000" :
加载驱动时,设定加载到虚拟内存的
地址
/stack:
0x400000,0x1000:
设定函数使用堆栈的地址与大
小
/entry:
"DriverEntry":
入口函数的地址 (为符合标准函
数调用的)
/subsystem:
console:
设置子系统
/incremental:
no :
非递曾式链接
/pdb:
"MyDriver_Check/GuidOpen.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 文件的类型
/subsystem:
native :
子系统为内核系统
/driver :
编译驱动
/SECTION:
INIT,D:
将 INIT 的段设置为可抛弃的
/IGNORE:
4078 :
忽略 4078 号警告错误
6
图 7
( 5) .修改
VC 的 lib 目
录和 include
目录。
Tools->Opti
ons->Direct
ories 属性页
下的
Show directories for
切换到 Includefie
添加DDK的头文件( 安装的 ddk 的目录文件夹 )
\ Inc \ w2k
(ddk 的目录文件夹 )\ Inc \
ddk\wdm\w2k
置于最上面
添加库文件 (安装的 ddk 的目录文件
7
夹 )\lib\w2k\i386
置于最上面
2.驱动程序说明
(1) 重要驱动程序中重要的数据结构
驱动对象 ( DRIVER_OBJECT )在驱动加载时被内核中
的对象管理程序所创建,由内核中的I/O 管理器负责加
载。
typedef struct _DRIVER_OBJECT{
CSHORT Type;
CSHORT Size;
PDEVICE_OBJECT DeviceObject;
ULONG Flags;
PVOID DriverStart;
ULONG DriverSize;
PVOID DriverSection;
PDRIVER_EXTENSION DriverExtension;
UNICODE_STRING DriverName;
PUNICODE_STRING HardwareDatabase;
PFAST_IO_DISPATCH FastIoDispatch;
PDRIVER_INITIALIZE DriverInit;
PDRIVER_STARTIO DriverStartIo;
PDRIVER_UNLOAD DriverUnload;
PDRIVER_DISPATCH
MajorFunction[IRP_MJ_MAXIMUM_FUNCTION+1];
} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT
*PDRIVER_OBJECT;
DeviceObject:
每个驱动程序会有一个或多个设备对象。
设备对象是由程序员自己创建的,而非操作系统完成,
在驱动被卸载时,遍历每个设备对象,并将其删除。
8
设备对象( DEVICE_OBJECT )
typedef struct _DEVICE_OBJECT{
struct_DRIVER_OBJECT
*DriverObject;
struct_DEVICE_OBJECT *NextDevice;
struct_DEVICE_OBJECT
*AttachedDevice;
struct_IRP *CurrentIrp;
ULONG Flags;
struct _DEVOBJ_EXTENSION
*DeviceObjectExtension;
......................
} DEVICE_OBJECT;
typedef struct _DEVICE_OBJECT
*PDEVICE_OBJECT;//ntndis
设备扩展 是由程序员制定内容和大小, 由 I/O 管理器创
建的,并且保存在非分页内存中。
( 2)WDM 驱动程序基本结构, 在 WDM 驱动程序中,
完成一个设备操作,至少需要两个设备对象共同完成,
一个是物理设备对象( Physical Device Object )PDO 和
功能设备对象( Function Device Object ) FDO 。
当 PC 插入一个设备时, PDO 会自动创建。
确切的说是
由总线驱动创建的, PDO 不能单独操作设备, 需要配合
FDO 一起使用。
系统会检测到新设备, 要求安装驱动程
序,需要安装的驱动程序指的就是WDM 程序,此驱动
程序负责创建 FDO ,并且附加到 PDO 上。
( 3)驱动程序分析
头文件中,除了生命函数之外,还要定义一个设
备扩展。
9
typedef struct
_DEVICE_EXTENSION
{
PDEVICE_OBJECT fdo;
PDEVICE_OBJECT NextStackDevice;
UNICODE_STRING interfaceName;// 设备接口
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
当驱动程序被加载时,首先进入DriverEntry函
数。
DriverEntry主要是对驱动程序进行初始化,它是
由系统进程所调用的, 在 Windows 中有个特殊的进程叫
做系统进程, 打开进程管理器, 里面有一个名为 System
的进程就是系统进程。
系统进程在系统启动的时候就被
创建了。
驱动加载时,系统进程启动新的线程,调用执
行体组建中的对象管理器,创建一个驱动对象。
这个驱
动对象是一个 DRIVER_OBJECT的结构体。
另外,系
统进程调用执行体组建中的配置管理器程序,查询词驱
动程序对应的注册表项。
DriverEntry函数由两个参数 PDRIVER_OBJECT
pDeriverObject是刚才被创建的驱动对象的指针,和
PUNICODE_STRING pRegistryPath,设备服务键的的
键名字符串的指针。
在这个函数中,主要是对系统进程
创建的驱动对象进行初始化。
DriverEntry函数中,它对驱动对象的初始化一
般是对例程的设置,卸载例程,AddDevice 和 IRP 派遣
函数。
具体代码如下:
pDriverObject->DriverExtension->AddDevice =
GuidOpenAddDevice;
10
pDriverObject->MajorFunction[IRP_MJ_PNP] =
GuidOpenPnp;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_
CONTROL] =
pDriverObject->MajorFunction[IRP_MJ_CREATE]
=
pDriverObject->MajorFunction[IRP_MJ_CLOSE] =
pDriverObject->MajorFunction[IRP_MJ_READ] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] =
GuidOpenDispatchRoutine;
pDriverObject->DriverUnload = GuidOpenUnload;
另外,设备服务键的键名有时候需要保存下来,
因为这个字符串不是长期存在的,如果以后想使用这个
UNICODE字符串,就必须先把它复制到安全的地方,
这个字符串的内容一般是
\REGISTRY\MACHINE\SYSTEM\ControlSet\Service\[
服务名 ].DriverEntry的返回值是 NTSTATUS ,是被定
义的为 32 位的无符号长整形。
0~0X7FFFFFFF 被认为
是正确的。
而 0X80000000~0XFFFFFFFF 被认为是错误
的。
接着驱动程序进入 GuidOpenAddDevice 例程。
它的主要任务是创建设备对象(功能设备对象)并将其
附加到 PDO 之上。
它有两个参数PDRIVER_OBJECT
DriverObject, 驱动对象和 PDEVICE_OBJECT
PhysicalDeviceObject 设备对象,就是底层总线驱动创
建的 PDO 对象。
创建设备对象,用 IoCreatDevice (
11
IN PDRIVER_OBJECT
DriverObject,// 系统创建的
驱动对象
IN ULONGDeviceExtensionSize,// 程序员自己定义
的( 在头文件中 )设备扩展的大小
IN PUNICODE_STRINGDeviceName
OPTIONAL,// 设备名,可以为空,此时, I/O 管理器会
自动以一个数字作为该设备对象的名称
IN DEVICE_TYPEDeviceType,// 设备类型
IN ULONGDeviceCharacteristics,// 对设备的进一步
描述
IN BOOLEANExclusize,// 是否一次只能进行一次
IRP 处理
OUT PDEVICE_OBJECT*DeviceObject )// 新创建的
设备对象
具体代码如下
PDEVICE_OBJECT fdo;
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
NULL,// 没有指定设备名
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo);
此时,功能设备对象,创建完毕,接着是将,功能设备
对象 FDO 附加到物理设备对象上PDO 。
利用函数
PDEVICE_OBJECTIoAttachDeviceToDeviceStack(
IN PDEVICE_OBJECT
12
SourceDevice,//新创建的功能设备对象FDO
IN PDEVICE_OBJECT
TargetDevice// 物理设备对象 PDO
);
该函数调用成功的话返回一个设备对象,附加设备对象
的设备对象,例如在这里,返回的是PDO ,如果, The
returned device object pointer can differ from
TargetDevice if TargetDevice had additional drivers
layered on top of it. 如果该函数运行失败,则返回
NULL 。
具体程序,我们将该函数返回的设备对象,保存在,设
备扩展中,这样一来,我们就需要先得到新创建的功能
设备对象的设备扩展。
// 得到设备扩展
PDEVICE_EXTENSION pdx =
(PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
// 将 FDO 附加到 PDO 上
pdx->NextStackDevice =
IoAttachDeviceToDeviceStack(fdo,
PhysicalDeviceObject);
WDM 驱动程序,设备名无法被用户模式下的应用程序
查询到,应用程序可以通过符号链接,设备名或者是设
备接口来访问设备。
本文只介绍设备接口方式。
设备接
口就是一组全局标志,他是一个128 位组成的数字,并
能保证在全世界范围内不会冲突。
VC 中有一个创建
GUID 的工具,叫 guidgen.exe,它在 D:
\Program
Files\Microsoft Visual Studio\Common\Tools中,运行
它,它为用户提供了四种方式产生guid ,其实它们都是
13
128 位的, 只是输出的形式不同而已,一般选择第二种,
单击 New GUID 会产生新的的 guid ,单击 Copy 将这个
guid 复制到新建的 guid.h 头文件中。
DEFINE_GUID(<
0x5dada759, 0xde9a, 0x45e2, 0x8f, 0xb4, 0x1a,
0xa8, 0x8b, 0x1d, 0xe7, 0x8);
需要将《 name》换成自己为这个接口而起的名字,例如
MY_WDM_DEVICE.另外需要注意,在创建guid 时,
程序中应该包含头文件 #include
initguid.h 只能在一个 .cpp 中包含, DEFINE_GUID ()
宏定义了,如果包含了一个initguid.h ,那么定义一个
guid, 如果没有包含一个 initguid.h , 则定义一个 extern
guid 指向定义的那个 guid, 所以项目中必须有一个文
件 .cpp 包含 initguid.h, 否则会出错。
但如果包含了多个
的 initguid.h ,也会出错否则会出现错误。
unresolved external symbol _MY_WDM_DEVICE
创建设备接口用函数NTSTATUS
IoRegisterDeviceInterface(
IN PDEVICE_OBJECTPhysicalDeviceObject,
IN CONST GUID*InterfaceClassGuid,
IN PUNICODE_STRINGReferenceString
OPTIONAL,
OUT PUNICODE_STRING
SymbolicLinkName// 将 GUID 输出一串 UNICODE 字符
串
);
具体代码创建设备接口
status =
IoRegisterDeviceInterface(PhysicalDeviceObject,
14
&MY_WDM_DEVICE,NULL, &pdx->interfaceName);
其中 pdx->interfaceName 就是暴露给应用程序的符号
链接。
包括四部分如图8
图 8
( 1)何种总线设备,例如ROOT
( 2)类设备的名称, LIUYOUJINDEVICE
( 3)这种设备的第几个设备 #0000
( 4)制定的设备接口 GUID 。
设置接口
IoSetDeviceInterfaceState(&pdx->interfaceName,
TRUE);
// 设置操作模式
fdo->Flags |= DO_BUFFERED_IO |
DO_POWER_PAGABLE;
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
实现即插即用
即插即用 IRP 即 IRP_MJ_PNP ,它一般是由即插
即用管理器发送给 WDM 驱动程序的 。
不同情况下,即
插即用管理器会发送不同子类型的IRP_MJ_PNP
IRP 。
在 IRP_MJ_PNP 派遣函数中要处理不同子功能代
码的 IRP ,本程序采用函数指针的方法。
首先初始化一
个函数指针组成的数组,然后在派遣函数中判断是那种
子功能代码。
根据这个子功能代码区寻找行的函数指针,
15
再通过指针找到针对具体子功能代码所作的操作函数。
加载驱动时,所用到的各个IRP_MJ_PNP 子功能代码。
1.953DefaultEnter DefaultPnpHandler
11.953DefaultL eave DefaultPnpHandler
11.953DefaultEnter GuidOpenPnp
11.953DefaultPNP Request
(IRP_MN_FILTER_RESOURCE_REQUIREMENTS)
修改 I/O 资源需求列表
11.953DefaultEnter DefaultPnpHandler
11.953DefaultL eave DefaultPnpHandler
11.953DefaultL eave GuidOpenPnp
11.953DefaultEnter GuidOpenPnp
11.953DefaultPNP Request
(IRP_MN_START_DEVICE)配置并初始化设备
11.953DefaultEnter HandleStartDevice
11.953DefaultEnter DefaultPnpHandler
11.953DefaultL eave DefaultPnpHandler
11.953DefaultL eave HandleStartDevice
11.953DefaultL eave GuidOpenPnp
11.953DefaultEnter GuidOpenPnp
11.953DefaultPNP Request
(IRP_MN_QUERY_CAPABILITIES)取设备能力
11.953DefaultEnter DefaultPnpHandler
11.953DefaultL eave DefaultPnpHandler
11.953DefaultL eave GuidOpenPnp
11.953DefaultEnter GuidOpenPnp
11.953DefaultPNP Request
(IRP_MN_QUERY_PNP_DEVICE_STATE)取设备状
态
11.953DefaultEnter DefaultPnpHandler
16
11.953DefaultL eave DefaultPnpHandler
11.953DefaultL eave GuidOpenPnp
11.953DefaultEnter GuidOpenPnp
11.953DefaultPNP Request
(IRP_MN_QUERY_DEVICE_RELATIONS)给出与制
定特征相关的设备列表
11.953DefaultEnter DefaultPnpHandler
11.953DefaultL eave DefaultPnpHandler
11.953DefaultL eave GuidOpenPnp
对 IRP_MN_DEVICE的处理
3.应用程序说明
创建一个对话框类型的驱动程序框架。
( 1)首先需要将
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 通过设备接口打开设备详细步骤 SS 通过 设备 接口 打开 详细 步骤