iniwf

风是温柔的,雨是伤心的,云是快乐的,月是多情的,爱是迷失的,恋是醉人的,情是难忘的,天是长久的,地是永恒的

驱动开发之一 --- 创建一个简单的设备驱动(续) 【译文】

转自http://hi.baidu.com/combojiang/blog/item/321851d4b42c9804a18bb7c5.html

接上继续。
/**********************************************************************
*
* Toby Opferman
*
* Driver Example
*
* This example is for educational purposes only. I license this source
* out for use in learning how to write a device driver.
*
*     Driver Functionality
**********************************************************************/

#define _X86_

#include <wdm.h>
#include "example.h"


/**********************************************************************
* Internal Functions
**********************************************************************/
BOOLEAN Example_IsStringTerminated(PCHAR pString, UINT uiLength);


#pragma alloc_text(PAGE, Example_Create)
#pragma alloc_text(PAGE, Example_Close)
#pragma alloc_text(PAGE, Example_IoControl)
#pragma alloc_text(PAGE, Example_Read)
#pragma alloc_text(PAGE, Example_WriteDirectIO)
#pragma alloc_text(PAGE, Example_WriteBufferedIO)
#pragma alloc_text(PAGE, Example_WriteNeither)
#pragma alloc_text(PAGE, Example_UnSupportedFunction)
#pragma alloc_text(PAGE, Example_IsStringTerminated)




/**********************************************************************
*
* Example_Create
*
*    This is called when an instance of this driver is created (CreateFile)
*
**********************************************************************/
NTSTATUS Example_Create(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    DbgPrint("Example_Create Called \r\n");

    return NtStatus;
}

/**********************************************************************
*
* Example_Close
*
*    This is called when an instance of this driver is closed (CloseHandle)
*
**********************************************************************/
NTSTATUS Example_Close(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    DbgPrint("Example_Close Called \r\n");

    return NtStatus;
}



/**********************************************************************
*
* Example_IoControl
*
*    This is called when an IOCTL is issued on the device handle (DeviceIoControl)
*
**********************************************************************/
NTSTATUS Example_IoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    DbgPrint("Example_IoControl Called \r\n");

    return NtStatus;
}



/**********************************************************************
*
* Example_Read
*
*    This is called when a read is issued on the device handle (ReadFile/ReadFileEx)
*
**********************************************************************/
NTSTATUS Example_Read(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    DbgPrint("Example_Read Called \r\n");

    return NtStatus;
}



/**********************************************************************
*
* Example_WriteDirectIO
*
*    This is called when a write is issued on the device handle (WriteFile/WriteFileEx)
*
*    This version uses Direct I/O
*
**********************************************************************/
NTSTATUS Example_WriteDirectIO(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    PIO_STACK_LOCATION pIoStackIrp = NULL;
    PCHAR pWriteDataBuffer;

    DbgPrint("Example_WriteDirectIO Called \r\n");
   
    /*
     * Each time the IRP is passed down the driver stack a new stack location is added
     * specifying certain parameters for the IRP to the driver.
     */
    pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);
   
    if(pIoStackIrp)
    {
        pWriteDataBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
   
        if(pWriteDataBuffer)
        {                            
            /*
             * We need to verify that the string is NULL terminated. Bad things can happen
             * if we access memory not valid while in the Kernel.
             */
           if(Example_IsStringTerminated(pWriteDataBuffer, pIoStackIrp->Parameters.Write.Length))
           {
                DbgPrint(pWriteDataBuffer);
           }
        }
    }

    return NtStatus;
}

/**********************************************************************
*
* Example_WriteBufferedIO
*
*    This is called when a write is issued on the device handle (WriteFile/WriteFileEx)
*
*    This version uses Buffered I/O
*
**********************************************************************/
NTSTATUS Example_WriteBufferedIO(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    PIO_STACK_LOCATION pIoStackIrp = NULL;
    PCHAR pWriteDataBuffer;

    DbgPrint("Example_WriteBufferedIO Called \r\n");
   
    /*
     * Each time the IRP is passed down the driver stack a new stack location is added
     * specifying certain parameters for the IRP to the driver.
     */
    pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);
   
    if(pIoStackIrp)
    {
        pWriteDataBuffer = (PCHAR)Irp->AssociatedIrp.SystemBuffer;
   
        if(pWriteDataBuffer)
        {                            
            /*
             * We need to verify that the string is NULL terminated. Bad things can happen
             * if we access memory not valid while in the Kernel.
             */
           if(Example_IsStringTerminated(pWriteDataBuffer, pIoStackIrp->Parameters.Write.Length))
           {
                DbgPrint(pWriteDataBuffer);
           }
        }
    }

    return NtStatus;
}

