这个例子是从《windows驱动开发技术详解》的光盘上copy的,我只是自己稍微改了一下。

入口函数DriverEntry

#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;
}

提供4个派遣函数:

HelloWDMAddDevice

HelloWDMPnp

HelloWDMDispatchRoutine

HelloWDMUnload

在DriverEntry里面简单的设置一下。

AddDevice函数

AddDevice函数是WDM驱动特有的,NT驱动没有,这也是主要的区别之一。先给出代码:

#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,IN PDEVICE_OBJECT PhysicalDeviceObject)
//DriverObject就是指向本驱动程序的一个对象,是由PNP管理器传递进来的。
//PhysicalDeviceObject是PNP管理器传递进来的底层驱动设备对象,这个东西在NT驱动中是没有的。通常称之为PDO,确切的说是由总线驱动创建的。
{ PAGED_CODE();KdPrint(("Enter HelloWDMAddDevice\n"));NTSTATUS status;PDEVICE_OBJECT fdo;UNICODE_STRING devName;RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");//设备名称,设备名称只能被内核模式下的其他驱动所识别。//创建FDO(Function Device Object)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;//将FDO附加在PDO上面,并且将Extension中的NextStackDevice指向FDO的下层设备。如果PDO上面有过滤驱动的话,NextStackDevice就是过滤驱动,如果没有就是PDO。pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);UNICODE_STRING symLinkName;//创建链接符号,这样用户模式的应用就可以访问此设备。内核模式下,符号链接是以\??\开头的(或者\DosDevices\)。用户模式下则是\\.\开头。//这里就可以在用户模式下用\\.\HelloWDM来访问本设备。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;//DO_BUFFERED_IO,定义为“缓冲内存设备”fdo->Flags &= ~DO_DEVICE_INITIALIZING;//将Flag上的DO_DEVICE_INITIALIZING位清零,保证设备初始化完毕,必须的。KdPrint(("Leave HelloWDMAddDevice\n"));return STATUS_SUCCESS;
}

IoCreateDevice有个设备类型参数,这里使用FILE_DEVICE_UNKNOWN。Windows已经预先定义了一些常见的设备类型,如果驱动设备并不在这些类型里面,有两种办法:

1. 使用FILE_DEVICE_UNKNOWN;

2. 使用>=0x8000(32768 - 65535)的值

详见:http://msdn.microsoft.com/en-us/library/windows/hardware/ff563821(v=vs.85).aspx

在DriverEntry函数里面通过pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;设置AddDevice的例程函数。我画了一个简单的图来描述AddDevice基本流程:

AddDevice函数有2个参数:DriverObject和PDO。

DriverObject就是当前驱动程序的一个实例,PDO是物理设备对象(由总线驱动创建)。

AddDevice函数主要工作就是创建一个功能设备对象FDO,然后附加在传递进来的PDO上面。

PNP IRP处理函数

WDM支持PNP(即插即用),这也是不同于NT驱动的一个主要特征。所有WDM驱动都需要设置PNP IRP的派遣函数。如:

pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;

这个是在入口函数DriverEntry里面设置的。

看具体代码:

#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;//DEVICE_EXTENSION是在AddDevice里面创建的。PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//获取irp信息switch (stack->MinorFunction)//根据PNP irp的minor function code来调用相应的处理函数。{case IRP_MN_REMOVE_DEVICE:status = HandleRemoveDevice(pdx, Irp);break;default:status = DefaultPnpHandler(pdx, Irp);break;}KdPrint(("Leave HelloWDMPnp\n"));return status;
}

Pnp派遣函数有2个参数:fdo和Irp。

fdo就是由AddDevice函数创建的那个功能设备对象,Irp是I/O管理器传进来的一个包(I/O Request Packet)。

这个例子里面简单处理了IRP_MN_REMOVE_DEVICE这个minor function code,其他所有的minor function code统统用一个函数来处理DefaultPnpHandler。

先看一下IRP_MN_REMOVE_DEVICE的处理函数:

