以下内容全部来自《Windows驱动开发技术详解》,作者张帆、史彩成等,属摘抄型笔记。
///
1.Windows驱动程序分为两类:一类是不支持即插即用功能的NT式驱动;另一类是这hi吃即插即用功能的WDM式驱动。
2.NT式驱动程序需要导入的头文件是NTDDK.h;WDM式驱动程序要导入的头文件是WDM.h。
3.DriverFrame.h文件及解释
#pragma once
#include <NTDDK.h> //NT式驱动开发必须的头文件NTDDK.h
/************************************************************************************/
/* Windows 驱动开发中,所有程序的函数和变量都要指明被加载到分页内存中还是非分页内存中
DriverEntry函数需要放在INIT标志的内存中
INIT标志指明该函数只是在加载时需要载入内存,加载成功后即可从内存中卸载 */
/************************************************************************************/
#define PAGECODE code_seg("PAGE") //分页内存标记
#define LOCKCODE code_seg() //非分页内存标记
#define INITCODE code_seg("INIT") //初始化内存块
#define PAGEDATA data_seg("PAGE")
#define LOCKDATA data_seg()
#define INITDATA data_seg("INIT")
#define ArraySize(p) (sizeof(p)/sizeof((p)[0]))
//一个设备拓展结构体,根据不同驱动程序的需要,负责补充定义设备的相关信息
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName; //设备名
UNICODE_STRING ustrSymLinkName; //符号链接名
} DEVICE_EXTENSION, * PDEVICE_EXTENSION; //设备拓展结构
//函数声明
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject); //创建设备
VOID FrameUnload(IN PDRIVER_OBJECT pDriverObject); //驱动卸载
NTSTATUS FrameDispatch(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp);
4.驱动入口函数DriverEntry
#include "DriverFrame.h"
//指明此函数加载到INIT内存区域中,即成功卸载后,可以退出内存
#pragma INITCODE
/***********************************************************************
Windows驱动程序的入口函数不是main函数,而是一个叫做DriverEntry的函数
由内核中的I/O管理器负责调用
***********************************************************************/
/***********************************************************************
函数名称:DriverEntry
功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
参数列表:
pDriverObject:从I/O管理器中传递进来的驱动对象
pRegPath:驱动程序在注册表中的路径
返回值:返回初始化驱动状态
***********************************************************************/
NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegPath)
{
NTSTATUS status = STATUS_SUCCESS;
//打印调试信息,在调试版本中使用DbgPrint进行调试信息输出,在发行版本中,不执行任何操作
/***********************************************************************
#if DBG
#define KdPrint(_x_) DbgPrint _x_
#else
#define KdPrint(_x_)
***********************************************************************/
KdPrint(("Enter DriverEntry\n"));
//注册其他驱动调用函数入口
//驱动向I/O管理器注册一些回调函数,由操作系统负责调用
pDriverObject->DriverUnload = FrameUnload;
//驱动处理创建、读写、关闭相关IRP时,调用FrameDispatch
pDriverObject->MajorFunction[IRP_MJ_CREATE] = FrameDispatch;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = FrameDispatch;
pDriverObject->MajorFunction[IRP_MJ_READ] = FrameDispatch;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = FrameDispatch;
//创建驱动设备对象
status = CreateDevice(pDriverObject);
KdPrint(("DriverEntry End\n"));
return status;
}
5.创建设备对象函数CreateDevice
/***********************************************************************
函数名称:CreateDevice
功能描述:初始化设备对象
参数列表:
pDriverObject:从I/O管理器中传递进来的驱动对象
返回值:返回初始化状态
***********************************************************************/
#pragma INITCODE
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT pDeviceObject;
PDEVICE_EXTENSION pDeviceExt;
//创建设备名称
//构造一个UNICODE字符串,保存设备对象名称
UNICODE_STRING devName;
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&devName, L"\\Device\\DriverFrameDevice");
RtlInitUnicodeString(&symLinkName, L"\\??\\DriverFrame");
//使用IoCreateDevice创建一个设备对象
//设备对象类型为FILE_DEVICE_UNKNOWN,独占设备,只能被一个应用程序使用
status = IoCreateDevice(pDriverObject, sizeof(PDEVICE_EXTENSION),
&devName, FILE_DEVICE_UNKNOWN,
0, TRUE, &pDeviceObject);
if ( !NT_SUCCESS(status) )
{
return status;
}
pDeviceObject->Flags |= DO_BUFFERED_IO;
pDeviceExt = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
pDeviceExt->pDevice = pDeviceObject;
pDeviceExt->ustrDeviceName = devName;
//创建符号链接
//驱动程序虽然有了设备名称,但是这种设备名称只能在内核态可见
//需要暴露一个符号链接,指向真正的设备名
pDeviceExt->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&symLinkName, &devName);
if ( !NT_SUCCESS(status) )
{
IoDeleteDevice(pDeviceObject);
return status;
}
return STATUS_SUCCESS;
}
/***********************************************************************
函数名:FrameUnload
功能描述:负责驱动程序的卸载操作
参数列表:
pDriverObject:驱动对象
返回值:无
PS:驱动被卸载时由I/O管理器负责调用此回调函数
***********************************************************************/
#pragma PAGECODE
VOID FrameUnload(IN PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pNextDev;
KdPrint(("Enter FrameUnload\r\n"));
//由驱动对象得到设备对象
pNextDev = pDriverObject->DeviceObject;
//遍历驱动的所有设备对象,并进行删除
while (pNextDev != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextDev->DeviceExtension;
//删除设备对象符号链接
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName);
pNextDev = pNextDev->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
}
}
/***********************************************************************
函数名称:FrameDispatch
功能描述:对IRP进行处理
参数列表:
pDeviceObject:功能设备对象
pIrp:从I/O请求包
返回值:返回状态
PS:由于是最简单的驱动框架,所以不做任何事情
***********************************************************************/
#pragma PAGECODE
NTSTATUS FrameDispatch(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
KdPrint(("Enter FrameDispatch\n"));
//设置IRP的状态为成功
pIrp->IoStatus.Status = status;
//设置操作的字节数为0
pIrp->IoStatus.Information = 0;
//指示完成此IRP
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrint(("FrameDispatch End\n"));
return status;
}
8.使用DDK/WDK环境编译驱动程序
在源文件目录下新建两个无后缀名文本文件,分别命名为makefile和sources
a.markfile文件可以从其他驱动例子程序中拷贝,无需修改,主要内容如下:
!INCLUDE $(NTMAKEENV)\makefile.def
b.sources文件则标识了编译时的一些信息,内容如下:
TARGETNAME=DRIVERFRAME
TARGETTYPE=DRIVER
TARGETPATH=OBJ
INCLUDES=$(BASEDIR)\inc;\
$(BASEDIR)\inc\ddk;\
SOURCES=DriverFrame.c\
都是自解释的比较好理解,就不一一注释。
c.通过DDK/WDK的命令行进入到源文件所在目录,使用build命令进行编译,即可生成相关驱动及PDB文件。
9.驱动的安装
书中介绍了一种安装方式,但是已经有前人写了非常好用的工具,直接在虚拟机内安装观察是否产生问题即可。至于安装工具后续会继续学习。
PS:至于书中写道VC6下的编译,个人觉得没有必要,将VC作为一个编辑器比较合适,在命令行下编译能看到更多的信息。如果之后有需要使用VC/VS环境,再回头复习。