/**********************************************************************
*
* Example_WriteNeither
*
*    This is called when a write is issued on the device handle (WriteFile/WriteFileEx)
*
*    This version uses Neither buffered or direct I/O. User mode memory is
*    read directly.
*
**********************************************************************/
NTSTATUS Example_WriteNeither(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS NtStatus = STATUS_SUCCESS;
    PIO_STACK_LOCATION pIoStackIrp = NULL;
    PCHAR pWriteDataBuffer;

    DbgPrint("Example_WriteNeither Called \r\n");
   
    /*
     * Each time the IRP is passed down the driver stack a new stack location is added
     * specifying certain parameters for the IRP to the driver.
     */
    pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);
   
    if(pIoStackIrp)
    {
        /*
         * We need this in an exception handler or else we could trap.
         */
        __try {
       
                ProbeForRead(Irp->UserBuffer, pIoStackIrp->Parameters.Write.Length, TYPE_ALIGNMENT(char));
                pWriteDataBuffer = Irp->UserBuffer;
           
                if(pWriteDataBuffer)
                {                            
                    /*
                     * We need to verify that the string is NULL terminated. Bad things can happen
                     * if we access memory not valid while in the Kernel.
                     */
                   if(Example_IsStringTerminated(pWriteDataBuffer, pIoStackIrp->Parameters.Write.Length))
                   {
                        DbgPrint(pWriteDataBuffer);
                   }
                }

        } __except( EXCEPTION_EXECUTE_HANDLER ) {

              NtStatus = GetExceptionCode();    
        }

    }

    return NtStatus;
}
                      

/**********************************************************************
*
* Example_UnSupportedFunction
*
*    This is called when a major function is issued that isn't supported.
*
**********************************************************************/
NTSTATUS Example_UnSupportedFunction(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    NTSTATUS NtStatus = STATUS_NOT_SUPPORTED;
    DbgPrint("Example_UnSupportedFunction Called \r\n");

    return NtStatus;
}


/**********************************************************************
*
* Example_IsStringTerminated
*
*    Simple function to determine a string is NULL terminated.
*
**** We could validate also the characters in the string are printable! ***
*
**********************************************************************/
BOOLEAN Example_IsStringTerminated(PCHAR pString, UINT uiLength)
{
    BOOLEAN bStringIsTerminated = FALSE;
    UINT uiIndex = 0;

    while(uiIndex < uiLength && bStringIsTerminated == FALSE)
    {
        if(pString[uiIndex] == '\0')
        {
            bStringIsTerminated = TRUE;
        }
        else
        {
           uiIndex++;
        }
    }

    return bStringIsTerminated;
}

分析:
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath);
DRIVER_OBJECT是一个数据结构,用来代表一个驱动。DriverEntry例程使用它来设置其他处理特殊I/O请求的驱动入口点。
在这个结构中还有一个指向DEVICE_OBJECT的指针。DEVICE_OBJECT用来代表一个特定的设备。一个驱动实际上可以创建多个设备。
这些设备组成一个链表,存放在DEVICE_OBJECT.NextDevice.
pRegistryPath:是一个字符串指针,指出了驱动信息在注册表位置。驱动程序可以利用它在注册表中存储一些特殊的信息。
内核程序使用IopLoadDriver来加载驱动,读读这个函数,可以完全了解DRIVER_OBJECT结构中各项的意义。

下一步我们来创建一个设备,你或许奇怪我们如何来创建设备,创建什么类型的设备?平时我们谈到驱动都是与硬件相关的
,如声卡驱动,网卡驱动等等。其实驱动有不同的类型,分别工作在不同的层级,并不是所有的驱动都是直接与硬件打交道的。
由于驱动是分层的,最高层的驱动与用户态通讯。最底层的驱动与硬件打交道。每一层驱动都有自己的堆栈他们会将请求分解
成为一个对于更底层的驱动而言更加易于处理的一个请求。