#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp)
{PAGED_CODE();KdPrint(("Enter HandleRemoveDevice\n"));//设置IRP的完成状态。Irp->IoStatus.Status = STATUS_SUCCESS;//将IRP请求向底层驱动转发。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;
}

里面做了一些简单的删除工作: 设置IRP完成状态 -> 将IRP请求向下一层驱动转发 -> 删除符号链接 -> 删除功能设备对象(FDO)。

再看一些DefaultPnpHandler函数:

#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);//将irp传递给下层驱动
}

啥也没做,就是跳过本层堆栈,直接将irp传递到下层驱动。
IoSkipCurrentIrpStackLocation:修改IRP的IO_STACK_LOCATION,使下层驱动可以获得跟本层驱动一样的IRP。

MSDN解释:

The IoSkipCurrentIrpStackLocation macro modifies the system'sIO_STACK_LOCATION array pointer, so that when the current driver calls the next-lower driver, that driver receives the sameIO_STACK_LOCATION structure that the current driver received.

IoCallDriver: 将irp传递给下层驱动设备对象。

现在可以看到,这个驱动程序例子只是处理了Remove Device,对于其他所有PNP请求,只是简单的传递给下层驱动处理。

DriverUnload函数

因为IRP_MN_REMOVE_DEVICE的函数里面已经处理了设备删除,DriverUnload函数里面不需要处理什么事情了,只是简单打印一些log。

DriverEntry函数里面的代码,简单设置一下派遣函数,如:

pDriverObject->DriverUnload = HelloWDMUnload;

HelloWDMUnload的实现:

#pragma PAGEDCODE
void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject)
{PAGED_CODE();KdPrint(("Enter HelloWDMUnload\n"));KdPrint(("Leave HelloWDMUnload\n"));
}

啥都没做,就是打印log。

最后剩下一个函数是一个派遣函数,处理CREATE,READ, WRITE等irp。

DispatchRoutine

看DriverEntry里面的几行代码:

 pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = pDriverObject->MajorFunction[IRP_MJ_CREATE] = pDriverObject->MajorFunction[IRP_MJ_READ] = pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine;

这个驱动例子里面,将DEVICE_CONTROL, CREATE, READ 和WRITE的IRP用同一个派遣函数来处理。

#pragma PAGEDCODE
NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo,IN PIRP Irp)
{PAGED_CODE();KdPrint(("Enter HelloWDMDispatchRoutine\n"));PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//打印IRP的major function code和minor function code。ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;KdPrint(("DispatchRoutine, Major function code: %x, Minor function code: %x, control code: %x\n", stack->MajorFunction, stack->MinorFunction, code));Irp->IoStatus.Status = STATUS_SUCCESS;Irp->IoStatus.Information = 0;   // no bytes xferedIoCompleteRequest( Irp, IO_NO_INCREMENT );KdPrint(("Leave HelloWDMDispatchRoutine\n"));return STATUS_SUCCESS;
}

HelloWDMDispatchRoutine是一个派遣函数,它有2个参数:fdo和irp(跟PNP处理函数一样)

这里只做了2个事情:

1. 打印一些log

2. 设置irp完成状态,调用IoCompleteRequest函数来完成这个irp。不需要往下层驱动传irp了,因为这个irp已经完成了。

好了,代码基本介绍完毕,看一下完整代码:

