HEIGHT: 18pt; TEXT-ALIGN: left; mso-pagination: widow-orphan; mso-char-indent-count: 2.0" align="left">   if( Handle == INVALID_HANDLE_VALUE )

 

   {

 

      // See Microsoft KB Article 259131

 

      Handle = CreateFile(

 

         "\\\\.\\Globals\\PassThru",

 

         DesiredAccess,

 

         ShareMode,

 

         lpSecurityAttributes,

 

         CreationDistribution,

 

         FlagsAndAttributes,

 

         TemplateFile

 

上一篇 | 下一篇 文摘 订阅所有文摘的日志

扩展微软DDK中的NDIS IM驱动的功能:添加一个DeviceIoControl接口

字体大小:

作者:Thomas F. Divine  译:feikoo

 

 微软的Windows驱动开发工具(DDK) 附带的NDIS中间层驱动PassThru源代码给我们提供了一个极好的示例,它向我们展示了实现NDIS 中间层过滤驱动框架一种方法。(感谢NDIS开发团队)。但是,PassThru中缺乏实际的功能。要想让它具备实际的用途,我们必须进行下一步的工作,就是向这个框架中添加自己的功能。如果你是Windows驱动开发或NDIS驱动开发的新手,接下来的工作可是一件苦差。

 

 本文接下来以微软Windows DDK  Build 3790(Windows Server 2003)附带的PassThru NDIS中间层驱动示例为起点,并在此基础上按下列步骤来添加自己的功能:

 

1.         基本的DeviceIoControl接口:提供Win32应用程序与PassThru驱动进行通信的一种基本方法。2.绑定枚举函数:允许Win32应用程序查询PassThru驱动的绑定信息;

 

2.         ADAPT结构引用计数:添加对ADAPT结构的逻辑计数;

 

3.         适配器名柄(Adapter Handle)打开/关闭功能:建立与某一具体命名的PassThru绑定的用户模式句柄的方法。该句柄可以用来在某一适配器上请求,读写I/O和进行其它的操作。

 

4.         处理事件通知:在这节我们将处理已经打开的驱动句柄变为无效的情况(如Pnp)。

 

5.         在一个打开的适配器句柄上查询信息:增加一种用Win32初始化的NDIS请求在一个打开的适配器句柄上查询信息的方法。 接下来的一系列文章将介绍对PassThru的另一些扩展。

 

  . 扩展PassThru驱动(PassThruEx)

 

我们由DDK(以下提及DDK均指Windows DDK Build 3790  Windows Server 2003)中的驱动程序源代码开始。PassThru包含以下所列的这些关键文件:

 

\PassThru - Windows DDK Build 3790 PassThru工程文件夹;

 

         \Driver – PassThru驱动源程序

 

                   PassThru.c – DriveEntry函数和其它PassThru小端口驱动程序与协议驱动程序共用的代码部分。

 

                   PassThru.h – PassThru的头文件;

 

                   Miniport.c – PassThru中与Miniport相关的函数;

 

                   Protocol.c – PassThru中与Protocol相关的函数;

 

                   Precomp.h – 预编译的头文件;

 

                   Sources – 编译工具所用的源文件列表文件;

 

另外,在PassThru.htm中包含了下列重要信息:

 

1.        编译该示例的方法;2. 安装编译好的驱动程序的方法;3. 代码说明

 

文章由易渐难,一步一步地向PassThru中添加功能。每一步中都包含了所添加部分的功能描述和需要修改的代码部分的说明。并且,我们还开发一个Win32应用程序来演示所添加函数的功能。

 

我们将新添中的代码尽量放在新的.C.H文件中,大部分的新增代码被放在了PTExtend.c文件中。其中PassThru 用户I/O(PTUUserIo)这个Win32控制台程序用来演示用户态下功能。整个完整的PassThru工程结构如下:

 

         \PassThru - Windows DDK Build 3790 PassThru工程文件夹;

 

         \Driver – PassThru驱动源程序

 

                   PassThru.c – DriveEntry函数和其它PassThru小端口驱动程序与协议驱动程序共用的代码部分。

 

                   PassThru.h – PassThru的头文件;

 

                   Miniport.c – PassThru中与Miniport相关的函数;

 

                   Protocol.c – PassThru中与Protocol相关的函数;

 

                   Precomp.h – 预编译的头文件;

 

                   Sources – 编译工具所用的源文件列表文件;

 

                   IOCommon.h – 驱动和用户态下程序所共用的头文件;

 

                   PTExtend.c – 包含新加代码的.c文件

 

         \Test – PassThruEX Win32控制台测试程序;

 

                   PTUserIo.cpp – Win32控制台测试程序;

 

                   PTUtils.cpp – 支持文件(次重要)。

 

修改后的PassThru驱动程序和测试程序源代码均可以下载。在此感谢微软件公司提供了使用源代码的许可。

 

 

 

二.添加基本的DeviceIoControl接口

 

 

 

     我们期望读者在看这篇文章之前已经熟悉基于IRP接口的用户/驱动程序编程。应用程序使用基本的为终端用户提供的Win32接口函数:CreateFile,DeviceIoControl,ReadFile,WriteFileCloseHandle

 

     驱动程序创建一个设备对象和一个在Win32用户态下可用CreateFile打开并访问的符号链接名,并注册一些基于IRP的函数,通过这些函数来实现驱动程序内核态的终端用户接口。

 

1. 驱动程序代码:

 

     设备I/O控制接口的代码在PassThru驱动示例中已经列出。其中NdisMRegisterDevice函数被PassThru.c中的PtRegisterDevice方法调用,通过该函数创建了设备对象和Win32用户态下可见的符号链接名字以及注册了用处理I/O请求的函数。

 

(1)PassThru中的代码:PassThru.c中的PtRegisterDevice函数

 

     以下的代码片断摘自PassThru驱动程序PassThru.c:

 

        DispatchTable[IRP_MJ_Create] = PtDispatch;

 

        DispatchTable[IRP_MJ_CLEANUP] = PtDispatch;

 

        DispatchTable[IRP_MJ_CLOSE] = PtDispatch;

 

        DispatchTable[IRP_MJ_DEVICE_CONTROL] = PtDispatch;

 

        NdisInitUnicodeString(&DeviceName, NTDEVICE_STRING);

 

        NdisInitUnicodeString(&DeviceLinkUnicodeString, LINKNAME_STRING);

 

        //

 

        // Create a device object and register our dispatch handlers

 

        //

 

        Status = NdisMRegisterDevice(

 

                    NdisWrapperHandle,

 

                    &DeviceName,

 

                    &DeviceLinkUnicodeString,

 

                    &DispatchTable[0],

 

                    &ControlDeviceObject,

 

                    &NdisDeviceHandle

 

                    );

 

(2)修改后的代码:PassThru.c中的PtRegisterDevice函数

 

在扩展后的PassThru驱动程序中,我们删除了PtDispatch 函数(在PassThru.c中删除PtDispatch 的代码并在PassThru.h中删除其原型),并在该处用分发函数DevOpen, DevCleanup, DevClose and DevIoControl代替。

 

        // BEGIN_PTUSERIO

 

        DispatchTable[IRP_MJ_Create] = DevOpen;

 

        DispatchTable[IRP_MJ_CLEANUP] = DevCleanup;

 

        DispatchTable[IRP_MJ_CLOSE] = DevClose;

 

        DispatchTable[IRP_MJ_DEVICE_CONTROL] = DevIoControl;

 

        // END_PTUSERIO

 

        NdisInitUnicodeString(&DeviceName, NTDEVICE_STRING);

 

        NdisInitUnicodeString(&DeviceLinkUnicodeString, LINKNAME_STRING);

 

        //

 

        // Create a device object and register our dispatch handlers

 

        //

 

        Status = NdisMRegisterDevice(

 

                    NdisWrapperHandle,

 

                    &DeviceName,                   // \\Device\\Passthru

 

                    &DeviceLinkUnicodeString,      // \\DosDevices\\Passthru

 

                    &DispatchTable[0],

 

                    &ControlDeviceObject,

 

                    &NdisDeviceHandle

 

                    );

 

以上所用的函数在PTExtend.c中实现,在文件夹\PassThruEx\Driver中可找到。以下所列为添加的函数的全部代码:

 

These are the skeleton I/O dispatch handlers that are implemented in PTExtend.c. These are sufficient for a quick-and-dirty test of opening and closing a handle on the PassThru device. More functionality will be added.

 

 

 

NTSTATUS

 

DevOpen(

 

    IN PDEVICE_OBJECT    pDeviceObject,

 

    IN PIRP              pIrp

 

    )

 

{

 

    PIO_STACK_LOCATION  pIrpSp;

 

    NTSTATUS            NtStatus = STATUS_SUCCESS;

 

    UNREFERENCED_PARAMETER(pDeviceObject);   

 

    pIrpSp = IoGetCurrentIrpStackLocation(pIrp);  

 

    pIrpSp->FileObject->FsContext = NULL;

 

    pIrpSp->FileObject->FsContext2 = NULL;        

 

    DBGPRINT(("==>Pt DevOpen: FileObject %p\n", pIrpSp->FileObject));

 

         

 

    pIrp->IoStatus.Information = 0;

 

    pIrp->IoStatus.Status = NtStatus;   

 

    IoCompleteRequest(pIrp, IO_NO_INCREMENT);     

 

    DBGPRINT(("<== Pt DevOpen\n"));     

 

    return NtStatus;

 

}

 

 

 

NTSTATUS

 

DevCleanup(

 

    IN PDEVICE_OBJECT    pDeviceObject,

 

    IN PIRP              pIrp

 

    )

 

{

 

    PIO_STACK_LOCATION  pIrpSp;

 

    NTSTATUS            NtStatus = STATUS_SUCCESS;

 

    UNREFERENCED_PARAMETER(pDeviceObject);   

 

    pIrpSp = IoGetCurrentIrpStackLocation(pIrp);  

 

    DBGPRINT(("==>Pt DevCleanup: FileObject %p\n", pIrpSp->FileObject ));        

 

    pIrp->IoStatus.Information = 0;

 

    pIrp->IoStatus.Status = NtStatus;

 

    IoCompleteRequest(pIrp, IO_NO_INCREMENT);     

 

    DBGPRINT(("<== Pt DevCleanup\n"));  

 

    return NtStatus;

 

}

 

 

 

NTSTATUS

 

DevClose(

 

    IN PDEVICE_OBJECT    pDeviceObject,

 

    IN PIRP              pIrp

 

    )

 

{

 

    PIO_STACK_LOCATION  pIrpSp;

 

    NTSTATUS            NtStatus = STATUS_SUCCESS;

 

         

 

    UNREFERENCED_PARAMETER(pDeviceObject);   

 

    pIrpSp = IoGetCurrentIrpStackLocation(pIrp);  

 

    DBGPRINT(("==>Pt DevClose: FileObject %p\n", pIrpSp->FileObject ));          

 

    pIrpSp->FileObject->FsContext = NULL;

 

    pIrpSp->FileObject->FsContext2 = NULL;        

 

    pIrp->IoStatus.Information = 0;

 

    pIrp->IoStatus.Status = NtStatus;

 

    IoCompleteRequest(pIrp, IO_NO_INCREMENT);     

 

    DBGPRINT(("<== Pt DevClose\n"));    

 

    return NtStatus;

 

}

 

 

 

NTSTATUS

 

DevIoControl(

 

    IN PDEVICE_OBJECT    pDeviceObject,

 

    IN PIRP              pIrp

 

    )

 

{

 

    PIO_STACK_LOCATION  pIrpSp;

 

    NTSTATUS            NtStatus = STATUS_SUCCESS;

 

    ULONG               BytesReturned = 0;

 

    ULONG               FunctionCode;

 

    PUCHAR              ioBuffer = NULL;

 

    ULONG               inputBufferLength;

 

    ULONG               outputBufferLength;    

 

    UNREFERENCED_PARAMETER(pDeviceObject);

 

    pIrpSp = IoGetCurrentIrpStackLocation(pIrp);   

 

    ioBuffer = pIrp->AssociatedIrp.SystemBuffer;

 

    inputBufferLength  = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;

 

    outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;   

 

    FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;   

 

    DBGPRINT(("==>Pt DevIoControl: FileObject %p\n", pIrpSp->FileObject ));   

 

    switch (FunctionCode)

 

    {

 

        case IOCTL_PTUSERIO_ENUMERATE:

 

        case IOCTL_PTUSERIO_OPEN_LOWER_ADAPTER:

 

        case IOCTL_PTUSERIO_OPEN_VIRTUAL_ADAPTER:

 

        case IOCTL_PTUSERIO_QUERY_INFORMATION:

 

        case IOCTL_PTUSERIO_SET_INFORMATION:

 

        default:

 

            NtStatus = STATUS_NOT_SUPPORTED;

 

            break;

 

    }

 

   

 

    if (NtStatus != STATUS_PENDING)

 

    {

 

        pIrp->IoStatus.Information = BytesReturned;

 

        pIrp->IoStatus.Status = NtStatus;

 

        IoCompleteRequest(pIrp, IO_NO_INCREMENT);

 

    }

 

   

 

    DBGPRINT(("<== Pt DevIoControl\n"));

 

   

 

    return NtStatus;

 

}

 

2.测试用的应用程序代码:

 

尽管I/O分发处理函数较简单,但是它足以让我们开始编译并测试它。在此,我们添加代码打开并关闭在符号链接名上的句柄。

 

测试程序叫作“PassThru用户I/O”,它是一个MFC控制台程序,在PTUserIO.cpp中实现,放在\PassThruEx\Test文件夹中。

 

在这一步中,我们添加的用户态函数主要是PtOpenControlChannel ,它通过CreateFile函数在标准文件名\\.\PassThru上打开一个句柄,这个用于控制的句柄是一个普通的句柄类型,它具体的适配器绑定没有关系。控制渠道(Control Channel)用于访问全局信息如驱动绑定列表等。

 

_tMain函数主要功能是调用PtOpenControlChannel ,当安装上我们修改后的驱动程序后,PtOpenControlChannel 函数应该能成功返回;当成功得到一个句柄后,此处不做任何事情,仅仅将其关闭即可。

 

/////////////////////////////////////////////////////////////////////////////

 

//// PtOpenControlChannel

 

//

 

// Purpose

 

// Open a "control channel" handle on the PassThru device.

 

//

 

// Parameters

 

//    None.

 

//

 

// Return Value

 

//   The INVALIE_HANDLE_VALUE if unsuccessful. Otherwise, a valid handle

 

//   to the passthru device.

 

//

 

// Remarks

 

//   There are no parameters to this function because the PassThru filespec

 

//   name is already known. For PassThru this is "\\.\PassThru" or

 

//   "\\.\Global\PassThru"

 

//

 

//   This call opens a "control channel". That is, a handle that can be

 

//   used for DeviceIoControl calls but is not associated with a specific

 

//   adapter.

 

//

 

//   Notice that the FILE_FLAG_OVERLAPPED attribute is not specified. The

 

//   returned handle is used for synchronous operations only.

 

//

 

//   A more sophisticated API would employ asynchronous I/O. However, a

 

//   sample of that complexity is beyond the scope of this article.

 

//

 

HANDLE

 

PtOpenControlChannel( void )

 

{

 

   DWORD        DesiredAccess;

 

   DWORD        ShareMode;

 

   LPSECURITY_ATTRIBUTES          lpSecurityAttributes = NULL;

 

   DWORD        CreationDistribution;

 

   DWORD        FlagsAndAttributes;

 

   HANDLE       TemplateFile;

 

   HANDLE       Handle;

 

   //

 

   // Use CreateFile to Open the Handle

 

   //

 

   DesiredAccess = GENERIC_READ|GENERIC_WRITE;

 

   ShareMode = 0;

 

   CreationDistribution = OPEN_EXISTING;

 

   FlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;

 

   TemplateFile = (HANDLE)INVALID_HANDLE_VALUE;

 

   Handle = CreateFile(

 

      "\\\\.\\PassThru",

 

      DesiredAccess,

 

      ShareMode,

 

      lpSecurityAttributes,

 

      CreationDistribution,

 

      FlagsAndAttributes,

 

      TemplateFile

 

      );