我们来看看我们自己的“DriverEntry”的第一部分吧。
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
UINT uiIndex = 0;
PDEVICE_OBJECT pDeviceObject = NULL;
UNICODE_STRING usDriverName, usDosDeviceName;

DbgPrint("DriverEntry Called \r\n");

RtlInitUnicodeString(&usDriverName, L"\\Device\\Example");
RtlInitUnicodeString(&usDosDeviceName, L"\\DosDevices\\Example");

NtStatus = IoCreateDevice(pDriverObject, 0,
&usDriverName,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE, &pDeviceObject);

其中的DbgPrint函数的工作很像“printf”,它会把数据输出到调试器或者是调试输出窗口上。你可以从www.sysinternals.com获取一个名为“DbgView”的工具,所有使用这个函数输出的信息这个工具都会显示出来。

之后你会注意到的是我们调用的函数“RtlInitUnicodeString”,这个函数只是初始化一个UNICODE_STRING数据块。这种数据块包含三个项目,第一个项是
Unicode字符串的长度,第二个项是Unicode的最大长度,第三个项是一个指向Unicode字符串的指针。这种数据块是用来描述一个
Unicode字符串,它在驱动中用的很多。唯一的一个关于UNICODE_STRING需要记住的事情就是,它们不是以NULL来结尾的,而是以数据结
构中的描述“大小”的参数来决定的。对于那些想当然的认为UNICODE_STRING是以NULL来结尾的驱动开发新手来说,这很容易写出“蓝屏驱
动”。大多数传递到你的驱动中的Unicode字符串都不会以NULL来结尾,所以这也是一个你需要意识到的问题。

设备也有名字。它们的名字通常类似于\Device\<somename>
这其实是一个我们通过IoCreateDevice来创建的一个字符串。第二个字符串是“\DosDevices\Example”,我们稍后将会讲到
他,因为我们现在的驱动中还用不到。对于函数IoCreateDevice而言,我们传入的有:一个驱动程序对象,一个指向包含有我们调用驱动所使用字串
的Unicode字符串,如果这个驱动程序和任何设备都无关的话,那么还会有一个被成为“UNKNOWN”的驱动类型。我们也要传入一个指针,以便于接收
新创建的驱动对象。
第二个参数我们传0进去,这个参数是指定用来创建新设备扩展的字节数的。这是一个驱动可以由驱动开发人员来声明的,并且对于设备并非唯一的一个数据结构。
你可以利用这个数据结构来扩展能储存数据的实例所传入信息,比如对驱动程序和创建驱动上下文等。不过在我们的例子中,我们不会使用这些东西。
现在我们已经成功的创建了我们的\\Device\\Example驱动程序,为了让我们的驱动能够响应用户态的IRP主要请求,我们应当设置驱动对象。当然,在IRP栈中也存在一些子请求,这些子请求就是次要请求。
以下代码组织好了所有确定的请求:
for(uiIndex = 0; uiIndex < IRP_MJ_MAXIMUM_FUNCTION; uiIndex++)
pDriverObject->MajorFunction[uiIndex] = Example_UnSupportedFunction;

pDriverObject->MajorFunction[IRP_MJ_CLOSE] = Example_Close;
pDriverObject->MajorFunction[IRP_MJ_CREATE] = Example_Create;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Example_IoControl;
pDriverObject->MajorFunction[IRP_MJ_READ] = Example_Read;
  pDriverObject->MajorFunction[IRP_MJ_WRITE] = USE_WRITE_FUNCTION;

我们把创建,关闭,I/O控制,读和写都组织好了。这也例程意味着什么呢?当我们和用户态的应用程序通讯的时候,这些API调用会直接把参数传入驱动!
  • CreateFile -> IRP_MJ_CREATE
  • CloseHandle -> IRP_MJ_CLEANUP & IRP_MJ_CLOSE
  • WriteFile -> IRP_MJ_WRITE
  • ReadFile-> IRP_MJ_READ
  • DeviceIoControl -> IRP_MJ_DEVICE_CONTROL
