一、NT式驱动的基本例程

1. 驱动入口函数——DriverEntry

  1. // 驱动程序的一般性定义
  2. NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath);
  3. //

DriverEntry的主要工作是对驱动程序进行初始化。它由系统进程System调用的。

驱动被加载的时候会创建一个驱动对象,查询此驱动程序对应的注册表项。

DriverEntry被调用的时候会传进两个参数。他们分别是刚才创建的驱动对象的指针和指向设备服务键的键名字符串指针。这个字符串的内容一般是\RESGISTRY\MACHINE\SYSTEM\ControlSet\Services\[服务名]。在驱动程序中,字符串一般以UNICODE形式存在:

注:设备服务键的键名有时候需要保存下来,当函数返回的时候此值可能会被清空。

DriverEntry等内核函数的返回值一般是NTSTATUS类型的。使用宏NT_SUCCESS(NTSTATUS status)可检测返回状态是否成功。

在DriverEntry中,一般需要做一下几件事情:

a. 设置驱动卸载例程

b. IRP的派发函数

c. 创建设备对象

其中,驱动卸载例程与IRP派发函数都是对驱动对象设置的。设备对象中有一个MajorFunction数字,用来存放哥哥IRP派发函数的函数指针。

在NT式驱动中,创建设备对象可以调用IoCreateDevice:

  1. // IoCreateDevice在WDK中的定义
  2. NTKERNELAPI
  3. NTSTATUS
  4. IoCreateDevice(
  5. __in  PDRIVER_OBJECT DriverObject,
  6. __in  ULONG DeviceExtensionSize,
  7. __in_opt PUNICODE_STRING DeviceName,
  8. __in  DEVICE_TYPE DeviceType,
  9. __in  ULONG DeviceCharacteristics,
  10. __in  BOOLEAN Exclusive,
  11. __out
  12. __drv_out_deref(
  13. __drv_allocatesMem(Mem)
  14. __drv_when((((inFunctionClass$("DRIVER_INITIALIZE"))
  15. ||(inFunctionClass$("DRIVER_DISPATCH")))),
  16. __drv_aliasesMem)
  17. __on_failure(__null))
  18. PDEVICE_OBJECT *DeviceObject
  19. );
  20. //

DriverObject:当前驱动对象指针。

DeviceExtensionSize:设备扩展的大小。IO管理器会根据这个大小在内存中创建设备扩展,并与驱动对象关联。

DeviceName:设备对象的名称。

DeviceCharacteristics:设备对象的特征。

Exclusive:设备对象是否为内核模式下使用,一般固定为TRUE。

DeviceObject:创建好的设备对象指针。

注:设备名称字符串必须是\Device\[设备名]的形式。

让用户态的应用程序能识别设备一般有两种方法:

a. 符号链接

b. 设备接口(在NT式驱动很少使用)

符号链接可以理解为设备对象在用户态的名称。即:设备名称在内核态使用,符号链接在用户态使用。

创建符号链接可以使用IoCreateSymbolicLink:

  1. // IoCreateSymbolicLink在WDK中的定义
  2. NTKERNELAPI
  3. NTSTATUS
  4. IoCreateSymbolicLink(
  5. __in PUNICODE_STRING SymbolicLinkName,
  6. __in PUNICODE_STRING DeviceName
  7. );
  8. //

SymbolicLinkName:符号链接名。

DeviceName:设备对象名。

注:在内核态,符号链接是以“\??\”或者“\DosDevices\”开头的。而在用户模式下则是以“\\.\”开头的。

设备扩展在被使用时可使用如下的代码:

  1. // Code:使用设备扩展
  2. PDEVICE_EXTENSION pDeviceExtension = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
  3. //

2. 驱动卸载例程——DriverUnload

此例程在驱动卸载的时候被调用。在NT式驱动里面负责删除在DriverEntry中创建的设备对象,并且删除与其相关联的符号链接。同时还负责某些资源的回收工作。

删除设备对象的函数是IoDeleteDevice

  1. // IoDeleteDevice在WDK中的定义
  2. NTKERNELAPI
  3. VOID
  4. IoDeleteDevice(
  5. __in __drv_mustHold(Memory) __drv_freesMem(Mem) PDEVICE_OBJECT DeviceObject
  6. );
  7. //

DeviceObject:要被删除的设备对象指针。

删除符号链接的函数是IoDeleteSymbolicLink

  1. // IoDeleteSymbolicLink在WDK中的定义
  2. NTKERNELAPI
  3. NTSTATUS
  4. IoDeleteSymbolicLink(
  5. __in PUNICODE_STRING SymbolicLinkName
  6. );
  7. //

SymbolicLinkName:已经被注册的、待删除的符号链接。

根据驱动对象可以遍历所有由该驱动创建的设备对象。通过驱动对象的DeviceObject域可以找到驱动对象的第一个设备,然后根据设备对象的NextDevice域,就可以找到设备链表中其他的设备对象。

二、WDM式驱动的基本例程

//