/************************************************************************
* 文件名称:HelloWDM.cpp
* 作    者:张帆
* 完成日期:2007-11-1
*************************************************************************/
#include "HelloWDM.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)
//DriverObject就是指向本驱动程序的一个对象,是由PNP管理器传递进来的。
//PhysicalDeviceObject是PNP管理器传递进来的底层驱动设备对象,这个东西在NT驱动中是没有的。通常称之为PDO,确切的说是由总线驱动创建的。
{ PAGED_CODE();KdPrint(("Enter HelloWDMAddDevice\n"));NTSTATUS status;PDEVICE_OBJECT fdo;UNICODE_STRING devName;RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");//设备名称,设备名称只能被内核模式下的其他驱动所识别。//创建FDO(Function Device Object)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;//将FDO附加在PDO上面,并且将Extension中的NextStackDevice指向FDO的下层设备。如果PDO上面有过滤驱动的话,NextStackDevice就是过滤驱动,如果没有就是PDO。pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);UNICODE_STRING symLinkName;//创建链接符号,这样用户模式的应用就可以访问此设备。内核模式下,符号链接是以\??\开头的(或者\DosDevices\)。用户模式下则是\\.\开头。//这里就可以在用户模式下用\\.\HelloWDM来访问本设备。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;//DO_BUFFERED_IO,定义为“缓冲内存设备”fdo->Flags &= ~DO_DEVICE_INITIALIZING;//将Flag上的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);//将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的完成状态。Irp->IoStatus.Status = STATUS_SUCCESS;//将IRP请求向底层驱动转发。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;//DEVICE_EXTENSION是在AddDevice里面创建的。PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//获取irp信息switch (stack->MinorFunction)//根据PNP irp的minor function code来调用相应的处理函数。{case IRP_MN_REMOVE_DEVICE:status = HandleRemoveDevice(pdx, Irp);break;default:status = DefaultPnpHandler(pdx, Irp);break;}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"));PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//打印IRP的major function code和minor function code。ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;KdPrint(("DispatchRoutine, Major function code: %x, Minor function code: %x, control code: %x\n", stack->MajorFunction, stack->MinorFunction, code));Irp->IoStatus.Status = STATUS_SUCCESS;Irp->IoStatus.Information = 0;   // no bytes xferedIoCompleteRequest( 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"));
}

接下来就是如何安装了。

WDM驱动安装

WDM驱动安装需要一个inf文件,这里就直接引用《windows驱动开发技术详解》里面的,没有做任何改动,直接贴出来:

;; The Win2K DDK documentation contains an excellent INF reference.;--------- 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 = "HelloWDM",Disk1,,[SourceDisksFiles]
HelloWDM.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,,HelloWDM.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\HelloWDM.sys[YouMark_NT_AddReg]
HKLM, "System\CurrentControlSet\Services\HelloWDM\Parameters",\
"BreakOnEntry", 0x00010001, 0; --------- Files (common) -------------[YouMark_Files_Driver]
HelloWDM.sys;--------- Strings Section ---------------------------------------------------[Strings]
ProviderName="Zhangfan."
MfgName="Zhangfan Soft"
DeviceDesc="Hello World WDM!"
DeviceClassName="Zhangfan_Device"
SvcDesc="Zhangfan"

有关里面的说明,以后再讲。

有了inf后,就可以通过控制面板->安装硬件来安装这个驱动。给出最后一个截图,其他略。

安装成功后,可以在设备管理里面看到:

这样就安装成功了。

搞了半天,这个驱动有啥用呢?当然我们可以写一个用户模式的测试程序。

调用驱动(用户模式和内核模式通信)

写个很简单的测试例子:

