4. WDM驱动开发示例
4.1 项目属性配置
1. 打开VS2008,新建一个Visual C++ à Win32 à win32空项目。例如WdmDemo。2. 打开VS2008 的“生成”菜单中的“配置管理器”选项。在活动解决方案配置中选择《新建》,新建一个Check空的解决方案配置。
3. 在解决方案管理器中,新建一个WdmDemo.h头文件,一个WdmDemo.cpp源文件。
4. 打开VS2008的“项目”菜单里面“属性”选项。即打开WdmDemo项目属性页。在项目属性页选择“配置属性”,打开十字图标。
5. 选择“C/C++”并展开内部选项。
在“常规”选项中,在“附加包含目录”中添加wdk 引用头文件目录。并去除“从父级或项目默认设置继承”复选框的勾选。Wdk头文件目录如下:
D:/WinDDK/7600.16385.0/inc/api
D:/WinDDK/7600.16385.0/inc/crt
D:/WinDDK/7600.16385.0/inc/ddk
注意笔者的WDK安装目录在D盘。
在“调试信息格式”中选择 “C7 兼容(/Z7)”选项。
在“警告等级”中选择“3级(/W3)”。
在“将警告视为错误”中选择“是(/WX)”。
在“优化”选项中,在“优化”中选择“禁用(/Od)”。
在“预处理器”选项中,在“预处理器定义”中添加 “WIN32=100;_X86_=1;WINVER=0x501;DBG=1”。并去除“从父级或项目默认属性继承”复选框的勾选。
在“高级”选项中,选择“调用约定”为“__stdcall (/Gz)”。
6. 选择“链接器”并展开内部选项。
在“常规”选项中,修改“输出文件”的文件扩展名为 .sys 添加“附加库目录”
D:/WinDDK/7600.16385.0/lib/Crt/i386
D:/WinDDK/7600.16385.0/lib/wxp/i386
并去除“从父级或项目默认设置继承”复选框的勾选。
在“输入”选项中,添加“附加依赖项”ntoskrnl.lib并去除“从父级或项目默认设置继承”复选框的勾选。
在“清单文件”选项中,选择“生成清单”为否。
在“调试”选项中,选择“生成调试信息”为“是 (/Debug)”。
在“系统”选项中,选择“子系统”为“本机 (/SUBSYSTEM:NATIVE)”。选择“驱动程序”为“WDM (/DRIVER:WDM)”。
在“高级”中,添加“入口点”为DriverEntry。选择“随即基址”为“默认值”。选择“数据执行保护”为默认值。选择“目标计算机”为“MachineX86(/MACHINE:X86)”。
在“命令行”选项中,添加“/SECTION:INIT,D /IGNORE:4078”。
4.2 示例代码简介
/************************************************************************
* 文件名称:HelloWDM.h
* 作 者:张帆
* 完成日期:2007-11-1
*************************************************************************/
#ifdef __cplusplus
extern "C"
{
#endif
#include <wdm.h>
#ifdef __cplusplus
}
#endif
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT fdo;
PDEVICE_OBJECT NextStackDevice;
UNICODE_STRING ustrDeviceName; // 设备名
UNICODE_STRING ustrSymLinkName; // 符号链接名
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
#define PAGEDCODE code_seg("PAGE")
#define LOCKEDCODE code_seg()
#define INITCODE code_seg("INIT")
#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")
#define arraysize(p) (sizeof(p)/sizeof((p)[0]))
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject);
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
IN PIRP Irp);
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,
IN PIRP Irp);
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject);
extern "C"
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath);
/************************************************************************
* 文件名称:HelloWDM.cpp
* 作 者:张帆
* 完成日期:2007-11-1
*************************************************************************/
#include "WdmDemo.h"
/************************************************************************
* 函数名称:DriverEntry
* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
* 参数列表:
pDriverObject:从I/O管理器中传进来的驱动对象
pRegistryPath:驱动程序在注册表的中的路径
* 返回值:返回初始化驱动状态
*************************************************************************/
#pragma INITCODE
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath)
{
KdPrint(("Enter DriverEntry/n"));
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
pDriverObject->MajorFunction[IRP_MJ_CREATE] =
pDriverObject->MajorFunction[IRP_MJ_READ] =
pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;
pDriverObject->DriverUnload = HelloWDMUnload;
KdPrint(("Leave DriverEntry/n"));
return STATUS_SUCCESS;
}
/************************************************************************
* 函数名称:HelloWDMAddDevice
* 功能描述:添加新设备
* 参数列表:
DriverObject:从I/O管理器中传进来的驱动对象
PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象
* 返回值:返回添加新设备状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMAddDevice/n"));
NTSTATUS status;
PDEVICE_OBJECT fdo;
UNICODE_STRING devName;
RtlInitUnicodeString(&devName,L"//Device//MyWDMDevice");
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)devName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&fdo);
if( !NT_SUCCESS(status))
return status;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
pdx->fdo = fdo;
pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&symLinkName,L"//DosDevices//HelloWDM");
pdx->ustrDeviceName = devName;
pdx->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);
if( !NT_SUCCESS(status))
{
IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
status = IoCreateSymbolicLink(&symLinkName,&devName);
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;
}
/************************************************************************
* 函数名称:DefaultPnpHandler
* 功能描述:对PNP IRP进行缺省处理
* 参数列表:
pdx:设备对象的扩展
Irp:从IO请求包
* 返回值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter DefaultPnpHandler/n"));
IoSkipCurrentIrpStackLocation(Irp);
KdPrint(("Leave DefaultPnpHandler/n"));
return IoCallDriver(pdx->NextStackDevice, Irp);
}
/************************************************************************
* 函数名称:HandleRemoveDevice
* 功能描述:对IRP_MN_REMOVE_DEVICE IRP进行处理
* 参数列表:
fdo:功能设备对象
Irp:从IO请求包
* 返回值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HandleRemoveDevice/n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
NTSTATUS status = DefaultPnpHandler(pdx, Irp);
IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName);
//调用IoDetachDevice()把fdo从设备栈中脱开:
if (pdx->NextStackDevice)
IoDetachDevice(pdx->NextStackDevice);
//删除fdo:
IoDeleteDevice(pdx->fdo);
KdPrint(("Leave HandleRemoveDevice/n"));
return status;
}
/************************************************************************
* 函数名称:HelloWDMPnp
* 功能描述:对即插即用IRP进行处理
* 参数列表:
fdo:功能设备对象
Irp:从IO请求包
* 返回值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo,
IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMPnp/n"));
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
static NTSTATUS (*fcntab[])(PDEVICE_EXTENSION pdx, PIRP Irp) =
{
DefaultPnpHandler, // IRP_MN_START_DEVICE
DefaultPnpHandler, // IRP_MN_QUERY_REMOVE_DEVICE
HandleRemoveDevice, // IRP_MN_REMOVE_DEVICE
DefaultPnpHandler, // IRP_MN_CANCEL_REMOVE_DEVICE
DefaultPnpHandler, // IRP_MN_STOP_DEVICE
DefaultPnpHandler, // IRP_MN_QUERY_STOP_DEVICE
DefaultPnpHandler, // IRP_MN_CANCEL_STOP_DEVICE
DefaultPnpHandler, // IRP_MN_QUERY_DEVICE_RELATIONS
DefaultPnpHandler, // IRP_MN_QUERY_INTERFACE
DefaultPnpHandler, // IRP_MN_QUERY_CAPABILITIES
DefaultPnpHandler, // IRP_MN_QUERY_RESOURCES
DefaultPnpHandler, // IRP_MN_QUERY_RESOURCE_REQUIREMENTS
DefaultPnpHandler, // IRP_MN_QUERY_DEVICE_TEXT
DefaultPnpHandler, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS
DefaultPnpHandler, //
DefaultPnpHandler, // IRP_MN_READ_CONFIG
DefaultPnpHandler, // IRP_MN_WRITE_CONFIG
DefaultPnpHandler, // IRP_MN_EJECT
DefaultPnpHandler, // IRP_MN_SET_LOCK
DefaultPnpHandler, // IRP_MN_QUERY_ID
DefaultPnpHandler, // IRP_MN_QUERY_PNP_DEVICE_STATE
DefaultPnpHandler, // IRP_MN_QUERY_BUS_INFORMATION
DefaultPnpHandler, // IRP_MN_DEVICE_USAGE_NOTIFICATION
DefaultPnpHandler, // IRP_MN_SURPRISE_REMOVAL
};
ULONG fcn = stack->MinorFunction;
if (fcn >= arraysize(fcntab))
{ // 未知的子功能代码
status = DefaultPnpHandler(pdx, Irp); // some function we don't know about
return status;
}
#if DBG
static char* fcnname[] =
{
"IRP_MN_START_DEVICE",
"IRP_MN_QUERY_REMOVE_DEVICE",
"IRP_MN_REMOVE_DEVICE",
"IRP_MN_CANCEL_REMOVE_DEVICE",
"IRP_MN_STOP_DEVICE",
"IRP_MN_QUERY_STOP_DEVICE",
"IRP_MN_CANCEL_STOP_DEVICE",
"IRP_MN_QUERY_DEVICE_RELATIONS",
"IRP_MN_QUERY_INTERFACE",
"IRP_MN_QUERY_CAPABILITIES",
"IRP_MN_QUERY_RESOURCES",
"IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
"IRP_MN_QUERY_DEVICE_TEXT",
"IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
"",
"IRP_MN_READ_CONFIG",
"IRP_MN_WRITE_CONFIG",
"IRP_MN_EJECT",
"IRP_MN_SET_LOCK",
"IRP_MN_QUERY_ID",
"IRP_MN_QUERY_PNP_DEVICE_STATE",
"IRP_MN_QUERY_BUS_INFORMATION",
"IRP_MN_DEVICE_USAGE_NOTIFICATION",
"IRP_MN_SURPRISE_REMOVAL",
};
KdPrint(("PNP Request (%s)/n", fcnname[fcn]));
#endif // DBG
status = (*fcntab[fcn])(pdx, Irp);
KdPrint(("Leave HelloWDMPnp/n"));
return status;
}
/************************************************************************
* 函数名称:HelloWDMDispatchRoutine
* 功能描述:对缺省IRP进行处理
* 参数列表:
fdo:功能设备对象
Irp:从IO请求包
* 返回值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,
IN PIRP Irp)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMDispatchRoutine/n"));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0; // no bytes xfered
IoCompleteRequest( Irp, IO_NO_INCREMENT );
KdPrint(("Leave HelloWDMDispatchRoutine/n"));
return STATUS_SUCCESS;
}
/************************************************************************
* 函数名称:HelloWDMUnload
* 功能描述:负责驱动程序的卸载操作
* 参数列表:
DriverObject:驱动对象
* 返回值:返回状态
*************************************************************************/
#pragma PAGEDCODE
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{
PAGED_CODE();
KdPrint(("Enter HelloWDMUnload/n"));
KdPrint(("Leave HelloWDMUnload/n"));
}
4.3 安装调试示例
WDM驱动程序安装需要一个.inf安装文件来支持其安装,其实它只是一个文本文件。如果我们需要创建一个这样的文件,只需要新建一个文本文件,然后更改扩展名为.inf即可。WDK开发驱动程序包中有个CheckInf的控制台的调试工具可以帮助我们检查inf文件的语法错误。inf示例如下:
;--------- Version Section ---------------------------------------------------
[Version]
Signature="$CHICAGO$";
Provider=Zhangfan_Device
DriverVer=11/1/2007,3.0.0.3
; If device fits one of the standard classes, use the name and GUID here,
; otherwise create your own device class and GUID as this example shows.
Class=ZhangfanDevice
ClassGUID={EF2962F0-0D55-4bff-B8AA-2221EE8A79B0}
;--------- SourceDiskNames and SourceDiskFiles Section -----------------------
; These sections identify source disks and files for installation. They are
; shown here as an example, but commented out.
[SourceDisksNames]
1 = "WdmDemo",Disk1,,
[SourceDisksFiles]
WdmDemo.sys = 1,MyDriver_Check,
;--------- ClassInstall/ClassInstall32 Section -------------------------------
; Not necessary if using a standard class
; 9X Style
[ClassInstall]
Addreg=Class_AddReg
; NT Style
[ClassInstall32]
Addreg=Class_AddReg
[Class_AddReg]
HKR,,,,%DeviceClassName%
HKR,,Icon,,"-5"
;--------- DestinationDirs Section -------------------------------------------
[DestinationDirs]
YouMark_Files_Driver = 10,System32/Drivers
;--------- Manufacturer and Models Sections ----------------------------------
[Manufacturer]
%MfgName%=Mfg0
[Mfg0]
; PCI hardware Ids use the form
; PCI/VEN_aaaa&DEV_bbbb&SUBSYS_cccccccc&REV_dd
;改成你自己的ID
%DeviceDesc%=YouMark_DDI, PCI/VEN_9999&DEV_9999
;---------- DDInstall Sections -----------------------------------------------
; --------- Windows 9X -----------------
; Experimentation has shown that DDInstall root names greater than 19 characters
; cause problems in Windows 98
[YouMark_DDI]
CopyFiles=YouMark_Files_Driver
AddReg=YouMark_9X_AddReg
[YouMark_9X_AddReg]
HKR,,DevLoader,,*ntkern
HKR,,NTMPDriver,,WdmDemo.sys
HKR, "Parameters", "BreakOnEntry", 0x00010001, 0
; --------- Windows NT -----------------
[YouMark_DDI.NT]
CopyFiles=YouMark_Files_Driver
AddReg=YouMark_NT_AddReg
[YouMark_DDI.NT.Services]
Addservice = HelloWDM, 0x00000002, YouMark_AddService
[YouMark_AddService]
DisplayName = %SvcDesc%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %10%/System32/Drivers/WdmDemo.sys
[YouMark_NT_AddReg]
HKLM, "System/CurrentControlSet/Services/HelloWDM/Parameters",/
"BreakOnEntry", 0x00010001, 0
; --------- Files (common) -------------
[YouMark_Files_Driver]
WdmDemo.sys
;--------- Strings Section ---------------------------------------------------
[Strings]
ProviderName="Zhangfan."
MfgName="Zhangfan Soft"
DeviceDesc="Hello World WDM!"
DeviceClassName="Zhangfan_Device"
SvcDesc="Zhangfan"
第一次亦可直接复制进文本文件中进行创建。
WDM驱动的辅助安装程序为EvDriverInstaller.exe,可以从DriverStudio中提取,亦可从网上搜集。然后被EvDriverInstaller复制到虚拟机中,从“file”菜单中选择“Open”查找到.inf所在目录选择打开(注:驱动文件WdmDemo.sys需和.inf文件放在同一目录下),然后在程序界面的右下方有三个按钮“Add New Device”,“Remove Device”,“Restart Device”,点击“Add New Device”程序自动根据.inf设置安装WdmDemo.sys驱动程序。安装完毕后,可以在EvDriverInstaller程序中间的列表框中发现添加了一个项目一个问号图标及“Hello World WDM!”,同时打开我的电脑的“计算机管理”-》“设备管理器”,即可发现有个新的设备已经出现。名字为“Zhangfan_Device”。
本文原创,转载请注明出处。