admin 管理员组

文章数量: 1184232


2024年3月9日发(作者:resizable)

DriverWorks简明教程

1.学会使用向导

很多人不喜欢向导,可是一个从应用开发转做驱动开发的人可能更容易接受这种方式,但在今天我不打算使用过多的向导,而是手工写驱动代码,只是使用了DriverWorks的包装类,使用MFC可以反过来使我的SDK技术长进不少,那同样的使用DriverWorks我相信同样可以使我们的DDK技术得到锻炼.

首先使用DDK Build Settings启动Visual C++ 6.0

1.1 点击DriverStudio工具栏上的Launch DriverWizard按钮,选择New Project中的"Start a New Driver Project"

1.2 向导会自动选中DDK Source/makefile和Visual Studio 6,在Project Name里输入"hello"后点下一步

1.3 project type页中选择"Kernel Mode Service"点下一步

1.4 IRP Handlers页中把所有自动勾上的请求都去掉,因为我们要手工写DriverWorks代码以加深印象.点下一步

1.5 在Additional页里把自动勾上的Generate SoftICE NMS file in

custom build step去掉.点下一步

1.6 预览工程,并点击完成:

Project Summary For kruglinski

Project Name = hello

Project Location = D:Workspacehello

Project Type = NT

1.8 空的工程框架就会生成完毕.

此时直接编译会出错,也许是DriverStudio的一个Bug,只要把sources中的:

TARGETLIBS=$(DDK_LIB_PATH)

$(DDK_LIB_PATH)

这一行去掉就可以编译通过了,让我们编试一下,一定感觉很不错吧!

2.拨开云雾见日出

2.1 现在我们要做的是清理掉所有会让我们眼花的东东(一大堆向导生成的代码),只留下编译环境,我喜欢在Visual Studio里直接编译的感觉,很讨厌总是看到那个黑黑的Build Console.切换到FileView初图,把除了makefile和sources文件以外的其它文件全部删除,然后我们只复制,sources和makefile三个文件到一个单独的文件夹helloworld里.选择File-Close Workspace关闭刚向导出来的工程,再把copy出来的拖到Visual Studio 6中打开(这时会自动生成一个).

2.2 现在要正式开工了,按Ctrl+N在Files页选择C++ Source File,在File栏里输入

在里首先要#include "vdw.h" ,因为我们要用的DriverWorks包装类的头文件都由该文件包括.

这里需要注意的是在#include "vdw.h"前应该加入一句#define

VDW_MAIN因为DriverWorks的帮助文件里这样说(懒得译了,

将就着看吧!!)

Symbol VDW_MAIN must be #define'd in exactly one source

module of the driver, generally the module that implements the

driver class. If, as is true for most drivers, there is more than one

source module, do not #define VDW_MAIN in any other modules.

The definition must precede #include . The purpose of this symbol is

to control the inclusion of certain source files in the DriverWorks

library. These source files must be compiled in the context of the

driver being built.

2.3 我们定义一个驱动对象类,它派生自KDriver,必须提供一个纯虚函数DriverEntry的实现:

class MyDriver:public KDriver

{

SAFE_DESTRUCTORS;

public:

virtual NTSTATUS DriverEntry(PUNICODE_STRING

RegistryPath);

};

DECLARE_DRIVER_CLASS(MyDriver,NULL)

NTSTATUS

RegistryPath)