需要解释的是,IRP_MJ_CLOSE不是在创建句柄的进程的上下文中调用的。如果你想要进程进行相关的清理操作,你需要通过操纵IRP_MJ_CLEANUP来替代。
如你所见,当一个用户态程序调用了这些函数,它会调用你驱动中相应的例程。你也许想知道为什么用户态的“文件”并不意味着真正的“文件”。的确是这样的,
这些API可以和任何将自己展现给用户态的设备通讯,而不是只访问文件。在这篇文章的结尾,我们将会写一个和我们的驱动通讯的用户态程序,而这个程序就是通过CreateFile,WriteFile,
CloseHandle来通讯的。这是多么简单的啊!至于USE_WRITE_FUNCTION,它是一个常数,稍后我会解释的。

下一步很容易,它是驱动的卸载函数。
pDriverObject->DriverUnload = Example_Unload;

在技术上你可以忽略这个函数,但是如果你想要动态的卸载你的驱动程序,那么它一定要被列出。如果你的驱动中没有这个函数,那么系统不会允许你卸载这个驱动。
之后的代码实际上是DEVICE_OBJECT,而不是DRIVER_OBJECT。由于他们都是以“D”开头,同时又都是以“_OBJECT”结尾的,所以可能会有点难以区分,所以我们很容易混淆他们。
pDeviceObject->Flags |= IO_TYPE;
pDeviceObject->Flags &= (~DO_DEVICE_INITIALIZING);
我们仅仅是设置了标志。“IO_TYPE”其实是一个用来定义我们所想要做的I/O类型的常数(我在example.h中定义了它)。我在操作用户态写请求的部分会进行解释。
“DO_DEVICE_INITIALIZING”用来告诉I/O管理器设备正在被初始化以及不要向驱动发送任何的I/O请求。对于在“DriverEntry”的上下文中创建的设备,当
“DriverEntry”完成的时候不需要清除这个标志,然而,如果你在“DriverEntry”之外的任何例程中设定了这个标志,那么你必须为任何你使用IoCreateDevice创建的设备通过手动清除这个标志,
事实上这个标志是由IoCreateDevice设定的,我们在这儿清除这个标志其实只是为了玩笑而已——因为我们并不需要这么做。
我们的驱动的最后一部分是使用我们在之前定义的Unicode字符串。“\Device\Example”和“\DosDevices\Example”。
IoCreateSymbolicLink(&usDosDeviceName, &usDriverName);
IoCreateSymbolicLink只做了这些事情,它在对象管理器中创建了一个“符号链接”。你可以下载我的工具“QuickView”,或者是去www.sysinternals.com下载“WinObj”来查看对象管理器。一个符号链接仅仅是把一个“DOS设备名”映射为“NT设备名”而已。
在这个例子中,“Example”是我们的DOS设备名,“\Device\Example”是我们的NT设备名。
我们接下来要看的代码段是卸载例程。这个例程是够动态的卸载驱动所需要的。这个部分比较短小而且没有什么可解释的。

VOID Example_Unload(PDRIVER_OBJECT DriverObject)
{

UNICODE_STRING usDosDeviceName;

DbgPrint("Example_Unload Called \r\n");

RtlInitUnicodeString(&usDosDeviceName, L"\\DosDevices\\Example");
IoDeleteSymbolicLink(&usDosDeviceName);

IoDeleteDevice(DriverObject->DeviceObject);
}
你可以在你的卸载进程中做任何你喜欢的事情。这个卸载例程很简单,它只是删除了我们之前创建的符号链接和被我们命名为\Device\Example的设备。

如果你使用过WriteFile和ReadFile,你应当知道调用函数的时候你只是传入一个指向缓冲区来实现写入数据或是从设备中读取数据。这些参数就如我们之前解释的那样,在IRP中被传入驱动。
尽管输入输出管理器在IRP传递到驱动之前有三种方法来排列数据,但是其实还是有更多的故事的。这也意味着数据的排列方式决定了驱动程序的读写函数需要如何分析传入的数据。