// TestWDMDriver.cpp : Defines the entry point for the console application.
//#include "stdafx.h"
#include <windows.h>#define DEVICE_NAME L"\\\\.\\HelloWDM"int _tmain(int argc, _TCHAR* argv[])
{HANDLE hDevice = CreateFile(DEVICE_NAME,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (hDevice != INVALID_HANDLE_VALUE){for(int i = 0; i < 10; i++){DWORD RetBytes = 0;BOOL b = DeviceIoControl(hDevice, 0xAB, NULL, 0, NULL, 0, &RetBytes, NULL);printf("Test number %d, DeviceIoControl result: %d, byte: %d\n", i + 1, b, RetBytes);Sleep(1000);}CloseHandle(hDevice);}elseprintf("CreateFile failed, err: %x\n", GetLastError());return 0;
}

相当的简单,通过CreateFile函数来打开HelloWDM驱动,通过设备名字\\.\HelloWDM。这个名字对应驱动的AddDevice函数里面设置的符号链接\\DosDevices\\HelloWDM。

这里有必要讲一下驱动的几个名字概念:

1. 首先是设备名称,就是IoCreateDevice里面用到的那个名字。这个名字只能被内核模式的程序(如其他驱动)所识别。用户模式的程序是不知道这个名字的。

2. 符号链接,驱动程序里面可以为某个设备设置符号链接,以\??\开头(或者\DosDevices\),这样用户模式的程序就可以通过这个符号链接来访问这个设备。

3. 用户模式程序里面的设备名称,以\\.\开头
比如这个例子里面内核模式的设备名称是"\Device\MyWDMDevice",符号链接是"\DosDevices\HelloWDM",然后用户模式程序通过"\\.\HelloWDM"访问这个设备。

当CreateFile成功打开这个设备后,就可以操作这个设备了。测试例子里面简单往这个设备调用了10次DeviceIoControl函数(也就是发送一个IRP_MJ_DEVICE_CONTROL类型的IRP)。

运行测试程序,在debugview里面可以看到:

哈哈,驱动程序成功地收到了来自测试程序(用户模式)的信息。

这里只是简单打印一下信息,

Major function code 是e(16进制),看一下WDM.H

#define IRP_MJ_CREATE                   0x00
#define IRP_MJ_CREATE_NAMED_PIPE        0x01
#define IRP_MJ_CLOSE                    0x02
#define IRP_MJ_READ                     0x03
#define IRP_MJ_WRITE                    0x04
#define IRP_MJ_QUERY_INFORMATION        0x05
#define IRP_MJ_SET_INFORMATION          0x06
#define IRP_MJ_QUERY_EA                 0x07
#define IRP_MJ_SET_EA                   0x08
#define IRP_MJ_FLUSH_BUFFERS            0x09
#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
#define IRP_MJ_SET_VOLUME_INFORMATION   0x0b
#define IRP_MJ_DIRECTORY_CONTROL        0x0c
#define IRP_MJ_FILE_SYSTEM_CONTROL      0x0d
#define IRP_MJ_DEVICE_CONTROL           0x0e

确实0x0e对应的是IRP_MJ_DEVICE_CONTROL。

然后测试例子里面发送了一个0xAB的控制码给驱动,那么驱动里面通过stack->Parameters.DeviceIoControl.IoControlCode得到的控制码也是0xab,看上面的debugview信息。

这样就大功告成了。首先我们写了一个简单的WDM驱动,然后安装,之后在用户模式里面访问这个驱动。这也就是驱动的一般流程。

当然真正的驱动程序远没有这个例子简单。我个人对于驱动大概也就是小学生的水平,需要继续学习,研究。

我将完整代码打包上传了,有兴趣可以下载:http://download.csdn.net/detail/zj510/4794275
(用DDK编译驱动,简单调用build命令即可。用VS2008编译用户模式的测试程序)

Windows驱动开发WDM (2)- 一个简单的WDM驱动程序相关推荐

  1. 树莓派底层IO驱动开发示例(一个简单io口驱动的开发)

    一.驱动代码的开发 1.树莓派寄存器的介绍 点击查看:树莓派(bcm2835芯片手册) GPFSEL0 GPIO Function Select 0: 功能选择 输入/输出 GPSET0 GPIO P ...

  2. 客户端开发 Windows驱动开发(1)SDK WDK DDK WDM的关系

    尽管Windows平台的SDK.DDK与WDK都包含了WinDBG工具包,但是用户获取WinDBG工具包的最主要方式还是从微软网站自由下载,因为这样获得的版本最新. 最近因为工作需要,尝试去了解WIN ...

  3. Windows驱动开发WDM (1) - 基本结构

    陆陆续续做过一些驱动的开发,但是一直以来都没有系统的学习过.这次重新阅读<windows驱动开发技术详解>(张帆,史彩成等编著),写博客记录一下,用以加深自己对驱动的理解. 驱动对象(DR ...

  4. [Windows驱动开发](一)序言

    笔者学习驱动编程是从两本书入门的.它们分别是<寒江独钓--内核安全编程>和<Windows驱动开发技术详解>.两本书分别从不同的角度介绍了驱动程序的制作方法. 在我理解,驱动程 ...

  5. 《Windows驱动开发技术详解》学习笔记

    Abstract   如果推荐 Windows 驱动开发的入门书,我强烈推荐<Windows驱动开发技术详解>.但是由于成书的时间较早,该书中提到的很多工具和环境都已不可用或找不到,而本文 ...

  6. Windows驱动开发书籍简介

    分享到 一键分享 QQ空间 新浪微博 百度搜藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 首页 我的主页 相册 广 ...

  7. c语言windows驱动编程入门,Windows驱动开发技术详解 PDF扫描版[175MB]

    Windows驱动开发技术详解由浅入深.循序渐进地介绍了windows驱动程序的开发方法与调试技巧.本书共分23章,内容涵盖了windows操作系统的基本原理.nt驱动程序与wdm驱动程序的构造.驱动 ...

  8. Windows驱动开发环境搭建:VS2013+WDK8.1

    你的Windows驱动开发环境之所以搭建不成功,是因为你没有看这篇博客 1.安装包准备 Visio Studio2013: https://pan.baidu.com/s/1P77yeSKuE7mWl ...

  9. Windows驱动开发入门系列教程

    从事驱动开发也有一段时间了,从最初的无头苍蝇到懵懵懂懂,到入门,直至今天,感觉一路走来,走了不少的弯路,只因为没有人引导.前几天,一个朋友问到我怎么学习Windows驱动开发,我就想到把我学习Wind ...

  10. 转:Windows驱动开发(中间层)

    Windows驱动开发(中间层) - 慧由心生 - 博客园Windows驱动开发一.前言依据<Windows内核安全与驱动开发>及MSDN等网络质料进行学习开发.二.初步环境1.下载安装W ...

最新文章

  1. mysql 的 前导零_将前导零添加到MySQL列?
  2. Windows 10 技术预览
  3. python省市区三级联动_Django Admin实现三级联动的示例代码(省市区)
  4. 关于 p3p ie 跨域 问题
  5. 【Python】用tkinter做一个采色器
  6. RabbitMq(十一) 死信交换机DLX介绍及使用
  7. 负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础
  8. 数据结构--树形结构(1)
  9. 【渝粤教育】国家开放大学2018年秋季 0538-21T社区护理 参考试题
  10. (七) 三维点云课程---ICP(Point-to-Point)
  11. python爬虫基础教程:利用python抓取返利网商品信息
  12. hexo博客之yilia主题的个性化设置
  13. amcharts示例
  14. 安装window7系统
  15. 怎样降低计算机屏幕亮度,如何调低电脑屏幕亮度【解决步骤】
  16. ActiveX控件属性的下拉列表
  17. Python站内文章精选大集合!
  18. Zookeeper启动成功,报错“Error contacting service. It is probably not running”
  19. iphone快捷指令蚂蚁森林能量_如何快速偷取蚂蚁森林能量?这有一个捷径!-芒果TV专栏...
  20. 软件测试工程师如何优雅的“甩锅”

热门文章

  1. 从0开始实现一个直播礼物系统
  2. matlab常微分方程2次初值问题,MATLAB求解二阶常微分方程初值问题
  3. yytext table html,快速掌握YYText
  4. Layout天线效应的产生原因以及解决方法
  5. 罗技 logic C930c 摄像头 驱动 win7 64位 家庭中文版 无法使用
  6. Day02 郝斌C语言自学视频之C语言编程预备知识
  7. 华为各系列数通网络产品介绍
  8. ADN8831ACPZ特征TPS63020DSJR应用 具有 4A 开关转换器
  9. SPSS学习笔记(一)判断是否服从正态分布
  10. 百度地图加载shp_ArcGIS中加载无偏移谷歌卫星影像!奥维官方插件与ArcGIS协同互动...