在WDM模型中,一个设备的操作至少需要两个设备对象共同来完成。一个是物理设备对象(Physical Device Object,简称PDO),一个是功能设备对象(Function Device Object,简称FDO)。当计算机插入某个设备的时候,总线驱动会自动创建PDO。PDO不能单独操作设备,必须与FDO一起使用。当Windows提示要安装驱动的时候,实际上安装的是WDM驱动程序,负责创建FDO,并附加到PDO上。

当一个FDO附加在PDO的时候,PDO的AttachedDevice会记录FDO的位置。PDO是底层驱动(下层驱动),FDO是高层驱动(上层驱动)。

在FDO与PDO之间还会存在过滤驱动。在FDO上面的叫做上层过滤驱动,在FDO下层的叫做下层过滤驱动。一个WDM驱动可以有很多个上层过滤驱动和下层过滤驱动。设备对象的StackSize子域表明该设备对象到最下层的物理设备中间还存在的设备对象数。

1. WDM驱动的入口函数——DriverEntry

和NT式驱动一样,WDM驱动的入口程序也是DriverEntry。但是创建设备对象的功能并不在DriverEntry中执行,而是交给了新的例程——AddDevice;同时增加了对IRP_MJ_PNP处理的派发函数。

AddDevice例程是WDM特有的,在DriverEntry中需要设置AddDevice例程的函数地址。设置的方式是在驱动对象的DriverExtension子域的AddDevice子域保存AddDevice实际例程的函数地址。AddDevice例程的名字可以随意命名。

  1. // AddDevice函数声明
  2. NTSTAUS MyAddDevice(IN PDRIVER_OBJECT pDriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject)
  3. // 设置AddDevice实际例程的地址
  4. pDriverObject->DriverExtension-> AddDevice = MyAddDevice;
  5. //

2. 驱动卸载例程——DriverUnload()

在WDM驱动中,实际的卸载工作被IRP_MN_REMOVE_DEVICE对应的派发函数处理了,这里的DriverUnload主要处理在DriverEntry中申请的内存。
 
    a. IRP_MN_REMOVE_DEVICE处理
    驱动横须内部是由IRP驱动的,IRP_MN_REMOVE_DEVICE这个IRP是当设备需要被卸载的时候,由即插即用管理器创建,并发送到驱动程序中的。IRP一般由两个号码指定该IRP的具体意义,一个是主IRP号(Major IRP),一个是辅IRP号(Minor IRP)。
    当设备需要被卸载的时候,会先后发出多个IRP_MJ_PNP。这些IRP的辅IRP号会有所不同。其中之一的IRP_MN_REMOVE_DEVICE。
    在WDM驱动程序中,对设备的卸载一般是在对IRP_MN_REMOVE_DEVICE的出口函数中进行卸载。其除了需要删除学部,取消符号链接外,还需要将FDO从PDO的堆栈中移除。需要调用IoDetachDevice:
  1. // IoDetachDevice在WDK中的定义:
  2. NTKERNELAPI
  3. VOID
  4. IoDetachDevice(
  5. __inout PDEVICE_OBJECT TargetDevice    // 下层堆栈上的设备对象
  6. );
  7. //

此时,FDO从设备链上删除,但是PDO还在。PDO由操作系统负责删除。

 
AddDevice基本步骤:
1. AddDevice通过IoCreateDevice函数创建FDO,创建FDO的符号链接
2. 在驱动设备扩展保存刚才创建的FDO的地址。
3. 调用IoAttachDeviceToDeviceStack()将FDO附加到PDO上。
  1. // IoAttachDeviceToDeviceStack在WDK中的定义
  2. NTKERNELAPI
  3. PDEVICE_OBJECT
  4. IoAttachDeviceToDeviceStack(
  5. __in __drv_mustHold(Memory) __drv_when(return!=0, __drv_aliasesMem)
  6. PDEVICE_OBJECT SourceDevice,
  7. __in PDEVICE_OBJECT TargetDevice
  8. );
  9. //
SourceDevice:FDO附加在PDO上时,这个参数代表FDO。
TargetDevice:被附加的设备。如果在FDO与PDO之间存在过滤驱动,则FDO实际上是附加在过滤驱动上的,过滤驱动则附加在PDO上。
返回值:返回SourceDevice的下层设备。
 
 
 
  1. // 根据上面的一个设备扩展定义
  2. typedef struct _DEVICE_EXTENSION
  3. {
  4. PDEVICE_OBJECT pFunctionDeviceObject;  // 设备对象(FDO)
  5. UNICODE_STRING ustrDeviceName;         // 设备名称
  6. UNICODE_STRING ustrSymbolicLinkName;   // 符号链接名
  7. PDEVICE_OBJECT pNextStackDevice;       // 下一个设备对象(FDO)的地址
  8. } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
  9. //
4. 设置设备扩展pFunctionDeviceObject->Flags。
    DO_BUFFERED_IO:缓冲内存设备
    ~DO_DEVICE_INITIALIZING:这个必须设置,表示完成Flags的初始化。
 
 
 
 
 