{

MyDriver::DriverEntry(IN PUNICODE_STRING

DbgPrint("Hello KMD world!");

return STATUS_SUCCESS;

}

这里还有一个DECLARE_DRIVER_CLASS(MyDriver,NULL)要注意,再copy一段帮助文档上的内容:

One module of the driver must invoke macro

DECLARE_DRIVER_CLASS. This macro actually inserts a small

function that the framework calls during initialization. The function

creates an instance of the driver class.

现在已经可以编译成sys,用我的小工具Driver Logicl Tester可以测试一下效果,可以看到输出了一行"hello KMD World!",我想需要解释一下KDriver,KDriver是驱动对象,按照DriverWorks对象模型里的观念,一个驱动程序里至少要包括一个驱动对象,这个驱动对象可以包括一个或多个设备对象,Ring3的应用程序可以通过设备对象来于驱动程序交互.后面我会做简单的演示.

3.打开潘多拉魔盒

上面演示的东东其实本上没有多大实际的用处,当然可以在riverEntry里添加一个调用门让Ring3的应用程序有机会使用调用门进入Ring0.所以我们必须要做到可以控制驱动程序执行指定的代码完成指定的任务.

3.1 设备对象与KDevice

在DriverWorks里KDevice和KPnpDeivce做为设备对象类的抽像,因为这里想演示的是KMD而不是WDM,所以我在这里只使用KDevice,KPnpDeivce以后有空再说.

在#define VDW_MAIN后#include "vdw.h"前加上以下几行:

#define DRIVER_FUNCTION_DEVICE_CONTROL

#define DRIVER_FUNCTION_CREATE

#define DRIVER_FUNCTION_CLOSE

#define DRIVER_FUNCTION_CLEANUP

当需要覆盖设备或是驱动对象基类的某个函数时,需要在include之前定义这些宏.原因不要问,文档上要求这样做那就这样做咯.具体的可以查看DriverWorks源代码,这四个是设备对象要用到的,可以对应看下面的代码.

现在我们实现一个设备对象的派生类MyDevice,它将负责与上层的驱动测试工具做交互.

class MyDevice : public KDevice

{

public:

SAFE_DESTRUCTORS;

MyDevice();

~MyDevice();

DEVMEMBER_DISPATCHERS;

};

DriverWorks帮助文档里说到如果没有SAFE_DESTRUCTORS这个宏,Unload例将工作的不太正常,所以不用问那么照做就是.而DEVMEMBER_DISPATCHERS宏的功能是自动的定义所有的派遣函数,如果Create,等.

然后分别实现前面#define的那几个派遣函数:

NTSTATUS MyDevice::Create(KIrp I)

{

DbgPrint(__FUNCTION__":IRP %pn", I);

ation() = 0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS MyDevice::Close(KIrp I)

{

DbgPrint(__FUNCTION__":IRP %pn", I);

ation() = 0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS MyDevice::DeviceControl(KIrp I)

{

DbgPrint(__FUNCTION__":IRP

I,ode());

ation() = 0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS MyDevice::CleanUp(KIrp I)

%p,CodeCode=%dn",

{

DbgPrint(__FUNCTION__":IRP %pn", I);

ation() = 0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

3.2 构造一个设备对象

在DriverEntry里创建这个设备对象,其实非常简单,只是调用一个new运算符函数.

NTSTATUS

RegistryPath)

{

NTSTATUS status = STATUS_SUCCESS;

DbgPrint(__FUNCTION__"RegistryPath:%Sn",RegistryPath->Buffer);

MyDevice* pDevice = new (

L"HelloDevice",

FILE_DEVICE_UNKNOWN,

L"HelloDevice",

0,

DO_DIRECT_IO

)

MyDriver::DriverEntry(PUNICODE_STRING

MyDevice();

if (pDevice == NULL)

{

status = STATUS_INSUFFICIENT_RESOURCES;

}

else

{

status = pDevice->ConstructorStatus();//读取设备对象构造后的状态

if (!NT_SUCCESS(status))

{

delete pDevice;

}

else

DbgPrint(__FUNCTION__"Created HelloDevice!n");

}

return status;

}

我想需要解释一下这个new运算符,它是KDevice的重载动算符,我使用的原型是:

void* __cdecl operator new(

size_t n,

PCWSTR DeviceName,

DEVICE_TYPE Type,

PCWSTR LinkName=NULL,

ULONG Characteristics=0,

ULONG DeviceFlags=DO_BUFFERED_IO

);

这个new运算符函数的comment说到:

// This is the form of new that allocates the storage for the class

instance in

// the system device object extension.

在设备对象扩展里分配并存储类实例,我看了一下它的源代码在中,它会调用一个叫__construct的函数,而__construct函数会调用IoCreateDevice创建一个驱动设备对象(注意不是DriverWorks设备对象类),并分配一个大小为n(在这里是n=sizeof(MyDevice))的设备扩展用来存储这个设备对象类(包括它的数据成员,感觉确实是很方便啊!),并调用IoCreateSymbolLink在DosDevice创建一个符号链接供Ring3程序使用,我想LinkName使用一个GUID字符串更合适一些.呵呵!其实不需要把代码跟到这里看,只不过了解一些底下的东东也好,但我很赞成潘爱民老师所说的,做应用开发尽可能不要去读软件开发商提供的类库源代码.只看参考手册能够用的很熟练很巧就行了.所以我只看nski的Inside VC++而从不去看什么深

入浅出MFC,我的MFC照样用的很好,也可以理解那些MFC底层如何实现,其实深入浅出MFC里的东西,Inside VC++里只用了几页纸就讲的非常清楚了,呵呵!话扯远了.也许每个人学习的思想和方法不一样.

再搬出我的小工具,测试一下.可以看到我们写的几个函数都被调用到了,每按一下"IoControl"按钮,DeviceControl函数就会被调用.

4.输入与输出

4.1 简单的KMD与上层应用同步方法

我想有了前面的基础,做输出输出已经不是什么难事,可以在DeviceIoControl里做,也可以在Read和Write派遣函数里做,不过我想通常的做法是,驱动创建一个同步事件,使用DriverWorks的KEvent对象,并使用这个成员函数原型进行初使化:

VOID Initalize(

KUstring& Name,

EVENT_TYPE type

);

第一个参数可以填以BaseNamedObjects为前缀的事件对象名,后一个参数可以是SynchronizationEvents(同步事件)或是NotificationEvents(通知事件),然后Ring3的程序可以用CreateEvent打开这个对象,WaitForSingleObject做同步.当有数据时上层的程序会在第一时间得到通知.

一般设备对象会添加几个容器成员,像前面分析的,它们会被会配并存储在设备扩展里,当然容器要使用的堆内存会是从PagedPool或是NonPagedPool里分配出来的,一些常用的容器如KList,等等.这样,数据的排队和存储解决后,就是在派遣函数中处理它们了,或是从上层收到并存入容器,或是从容器里摘除并返回到上层.

4.2 IO控制代码

一般使用一个叫做CTL_CODE的宏来定义IO控制代码,在每个派遣函数有一个KIrp类型的参数,它也IRP请求包的包装

类,KIrp::IoctlCode函数返回控制代码的引用,也就是说我们可以读也可以改写控制代码.通常在DeviceIoControl里根据不同的控制代码做不同的动作.类似于这样:

NTSTATUS MyDevice::DeviceControl(KIrp I)

{

switch (ode())

{

case CTRL_SETUP_HOOK:

.............break;

case CTRL_REMOVE_HOOK:

.............break;

default:

status = STATUS_INVALID_DEVICE_REQUEST;

break;

}

...

}

而由输入输出缓冲及大小分别和,KIrp::IoctlBuffer,KIrp::IoctlInputBufferSizeKIrp::IoctlOutputBufferSize来获得.这只是Buffered IO方式,还有Direct IO方式有空再说!

完整的程序代码,我更喜欢看面向对象的代码结构,呵呵!

//:

#define VDW_MAIN

#define DRIVER_FUNCTION_DEVICE_CONTROL

#define DRIVER_FUNCTION_CREATE

#define DRIVER_FUNCTION_CLOSE

#define DRIVER_FUNCTION_CLEANUP

#define DRIVER_FUNCTION_UNLOAD

#include "vdw.h"

class MyDriver : public KDriver

{

SAFE_DESTRUCTORS

public:

virtual NTSTATUS DriverEntry(PUNICODE_STRING

RegistryPath);

virtual VOID Unload(VOID);

};

DECLARE_DRIVER_CLASS(MyDriver,NULL)

class MyDevice : public KDevice

{

public:

SAFE_DESTRUCTORS;

MyDevice();

~MyDevice();

DEVMEMBER_DISPATCHERS

};

MyDevice::~MyDevice()

{

}

MyDevice::MyDevice() :

KDevice()

{

if (!NT_SUCCESS(m_ConstructorStatus))

{

DbgPrint(__FUNCTION__": Failed to create device MyDevice");

return;

}

}

NTSTATUS MyDevice::Create(KIrp I)

{

DbgPrint(__FUNCTION__":IRP %pn", I);

ation() = 0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS MyDevice::Close(KIrp I)

{

DbgPrint(__FUNCTION__":IRP %pn", I);

ation() = 0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS MyDevice::DeviceControl(KIrp I)

{

DbgPrint(__FUNCTION__":IRP

I,ode());

ation() = 0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS MyDevice::CleanUp(KIrp I)

{

DbgPrint(__FUNCTION__":IRP %pn", I);

ation() = 0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

%p,CodeCode=%dn",

}

VOID MyDriver::Unload(VOID)

{

KDriver::Unload();

}

NTSTATUS

RegistryPath)

{

NTSTATUS status = STATUS_SUCCESS;

DbgPrint(__FUNCTION__"RegistryPath:%Sn",RegistryPath->Buffer);

MyDevice* pDevice = new (

L"HelloDevice",

FILE_DEVICE_UNKNOWN,

L"HelloDevice",

0,

DO_DIRECT_IO

)

MyDevice();

if (pDevice == NULL)

{

status = STATUS_INSUFFICIENT_RESOURCES;

MyDriver::DriverEntry(PUNICODE_STRING

}

else

{

status = pDevice->ConstructorStatus();

if (!NT_SUCCESS(status))

{

delete pDevice;

}

else

DbgPrint(__FUNCTION__"Created HelloDevice!n");

}

return status;

}

到现在为止,如果你是已经可以很熟练的用DDK写驱动肯定觉得这一切会有点繁琐,但这仅仅是开始,驱动的工程越大,代码量越多时使用DriverWorks表现出的效率会越明显.可以类比像MFC与SDK

玩玩DriverWorks(二)-读写数据

这两天怎么老想玩DriverWorks

今天抽了一个小时写个小驱动练习一下KIrp和KMemory配合访问三种IO方式中的数据.KIrp是对IRP请求对象的包装,它有很

多成员函数用于操作IRP结构.

ed IO:

在Buffered IO方式中IO管理器会分配一块堆内存然后把用户态缓冲区数据copy进来再传给驱动程序,而输出时IO管理器会把堆内存中的数据copy回用户态缓冲区.

我们使用下面这样的代码来得到输入/输出缓冲区和大小

KIrp I;

....

ULONG inputSize = nputBufferSize();

ULONG outputSize = utputBufferSize();

PVOID inputBuffer = uffer();

PVOID outputBuffer = uffer();

一目了然吧!

我想要介绍一下这几个函数,但你实际上可以跳过下面的一小段因为用DriverWorks根本用不着知道这些细节.

IoctlBuffer,IoctlInputBufferSize,IoctlOutputBufferSize函数实现非常简单:

inline PVOID& KIrp::IoctlBuffer(void)

{

return m_Irp->Buffer;

}

inline ULONG& KIrp::IoctlOutputBufferSize(EStackLocation s)

{

ValidateStackLocation(s);

if (s == CURRENT)

return IoGetCurrentIrpStackLocation(m_Irp)->

BufferLength;

else

return IoGetNextIrpStackLocation(m_Irp)->

BufferLength;

}

inline ULONG& KIrp::IoctlInputBufferSize(EStackLocation s)

{

ValidateStackLocation(s);

if (s == CURRENT)

return IoGetCurrentIrpStackLocation(m_Irp)->

ufferLength;

else

return IoGetNextIrpStackLocation(m_Irp)->

ufferLength;

}

在IoctlOutputBufferSize和IoctlInputBufferSize中S有一个默认参数CURRENT,上面的代码就是这样调用它们的.可以看到其实

是对IO_STACK_LOCATION结构直接的操作.

IO:

Direct IO方式中IO管理器分将用户态缓冲区映射到核心态(地址不一样,但其实是一块内存),并锁定内存不要分页机制将内存交换到外存中.然后IO管理器将缓冲区在核心态的的地址传给驱动程序.Direct IO的分成METHOD_IN_DIRECT和METHOD_OUT_DIRECT

2.1 在METHOD_IN_DIRECT中我们使用这样的代码来访问缓冲区及大小

KIrp I;

....

KMemory Mem(());

PUCHAR pBuffer = (PUCHAR) ystemSpace();

ULONG writeSize = ize();

ULONG bytesSent = 0;

2.2 在METHOD_OUT_DIRECT中要像这样来访问缓冲区及大小

KIrp I;

....

KMemory Mem(());

PUCHAR pBuffer = (PUCHAR) ystemSpace();

ULONG readSize = ze();

ULONG bytesRead = 0;

函数是这样实现的:

inline PMDL& KIrp::Mdl(void)

{

return m_Irp->MdlAddress;

}

它直接返回IRP结构的MdlAddress字段,而MapToSystemSpace的实现代码是这样的:

inline PVOID KMemory::MapToSystemSpace(void)

{

return GetSystemAddressForMdl(m_pMdl);

}

它调用下面这个函数,这里我不太明白为什么不直接调用MmGetSystemAddressForMdl

inline PVOID GetSystemAddressForMdl(PMDL pMdl)

{

CSHORT canFail; // original fail flag in MDL

if(!(pMdl->MdlFlags &

(MDL_MAPPED_TO_SYSTEM_VA

MDL_SOURCE_IS_NONPAGED_POOL))) {

|

// get the current fail flag

canFail = (CSHORT)(pMdl->MdlFlags &

MDL_MAPPING_CAN_FAIL);

// set 'mapping can fail' so we don't bugcheck

// if MmMapLockedPages fails

pMdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;

// try to map the buffer

pMdl->MappedSystemVa =

(PVOID)(((ULONG_PTR)MmMapLockedPages(pMdl,

KernelMode)) |

MmGetMdlByteOffset(pMdl));

// set the original flag back in the MDL

if(!canFail) {

pMdl->MdlFlags &= ~MDL_MAPPING_CAN_FAIL;

}

}

return pMdl->MappedSystemVa;

}

3 Neither IO:

在Neither IO,IO管理器直接把用户态的缓冲区地址和大小传给驱动,不做任何处理.这是很危险的,通常情况下,我们需要写一些小的助手驱动以进入Ring0或是调用一些底层功能,如果可以肯

定驱动的派遣函是运行在被动级并且在我们自己指定程序的进程上下文里,那么用用这种方式也无所谓.除此以外尽可能不用这样的IO方式.通常需要使用这样的代码来访问缓冲区和大小:

KIrp I;

....

ULONG inputSize = nputBufferSize();

ULONG outputSize = utputBufferSize();

PVOID inputBuffer = ype3InputBuffer();

PVOID outputBuffer = ffer();

IoctlType3InputBuffer和UserBuffer的实现代码:

inline PVOID& KIrp::IoctlType3InputBuffer(EStackLocation s)

{

ValidateStackLocation(s);

if (s == CURRENT)

return IoGetCurrentIrpStackLocation(m_Irp)->

3InputBuffer;

else

return IoGetNextIrpStackLocation(m_Irp)->

3InputBuffer;

}

inline PVOID& KIrp::UserBuffer(void)

{

return m_Irp->UserBuffer;

}

4. 现在可以总结一下,根据这些我们可以学到在DDK层面上应该怎么操作这些IO方式中的缓冲区:

4.1 在驱动的派遣函数中可以使用IoGetCurrentIrpStackLocation得到IO_STACK_LOCATION可以根据它的Control字段确定是什么派遣函数(因为可以将多个派遣函数地址指向一个派遣函数来处理多个请求).在Control等于IRP_MJ_DEVICE_CONTROL时来要根据DeviceIoControl的子字段IoControlCode来确定是什么控制代码.

4.2 对于Buffered IO和Neither IO可以使用它的DeviceIoControl字段和三个子字段来访问缓冲区和大小参数:

typedef struct _IO_STACK_LOCATION {

....

union {

...

struct {

ULONG OutputBufferLength;

ULONG InputBufferLength;

ULONG IoControlCode;

PVOID Type3InputBuffer;

} DeviceIoControl;

...

}

....

} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

4.3 对于Direct IO则要使用MmGetSystemAddressForMdl来将IRP结构的MdlAddress(内存描述表)字段映射到内核地址空间并锁定,得到一个内核空间地址才能进一步操作.Direct IO方式中的输入输出大小参数由IO_STACK_LOCATION的和指示

typedef struct _IO_STACK_LOCATION {

....

union {

...

struct {

ULONG Length;

ULONG Key;

LARGE_INTEGER ByteOffset;

} Read;

struct {

ULONG Length;

ULONG Key;

LARGE_INTEGER ByteOffset;

} Write;

...

}

....

} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

说不看库源代码的又忍不住开始分析,呵呵!其实可以把DriverWorks的对象类搞清楚怎么用就可以了.如果是想顺便把自己的DDK开发能力也提高一下的话,库的源代码也可以参考.

有了昨天的DriverWorks基本介绍,我想下面的代码应该不是很难懂吧!

//Interface.h

#ifndef __INTERFACE_H__

#define __INTERFACE_H__

#define DEVICE_NAME L"IoDemoDevice"

#define FILE_DEVICE_IODEMO 0x8000

#define IOCTL_DO_BUFFERED_IO

CTL_CODE(FILE_DEVICE_IODEMO,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)

#define IOCTL_DO_DIRECT_IN

CTL_CODE(FILE_DEVICE_IODEMO,0x801,METHOD_IN_DIR

ECT,FILE_ANY_ACCESS)

#define IOCTL_DO_DIRECT_OUT

CTL_CODE(FILE_DEVICE_IODEMO,0x802,METHOD_OUT_DIRECT,FILE_ANY_ACCESS)

#define IOCTL_DO_NEITHER_IO

CTL_CODE(FILE_DEVICE_IODEMO,0x803,METHOD_NEITHER,FILE_ANY_ACCESS)

#endif

//

#define VDW_MAIN

#define DRIVER_FUNCTION_UNLOAD

#define DRIVER_FUNCTION_CREATE

#define DRIVER_FUNCTION_CLOSE

#define DRIVER_FUNCTION_CLEANUP

#define DRIVER_FUNCTION_DEVICE_CONTROL

#include "vdw.h"

#include "Interface.h"

class IoDemoDriver:public KDriver

{

SAFE_DESTRUCTORS;

public:

virtual NTSTATUS DriverEntry(IN PUNICODE_STRING

RegistryPath);

virtual VOID Unload();

};

DECLARE_DRIVER_CLASS(IoDemoDriver,NULL)

class IoDemoDevice:public KDevice

{

SAFE_DESTRUCTORS;

public:

IoDemoDevice();

DEVMEMBER_DISPATCHERS;

};

IoDemoDevice::IoDemoDevice():KDevice()

{

if (!NT_SUCCESS(m_ConstructorStatus))

{

DbgPrint(__FUNCTION__": Failed to create device MyDevice");

return;

}

}

NTSTATUS IoDemoDevice::Create(KIrp I)

{

DbgPrint(__FUNCTION__":IRP 0x%08Xn",I);

ation()=0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS IoDemoDevice::Close(KIrp I)

{

DbgPrint(__FUNCTION__":IRP 0x%08Xn",I);

ation()=0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS IoDemoDevice::CleanUp(KIrp I)

{

DbgPrint(__FUNCTION__":IRP 0x%08Xn",I);

ation()=0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS IoDemoDevice::DeviceControl(KIrp I)

{

DbgPrint(__FUNCTION__":IRP 0x%08Xn",I);

switch(ode())

{

case IOCTL_DO_BUFFERED_IO:

{

DbgPrint(__FUNCTION__":IOCTL_DO_BUFFERED_IOn",I);

ULONG inputSize = nputBufferSize();

ULONG outputSize = utputBufferSize();

PVOID inputBuffer = uffer();

PVOID outputBuffer = uffer();

//显示出传进来的字符串

if(inputSize)

DbgPrint("inputBuffer:%s",(char*)inputBuffer);

//返回一个字符串

char chDoBufferedIO[]="DO_BUFFERED_IO";

strncpy((char*)outputBuffer,chDoBufferedIO,outputSize);

ation()=strlen(chDoBufferedIO);

}

break;

case IOCTL_DO_DIRECT_IN:

{

DbgPrint(__FUNCTION__":IOCTL_DO_DIRECT_INn",I);

KMemory Mem(());

PUCHAR pBuffer = (PUCHAR) ystemSpace();

ULONG writeSize = ize();

ULONG bytesSent = 0;

if(writeSize)

DbgPrint("pBuffer:%s",(char*)pBuffer);

ation()=0;

}

break;

case IOCTL_DO_DIRECT_OUT:

{

DbgPrint(__FUNCTION__":IOCTL_DO_DIRECT_OUTn",I);

KMemory Mem(());

PUCHAR pBuffer = (PUCHAR) ystemSpace();

ULONG readSize = ze();

ULONG bytesRead = 0;

if(readSize)

{

char chDirectOut[]="DO_DIRECT_OUT";

strncpy((char*)pBuffer,chDirectOut,readSize);

ation()=strlen(chDirectOut);

}

}

break;

case IOCTL_DO_NEITHER_IO:

{

DbgPrint(__FUNCTION__":IOCTL_DO_NEITHER_IOn",I);

ULONG inputSize = nputBufferSize();

ULONG outputSize = utputBufferSize();

PVOID inputBuffer = ype3InputBuffer();

PVOID outputBuffer = ffer();

//显示出传进来的字符串

if(inputSize)

DbgPrint("inputBuffer:%s",(char*)inputBuffer);

//返回一个字符串

char chDoNeitherIo[]="DO_NEITHER_IO";

strncpy((char*)outputBuffer,chDoNeitherIo,outputSize);

ation()=strlen(chDoNeitherIo);

}

break;

default:

ation()=0;

te(STATUS_INVALID_DEVICE_REQUEST);

return STATUS_INVALID_DEVICE_REQUEST;

}

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS IoDemoDriver::DriverEntry(IN PUNICODE_STRING

RegistryPath)

{

NTSTATUS status = STATUS_SUCCESS;

DbgPrint(__FUNCTION__":RegistryPath:%Sn",RegistryPath->Buffer);

IoDemoDevice* pDevice = new (

DEVICE_NAME,

FILE_DEVICE_UNKNOWN,

DEVICE_NAME,

0,

DO_DIRECT_IO

)

IoDemoDevice();

if (pDevice == NULL)

{

status = STATUS_INSUFFICIENT_RESOURCES;

}

else

{

status = pDevice->ConstructorStatus();

if (!NT_SUCCESS(status))

{

delete pDevice;

}

else

DbgPrint(__FUNCTION__":Created IoDemoDevice!n");

}

return status;

}

VOID IoDemoDriver::Unload()

{

DbgPrint(__FUNCTION__);

KDriver::Unload();

}

最后可以用我的小工具测试一下,Load驱动后可以点Content按钮编辑数据包,也可以查看驱动返回的数据,显示方式是十六进制,注意根据IO控制代码的定义选择不同的Method,DeviceId和ControlCode组合方式.个人感觉这个小工具还是比较好用的,呵呵!

玩玩DriverWorks(三)-串行化IO请求

周末实在不太想写我的数据库程序,又花了一个小时练习了一下DriverWorks里的排队IO请求模型,其实这和DDK中的模型基本上是一样的,因为参考了Programming the Microsoft Windows

Driver Model里的一些代码,并且少看了DriverWorks关于排队IO的一句话,我还得到了一个BSOD,不过这个BSOD让我注

意到了帮助文档上我没有注意到的地方,呵呵!

废话不多说,来一个段代码!

#define VDW_MAIN

#define DRIVER_FUNCTION_UNLOAD

#define DRIVER_FUNCTION_CREATE

#define DRIVER_FUNCTION_CLEANUP

#define DRIVER_FUNCTION_CLOSE

#define DRIVER_FUNCTION_READ

#define DRIVER_FUNCTION_WRITE

#define DRIVER_FUNCTION_STARTIO

#define DRIVER_FUNCTION_DEVICE_CONTROL

#include "vdw.h"

//Begin Device Code///////////////////////////////////////////////////////

class CQueueDevice:public KDevice

{

SAFE_DESTRUCTORS;

public:

CQueueDevice();

NTSTATUS ComplateIrp(KIrp I);

DEVMEMBER_DISPATCHERS;

DEVMEMBER_CANCELIRP(CQueueDevice,CancelIo);

NTSTATUS SerialRead(KIrp I);

NTSTATUS SerialWrite(KIrp I);

NTSTATUS SerialIoControl(KIrp I);

};

CQueueDevice::CQueueDevice():KDevice(L"CQueueDevice",FILE_DEVICE_UNKNOWN,L"CQueueDevice",0,DO_DIRECT_IO)

{

if(!NT_SUCCESS(ConstructorStatus()))

{

DbgPrint(__FUNCTION__":Failed to Create Devicen");

}

}

NTSTATUS CQueueDevice::ComplateIrp(KIrp I)

{

ation()=0;

te(STATUS_SUCCESS);

return STATUS_SUCCESS;

}

NTSTATUS CQueueDevice::Create(KIrp I)

{

DbgPrint(__FUNCTION__"n");

return ComplateIrp(I);

}

NTSTATUS CQueueDevice::CleanUp(KIrp I)

{

DbgPrint(__FUNCTION__"n");

return ComplateIrp(I);

}

NTSTATUS CQueueDevice::Close(KIrp I)

{

DbgPrint(__FUNCTION__"n");

return ComplateIrp(I);


本文标签: 对象 驱动 函数 设备 使用