这三种方法是“直接输入输出”,“被缓冲的输入输出”,“两者皆非”。
#ifdef __USE_DIRECT__
#define IO_TYPE DO_DIRECT_IO
#define USE_WRITE_FUNCTION Example_WriteDirectIO
#endif

#ifdef __USE_BUFFERED__
#define IO_TYPE DO_BUFFERED_IO
#define USE_WRITE_FUNCTION Example_WriteBufferedIO
#endif

#ifndef IO_TYPE
#define IO_TYPE 0
#define USE_WRITE_FUNCTION Example_WriteNeither
#endif

这段代码意味着,如果你在头文件中定义了“__USE_DIRECT__”,那么IO_TYPE将是DO_DIRECT_IO并且USE_WRITE_FUNCTION指向Example_WriteDirectIO。
如果你在头文件中定义了“__USE_BUFFERED__”,那么IO_TYPE将是DO_BUFFERED_IO并且USE_WRITE_FUNCTION指向Example_WriteBufferedIO。
如果你在头文件中既没有定义__USE_DIRECT__也没有定义__USE_BUFFERED__,那么IO_TYPE将被定义为0(两者皆非)并且写入函数指向Example_WriteNeither。

我们将会讲解每一种类型的输入输出。
Direct I/O:

NTSTATUS Example_WriteDirectIO(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PIO_STACK_LOCATION pIoStackIrp = NULL;
PCHAR pWriteDataBuffer;

DbgPrint("Example_WriteDirectIO Called \r\n");

/*
* Each time the IRP is passed down
* the driver stack a new stack location is added
* specifying certain parameters for the IRP to the driver.
*/

pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);

if(pIoStackIrp)
{
pWriteDataBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);

if(pWriteDataBuffer)
{
/*
* We need to verify that the string
* is NULL terminated. Bad things can happen
* if we access memory not valid while in the Kernel.
*/

if(Example_IsStringTerminated(pWriteDataBuffer, pIoStackIrp->Parameters.Write.Length))
{
DbgPrint(pWriteDataBuffer);
}
}
}

return NtStatus;
}

我们所做的第一件事是调用“IoGetCurrentIrpStackLocation”,这个函数只是给我们提供我们的IO_STACK_LOCATION。
在我们的例子中,惟一一个我们需要从它之中获取的参数是提供给驱动的缓冲区长度,它位于Parameters.Write.Length。
缓冲输入输出所工作的方法是提供给你一个“MdlAddress”,它是“内存描述表”。这个表是一个对用户态的内存地
址以及如何将其映射到物理地址的描述。我们可以调用
MmGetSystemAddressForMdlSafe这个函数,这个操作可以给我们一个系统虚拟地址,
我们可以使用这个虚拟地址来读内存。
所以,这只是简单的映射被用户态使用的物理页面到系统内存中。我们可以使用它的返回值来访问被用户态所传入的缓冲区。
这种方法通常用于较大的换用区因为他并不需要内存来复制。用户态的缓冲区在IRP请求完成之前是被锁定的,这是使用直接输入输出的不利方面。这是唯一的不好之处,也是为什么它通常对较大缓冲区而言更有用的原因。
Buffered I/O :
NTSTATUS Example_WriteBufferedIO(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PIO_STACK_LOCATION pIoStackIrp = NULL;
PCHAR pWriteDataBuffer;

DbgPrint("Example_WriteBufferedIO Called \r\n");

/*
* Each time the IRP is passed down
* the driver stack a new stack location is added
* specifying certain parameters for the IRP to the driver.
*/

pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);

if(pIoStackIrp)
{
pWriteDataBuffer = (PCHAR)Irp->AssociatedIrp.SystemBuffer;

if(pWriteDataBuffer)
{
/*
* We need to verify that the string
* is NULL terminated. Bad things can happen
* if we access memory not valid while in the Kernel.
*/

if(Example_IsStringTerminated(pWriteDataBuffer,
pIoStackIrp->Parameters.Write.Length))
{
DbgPrint(pWriteDataBuffer);
}
}
}

return NtStatus;
}

这种方法的好处是传送到驱动程序中的数据可以在任何上下文中,比如另外一个位于其他进程中的线程的上下文中被访问。另外一个原因是映射内存到未分页的页面中,以便于驱动也能够在被提升的IRQL等级中读取。