[Windows驱动开发](三)基础知识——驱动例程的更多相关文章

  1. Linux驱动开发1——基础知识

    1.三类驱动 字符设备驱动:字节流,/dev下有设备节点,file_operations,inode, file 块设备驱动:数据块,/dev下有设备节点,通常有文件系统 网络设备驱动:网络报文的收发 ...

  2. 背水一战 Windows 10 (78) - 自定义控件: 基础知识, 依赖属性, 附加属性

    [源码下载] 背水一战 Windows 10 (78) - 自定义控件: 基础知识, 依赖属性, 附加属性 作者:webabcd 介绍背水一战 Windows 10 之 控件(自定义控件) 自定义控件 ...

  3. android开发学习---基础知识学习、如何导入已有项目和开发一个电话拨号器

    一.基础知识点学习  1.Android体系结构 如图所示,android 架构分为三层: (1)最底层是linux内核,主要是各种硬件的驱动,如相机驱动(Camera Driver),闪存驱动(Fl ...

  4. 【千纸诗书】—— PHP/MySQL二手书网站后台开发之基础知识

    前言: 在具体回顾每一个功能的实现前,还是有必要先温习一些项目涉及到的PHP.MySQL[语法基础].项目github地址:https://github.com/66Web/php_book_stor ...

  5. ArcGIS API for JavaScript开发初探——基础知识

    1.前言 在ArcGIS Web API开发体系中一共有四大类,分别为: ArcGIS API for Flex ArcGIS API for JavaScript ArcGIS API for RE ...

  6. Java开发培训基础知识解析之反射机制

    Java是老牌编程语言,是当前应用最广泛的编程语言之一.想要学习Java你就一定要掌握Java基础知识,而反射对于初学Java的人来说绝对是非常重要的知识点.什么是反射?如何理解反射机制?如何使用反射 ...

  7. 单片机知识是Linux驱动开发的基础之一

    这是arm裸机1期加强版第1课第2节课程的wiki文字版. 为什么没前途也要学习单片机? 因为它是个很好的入口. 学习单片机可以让我们抛开复杂的软件结构,先掌握硬件操作,如:看原理图.芯片手册.写程序 ...

  8. Spring注解驱动开发(三)-----自动装配

    自动装配 概念 Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值. @Autowired-----自动注入 1.默认优先按照类型去容器中找对应的组件 application ...

  9. 【Unity3D游戏开发】基础知识之Tags和Layers (三二)[转]

    Tags和Layers分别表示是Unity引擎里面的标签和层,他们都是用来对GameObject进行标识的属性,Tags常用于单个GameObject,Layers常用于一组的GameObject.添 ...

随机推荐

  1. 在Mac系统上安装Tomcat

    到 apache官方主页 下载 Mac 版本的完整 .gz文件包.解压拷贝到 /Library目录下.   1.Mac中 Finder打开 Library的方法 新建 Finder窗口   按下 sh ...

  2. TRF7970A IC Communication Interface

    General Introduction The communication interface to the reader can be configured in two ways: with a ...

  3. Spring JdbcTemplate+JdbcDaoSupport实例

    在Spring JDBC开发中,可以使用 JdbcTemplate 和 JdbcDaoSupport 类来简化整个数据库的操作过程. 在本教程中,我们将重复上一篇文章Spring+JDBC例子,看之前 ...

  4. React事件初探

    作者:朱灵子 React 是一个 Facebook 和 Instagram 用来创建用户界面的 JavaScript 库.创造 React 是为了解决一个问题:构建随着时间数据不断变化的大规模应用程序 ...

  5. ffplay播放器移植VC的project:ffplay for MFC

    本文介绍一个自己做的FFPLAY移植到VC下的开源project:ffplayfor MFC.本project将ffmpeg项目中的ffplay播放器(ffplay.c)移植到了VC的环境下.而且使用 ...

  6. inno setup检查是否已经安装

    [Registry] Root: HKLM; Subkey: "Software\MCS"; ValueType: string; ValueName: "MCSVers ...

  7. jquery hide和show方法

    html 元素的显示和隐藏hide() 如果被选的元素已被显示,则隐藏该元素. show() 如果被选的元素已被隐藏,则显示该元素. <html> <head> <scr ...

  8. CSS:CSS+DIV布局网页

    现代网页布局:CSS+DIV: 一般的网页都是顺序布局的,很难达到我们需要的网页布局格式,此时使用DIV进行分层布局,类似于盒子,对每一部分内容进行设计.这是现在主流的网页布局方式,使用DIV+CSS ...

  9. 【转】javascript 中的很多有用的东西

    原文:https://www.cnblogs.com/ys-ys/p/5158510.html ---------------------------------------------------- ...

  10. Android 自己主动化測试(3)&lt;monkeyrunner&gt; 依据ID查找对象&amp;touch&amp;type (python)

    我在之前的两篇文章中用java来实现过 Android 自己主动化測试(1)怎样安装和卸载一个应用(java).Android 自己主动化測试(2)依据ID查找对象(java). 可是本质上都是用mo ...