使用Buffered I/O的不好之处在于它分配了未分页的内存并且进行了复制。这总是凌驾于驱动驱动每次的读写之上的。
这也是它最好被用于较小的缓冲区的原因之一。整个用户态的页不需要再像直接访问输入输出那样被锁定,这是它的好的方面。另外一个将它用于较大缓冲区的问题
是,由于他分配了未分页内存,它也许会需要在连续的未分页内存中分配较大的一个块。

Neither Buffered nor Direct:
NTSTATUS Example_WriteNeither(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PIO_STACK_LOCATION pIoStackIrp = NULL;
PCHAR pWriteDataBuffer;

DbgPrint("Example_WriteNeither Called \r\n");

/*
* Each time the IRP is passed down
* the driver stack a new stack location is added
* specifying certain parameters for the IRP to the driver.
*/

pIoStackIrp = IoGetCurrentIrpStackLocation(Irp);

if(pIoStackIrp)
{
/*
* We need this in an exception handler or else we could trap.
*/

__try {

ProbeForRead(Irp->UserBuffer,
pIoStackIrp->Parameters.Write.Length,
TYPE_ALIGNMENT(char));
pWriteDataBuffer = Irp->UserBuffer;

if(pWriteDataBuffer)
{
/*
* We need to verify that the string
* is NULL terminated. Bad things can happen
* if we access memory not valid while in the Kernel.
*/

if(Example_IsStringTerminated(pWriteDataBuffer,
pIoStackIrp->Parameters.Write.Length))
{
DbgPrint(pWriteDataBuffer);
}
}

} __except( EXCEPTION_EXECUTE_HANDLER ) {

NtStatus = GetExceptionCode();
}

}

return NtStatus;
}
在这个方法中,驱动直接访问用户态内存。输入输出管理器不会复制数据,但是也不会锁定用户态内存中的页。它只不过将用户态缓冲区的地址交给了驱动。
好处是没有数据被复制,没有内存被分配,并且也没有页被锁定到内存中。不好之处在于你必须在调用线程的上下文中处理,
否则你将无法正确的访问到用户态内存。另外一个不好之处是进程自身有可能会在另一个线程中尝试去修改页的访问权限,释放内存,等等。
这就是为什么你通常想要使用“ProbeForRead”和“ProbeForWrite”函数并且给所有的代码都写上异常处理。没有什么可以担保在任何时候页都是可用的,
你可以简单的在你尝试读写之前,尝试确认他们是可用的。这个缓冲区位于Irp->UserBuffer。








接上:
动态加载和卸载驱动,代码如下:
int _cdecl main(void)
{
HANDLE hSCManager;
HANDLE hService;
SERVICE_STATUS ss;

hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);

printf("Load Driver\n");

if(hSCManager)
{
printf("Create Service\n");

hService = CreateService(hSCManager, "Example",
"Example Driver",
SERVICE_START | DELETE | SERVICE_STOP,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE,
"C:\\example.sys",
NULL, NULL, NULL, NULL, NULL);

if(!hService)
{
hService = OpenService(hSCManager, "Example",
SERVICE_START | DELETE | SERVICE_STOP);
}

if(hService)
{
printf("Start Service\n");

StartService(hService, 0, NULL);
printf("Press Enter to close service\r\n");
getchar();
ControlService(hService, SERVICE_CONTROL_STOP, &ss);

DeleteService(hService);

CloseServiceHandle(hService);

}

CloseServiceHandle(hSCManager);
}

return 0;
}
与设备驱动通讯,代码如下:
int _cdecl main(void)
{
HANDLE hFile;
DWORD dwReturn;

hFile = CreateFile("\\\\.\\Example",
GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);

if(hFile)
{
WriteFile(hFile, "Hello from user mode!",
sizeof("Hello from user mode!"), &dwReturn, NULL);
CloseHandle(hFile);
}

return 0;
}

posted on 2009-03-23 21:53 iniwf 阅读(389) 评论(0)  编辑 收藏 引用 所属分类: 驱动


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

常用链接

留言簿(2)

随笔分类

随笔档案

收藏夹

IT技术

积分与排名

最新评论

阅读排行榜

评论排行榜