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

July 15, 2003
作者: 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,WriteFile CloseHandle
     驱动程序创建一个设备对象和一个在 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
      );
   if( Handle == INVALID_HANDLE_VALUE )
   {
      // See Microsoft KB Article 259131
      Handle = CreateFile(
         ".//Globals//PassThru",
         DesiredAccess,
         ShareMode,
         lpSecurityAttributes,
         CreationDistribution,
         FlagsAndAttributes,
         TemplateFile
         );
   }
   return (Handle);
}
 
/
_tmain
//
// Purpose
// PTUserIo MFC console application MAIN entry point.
//
// Parameters
//
// Return Value
//
// Remarks
//
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  int nRetCode = 0;
 
   //
   // Open A Handle On The PassThru Device
   //
   HANDLE PtHandle = PtOpenControlChannel();
   if( PtHandle == INVALID_HANDLE_VALUE )
   {
      cout << "PassThru Handle Open Failed" << endl;
      nRetCode = 1;
      return nRetCode;
   }
   cout << "PassThru Handle Open Successful" << endl;
   CloseHandle( PtHandle );
   return nRetCode;
}
 
添加一个枚举绑定信息的函数
添加到 PassThru 驱动程序中的第一个有用的函数是用于查询当前适配器绑定关系的。返回的绑定关系名称用于区分适配器上的绑定关系,并可用于其它基于绑定关系的函数中。
这个函数可直接实现,我们在 PtOpenControlChannel 返回的句柄 基础上调用 DeviceIoControl IOCTL_PTUSERIO_ENUMERATE 命令(在 IoPrecommon.h 中定义)向驱动程序传递一个输出缓冲区指针,相应的分发函数(前面添加的四个之一)就会在缓冲中填入相应的绑定信息字符串。
DeviceIoControl 返回时,用户就在缓冲区中得到了绑定关系信息。
1. 驱动代码:
保存绑定关系名的必要工作在原来的 PassThru 代码中已经实现,我们要做是添加一个处理 IOCTL_PTUSERIO_ENUMERATE 命令的函数来向用户缓冲区填写数据。
绑定关系名保存在由 PtBindAdapter 成功调用时创建的 ADAPT 结构中,虚拟适配器名(传递给 NdisIMInitializeDeviceInstanceEx 的名字)保存在 ADAPT 结构中的 DeviceName 字段中。
ADAPT 结构本身保存在由 pAdaptList 指向的单链表中。
我们要做的工作就是将从 ADAPT 结构中 DeviceName 字段提取的字符串放到用户提供的缓冲区中。在 PTExtend.c 中,添加一个 DevEnumerateBindings 来完成此工作。此函数由 DevIoControl IOCTL 处理 IOCTL_PTUSERIO_ENUMERATE 命令时调用分发函数时调用它。 缓冲区中最终内容为 NULL 结尾的 UNICODE 字符串, LIST 则以空 UNICODE 字符串结尾。
有一点需要注意:当我们在遍历 pAdaptList 和向缓冲区中拷贝绑定关系名时, pAdaptList 列表中的内容可能会发生变化,原 PassThru 代码中的 Spinlock 就是用于解决此问题,当我们要检查列表时,只需要先获取 ClobalLock 这个互斥变量即可(用于互斥)。另外,可添加错误检查 ___try….__except….. 语句来阻止错误发生。
< 点击下面的链接查看枚举绑定关系的代码 >
以下是这部分的完整代码:
NTSTATUS
DevEnumerateBindings(
   IN PDEVICE_OBJECT    pDeviceObject,
   IN PIRP              pIrp
   )
{
   PIO_STACK_LOCATION pIrpSp;
   NTSTATUS            NtStatus = STATUS_SUCCESS;
   ULONG               BytesReturned = 0;
   PUCHAR              ioBuffer = NULL;
   ULONG               inputBufferLength;
   ULONG               outputBufferLength, Remaining;
   PADAPT              *ppCursor;
   pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
   ioBuffer = pIrp->AssociatedIrp.SystemBuffer;
   inputBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
   outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
   Remaining = outputBufferLength;
   //
   // Sanity Check On Length
   //
   if( sizeof( UNICODE_NULL ) > Remaining
)
   {
      BytesReturned = 0;
      NtStatus = NDIS_STATUS_BUFFER_OVERFLOW;
      goto CompleteTheIRP;
   }
   //
   // Walk The Adapter List
   //
   NdisAcquireSpinLock( &GlobalLock );
   __try
   {
      //
      // Insert List-Terminating NULL
      //
      *((PWCHAR )ioBuffer) = UNICODE_NULL;
      BytesReturned = sizeof( UNICODE_NULL );
      Remaining -= sizeof( UNICODE_NULL );
      for( ppCursor = &pAdaptList; *ppCursor != NULL; ppCursor = &(*ppCursor)->Next )
      {
         //
         // Sanity Check On Length
         //
         if( (*ppCursor)->DeviceName.Length sizeof( UNICODE_NULL) > Remaining ) {
            BytesReturned = 0;
            NtStatus = NDIS_STATUS_BUFFER_OVERFLOW;
            break;
         }
         //
         // Add The Virtual DeviceName To The Buffer
         // ----------------------------------------
         // This name passed to NdisIMInitializeDeviceInstanceEx.
         //        
                    NdisMoveMemory(ioBuffer, (*ppCursor)->DeviceName.Buffer, (*ppCursor)->DeviceName.Length );
         //
         // Move Past Virtual DeviceName In Buffer
         //
         Remaining -= (*ppCursor)->DeviceName.Length;
         BytesReturned = (*ppCursor)->DeviceName.Length;
         ioBuffer = (*ppCursor)->DeviceName.Length;
         //
         // Add Name-Terminating NULL
         //
         *((PWCHAR )ioBuffer) = UNICODE_NULL;
         Remaining -= sizeof( UNICODE_NULL );
         BytesReturned = sizeof( UNICODE_NULL );
         ioBuffer = sizeof( UNICODE_NULL );
         //
         // Add List-Terminating NULL
         // -------------------------
         // Space is already accomodated for this.
         //
         *((PWCHAR )ioBuffer) = UNICODE_NULL;
      }
   }
   __except( EXCEPTION_EXECUTE_HANDLER )
   {
      BytesReturned = 0;
      NtStatus = STATUS_INVALID_PARAMETER;
   }
   NdisReleaseSpinLock( &GlobalLock );
CompleteTheIRP:
   if (NtStatus != STATUS_PENDING)
   {
      pIrp->IoStatus.Information = BytesReturned;
      pIrp->IoStatus.Status = NtStatus;
      IoCompleteRequest(pIrp, IO_NO_INCREMENT);
   }
   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:
         return( DevEnumerateBindings(pDeviceObject, pIrp) );
        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. 测试程序代码
这里需要写一个用户程序用于演示如何通过驱动得到相关信息以及获得相关的绑定信息。
这个函数可直接实现,我们在 PtOpenControlChannel 返回的句柄基础上调用 DeviceIoControl ,通过命令 IOCTL_PTUSERIO_ENUMERATE( precommon.h 中定义 ) 将用户缓冲区指针传递给驱动程序。 DeviceIoControl 分发函数会调用相应的函数向用户缓冲区中填写数据。
第一步我们先写一个 DeviceIoControl 简单的 Wrapper 函数 PtEnumerateBindings ,这个函数使命令 IOCTL_PTUSERIO_ENUMERATE 将用户缓冲区指针传递给驱动程序。如果调用成功,则返回填满绑定关系信息的缓冲区指针。
缓冲区是一个以 NULL 结尾的字符串,字符串列表的结尾以 UNICODE 空字符串结尾。
函数 _tmain() 调用 PtEnumerateBindings 。如果成功,则它遍历整个缓冲区并在控制台中显示出来。
如果程序一切 OK 的话,则将显示以下的信息:
PassThru User I/O Test Application
Copyright (c) 2003 Printing Communications Assoc., Inc. (PCAUSA)
All rights reserved.
Driver Bindings:
   "/Device/{67A4853E-1940-43A3-A442-74701B5133B0}"
   "/Device/{0611AD65-41D8-4BB1-8A8F-43008BB362A3}"
   "/Device/{8DA82E8E-D091-4FB2-902A-673FBEC2DA7C}"
虽然以上的信息可读性很差,但是我可以断定我们的程序工作得很好。
以上程序在装有 NDIS 小端口驱动程序(网卡驱动)的 Windows XP sp1 上正常运行。如果调用其它的 API IOCTL_NDIS_QUERY_GLOBAL_STATS 则可以显示其它的信息(具体内容跟测试用的机器的配置有关系):
PassThru User I/O Test Application
Copyright (c) 2003 Printing Communications Assoc., Inc. (PCAUSA)
All rights reserved.
Driver Bindings:
   "/Device/{67A4853E-1940-43A3-A442-74701B5133B0}"
      Description: " Intel 8255x-based Integrated Fast Ethernet"
      Medium: 802.3
      Mac address = 00-00-39-14-92-A9
      Media Connect Status: Disconnected
   "/Device/{0611AD65-41D8-4BB1-8A8F-43008BB362A3}"
      Description: " NdisWan Adapter"
      Medium: 802.3
      Mac address = C0-F2-20-52-41-53
      Media Connect Status: Connected
   "/Device/{8DA82E8E-D091-4FB2-902A-673FBEC2DA7C}"
      Description: "3CRWE737A AirConnect Wireless LAN PC Card"
      Medium: 802.3
      Mac address = 00-50-DA-03-4E-6C
      Media Connect Status: Connected
If all is well running the PTUserIo application will display the binding names on the console:
 
PassThru User I/O Test Application
Copyright (c) 2003 Printing Communications Assoc., Inc. (PCAUSA)
All rights reserved.
Driver Bindings:
   "/Device/{67A4853E-1940-43A3-A442-74701B5133B0}"
   "/Device/{0611AD65-41D8-4BB1-8A8F-43008BB362A3}"
   "/Device/{8DA82E8E-D091-4FB2-902A-673FBEC2DA7C}"
This display is hardly human readable. However, it does confirm that the binding enumeration function works.
 
This test was run on a Windows XP SP1 system with three NDIS miniports installed. Using other APIs we can display additional information describing each binding:
 
PassThru User I/O Test Application
Copyright (c) 2003 Printing Communications Assoc., Inc. (PCAUSA)
All rights reserved.
Driver Bindings:
   "/Device/{67A4853E-1940-43A3-A442-74701B5133B0}"
      Description: " Intel 8255x-based Integrated Fast Ethernet"
      Medium: 802.3
      Mac address = 00-00-39-14-92-A9
      Media Connect Status: Disconnected
   "/Device/{0611AD65-41D8-4BB1-8A8F-43008BB362A3}"
      Description: " NdisWan Adapter"
      Medium: 802.3
      Mac address = C0-F2-20-52-41-53
      Media Connect Status: Connected
   "/Device/{8DA82E8E-D091-4FB2-902A-673FBEC2DA7C}"
      Description: "3CRWE737A AirConnect Wireless LAN PC Card"
      Medium: 802.3
      Mac address = 00-50-DA-03-4E-6C
      Media Connect Status: Connected
  /
PtEnumerateBindings
//
// Purpose
// Use DeviceIoControl to query the PassThru device for a list of its
// current bindings.
//
// Parameters
//   PtHandle - Handle returned from a successful call to PtOpenControlChannel.
//   Buf       - Pointer to a unsigned character array to be filled with
//               the PassThru driver's binding information.
//   BufLength - Pointer to a DWORD. On input this variable must be filled with
//               the size (in bytes) of the buffer. On successful completion the
//               variable is used to return the number of bytes written to the
//               buffer.
//
// Return Value
//   Returns TRUE if the I/O operation was successful. In this case the
//   variable pointed to by BufLength is used to return the number of
//   bytes written to Buff.
//
//   Returns FALSE if the operation was unsuccessful. In this case additional
//   error information can be fetched by calling GetLastError. The
//
// Remarks
//   If successful the buffer is filled with multiple wide-character strings
//   with the end of the buffer identified by an empty string.
//
//   Each binding made in the driver is represented by two strings (a tuple)
//   in the buffer:
//
//     Virtual Adapter Name - The name passed to NdisIMInitializeDeviceInstanceEx
//                            for the binding.
//     Lower Adapter Name   - The name passed to NdisOpenAdapter for the binding.
//
BOOL
PtEnumerateBindings(
   HANDLE PtHandle,
   PCHAR Buf,
   DWORD *BufLength
   )
{
   BOOL                        bResult;
   //
   // Use DeviceIoControl to Call The Device
   //
   bResult = DeviceIoControl(
      PtHandle,
      IOCTL_PTUSERIO_ENUMERATE,
      NULL,
      0,
      Buf,
      *BufLength,
      BufLength,
      NULL
      );
   return( bResult );
}
/
_tmain
//
// Purpose
// PTUserIo MFC console application MAIN entry point.
//
// Parameters
//
// Return Value
//
// Remarks
//
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
   int nRetCode = 0;
 
   //
   // Open A Handle On The PassThru Device
   //
   HANDLE PtHandle = PtOpenControlChannel();
   if( PtHandle == INVALID_HANDLE_VALUE )
   {
      cout << "PassThru Handle Open Failed" << endl;
      nRetCode = 1;
      return nRetCode;
   }
   cout << "PassThru Handle Open Successful" << endl;
   //
   // Enumerate The PassThru Bindings
   //
   WCHAR BindingList[ 2048 ];
   DWORD BufLength = sizeof( BindingList );
   if( PtEnumerateBindings( PtHandle, (PCHAR )BindingList, &BufLength ) )
   {
      PWCHAR   pWStr = BindingList;
      UINT     nWCHARsRead;
      INT      nBytesUnread = BufLength;
      if( !BufLength )
      {
         cout << "Binding List Is Empty" << endl;
      }
      else
      {
         cout << endl << "Driver Bindings:" << endl;
         while( pWStr && *pWStr && nBytesUnread > 0 )
         {
            //
            // Display Virtual Adapter Name
            // ----------------------------
            // This is the name passed to NdisIMInitializeDeviceInstanceEx.
            // We can call this our "virtual adapter name".
            //
            _tprintf( "   /042%ws/042/n", pWStr );
            //
            // Advance In Buffer
            //
            nWCHARsRead = wcslen( pWStr ) 1;
            nBytesUnread -= nWCHARsRead * sizeof( WCHAR );
            if( nBytesUnread <= 0 )
            {
               pWStr = NULL;
            }
            else
            {
               pWStr = nWCHARsRead;
            }
         }
      }
   }
   else
   {
      cout << endl << "Binding Enumeration Failed" << endl;
   }
   CloseHandle( PtHandle );
   return nRetCode;
}
 
ADAPT 结构引用计数
        在继续下一步之前,我们先解决一个问题:在 PassThru 中维护的一个重要对象就是 ADAPT 结构,该结构在 PassThru.h 中定义。在 ADAPT 结构中,包含了所有已成功打开的绑定关系的相关信息。在原 PassThru 代码中, ADAPT 结构的生命周期是由 NDIS Wrapper 函数决定的。它由 Protocol.c 中的 PtBindAdapter 分配并初始化的,在 PtUnbindAdapter 或者在 MPHalt 处理函数释放。因为这三个函数的调用都是由 NDIS 负责的,所以创建和销毁工作都能安全的完成。
        当我们在一个特定的绑定的关系中创建句柄时,实际上我们创建的是一种句柄和 ADAPT 结构之间的映身关系。当我们在适配器句柄上进行操作时,就需要访问 ADAPT 结构中的相关信息。
        现在问题已经摆在我们面前了:当一个已经关联的 ADAPT 结构在用户句柄关闭前被子释放了,这时系统有可能崩溃。
        警告:我们必须提供一种机制保证这种情况不会发生。
        控制这种临时对象生命周期的通用方法就是“引用计数”。当在 PtBindAdapter 中创建 ADAPT 结构时, ADAPT 的引用计数被加 1 ,当计数变成 0 时, ADAPT 结构就可以安全释放了。
 
1. 驱动代码
        首先在 ADAPT 结构中添加一个成员变量: RefCount ,然后添加两个函数用来操作该变量。
                PtRefAdapter 安全的将 RefCount 1
PtDerefAdapter RefCount 1 并作以下判断: 如果 RefCount 0 则调用 NdisFreeMemory
下面是两个函数的实现:
VOID PtRefAdapter( PADAPT pAdapt )
{
   NdisInterlockedIncrement( &pAdapt->RefCount );
}
VOID PtDerefAdapter( PADAPT pAdapt )
{
   if( NdisInterlockedDecrement( &pAdapt->RefCount) == 0 )
   {
      NdisFreeMemory(pAdapt, 0, 0);
   }
}
然后,我们修改 PtBindAdapter ,使其设置 ADAPT 中的 RefCount 1 ,并且在 PtUnbindAdapter and MPHalt PtDerefAdapter 代替 NdisFreeMemory
从逻辑上看,我们似乎用了一种复杂的方法来调用 NdisFreeMemory ;但是,当添加了在特定的 PassThru 绑定关系上打开句柄的代码后,我们就可以用 PtRefAdapter PtDerefAdapter 来保证 ADAPT 结构不会先于用户句柄被释放。
值得注意的是, PtDerefAdapter 不仅可以释放被引用的内存(变量),在实际实现的时候,还可将 PtUnbindAdapter MPHalt 中的部分代码放在 PtDerefAdapter 中。
Modified Code: PassThru.h Module, ADAPT Structure
typedef struct _ADAPT
{
    ...
    NDIS_STRING         DeviceName;               // For initializing the miniport edge
// BEGIN_PTUSERIO
    ULONG               RefCount;                 // Used For ADAPT Reference Counting
// END_PTUSERIO
    NDIS_EVENT          MiniportInitEvent;        // For blocking UnbindAdapter while
                                                  // an IM Init is in progress.
    ...
} ADAPT, *PADAPT;
 
New Code: PTExtend.c Module, PtRefAdapter and PtDerefAdapter Functions
These are the two ADAPT structure reference counting functions. In addition to managing when the ADAPT structure is freed we have absorbed additional redundant code from MPHalt and PtUnbindAdapter into PtDerefAdapter.
 
VOID
PtRefAdapter( PADAPT pAdapt )
{
   NdisInterlockedIncrement( &pAdapt->RefCount );
}
VOID
PtDerefAdapter( PADAPT pAdapt )
{
   if( !pAdapt )
   {
      return;
   }
   if( NdisInterlockedDecrement( &pAdapt->RefCount) == 0 )
   {
      DBGPRINT(( "PtDerefAdapter: Adapter: 0x%8.8X/n", pAdapt ? (ULONG )pAdapt : 0 ));
      //
      // Free all resources on this adapter structure.
      //
      if (pAdapt->RecvPacketPoolHandle != NULL)
      {
         //
         // Free the packet pool that is used to indicate receives
         //
         NdisFreePacketPool(pAdapt->RecvPacketPoolHandle);
         pAdapt->RecvPacketPoolHandle = NULL;
      }
      if (pAdapt->SendPacketPoolHandle != NULL)
      {
         //
         // Free the packet pool that is used to send packets below
         //
         NdisFreePacketPool(pAdapt->SendPacketPoolHandle);
         pAdapt->SendPacketPoolHandle = NULL;
      }
      NdisFreeMemory(pAdapt, 0, 0);
   }
}
Modified Code: Protocol.c Module, PtBindAdapter Function
These modifications add the initial reference count to the ADAPT structure, In addition, the new PtDerefAddapter function is called instead of NdisFreeMemory in one place.
 
VOID
PtBindAdapter(
    OUT PNDIS_STATUS            Status,
    IN NDIS_HANDLE             BindContext,
    IN PNDIS_STRING            DeviceName,
    IN PVOID                   SystemSpecific1,
    IN PVOID                   SystemSpecific2
    )
{
    ...
    do
    {
        ...
        //
        // Initialize the adapter structure. We copy in the IM device
        // name as well, because we may need to use it in a call to
        // NdisIMCancelInitializeDeviceInstance. The string returned
        // by NdisReadConfiguration is active (i.e. available) only
        // for the duration of this call to our BindAdapter handler.
        //
        NdisZeroMemory(pAdapt, TotalSize);
        pAdapt->DeviceName.MaximumLength = Param->ParameterData.StringData.MaximumLength;
        pAdapt->DeviceName.Length = Param->ParameterData.StringData.Length;
        pAdapt->DeviceName.Buffer = (PWCHAR)((ULONG_PTR)pAdapt sizeof(ADAPT));
        NdisMoveMemory(pAdapt->DeviceName.Buffer,
                       Param->ParameterData.StringData.Buffer,
                       Param->ParameterData.StringData.Length);
 
// BEGIN_PTUSERIO
        //
        // Add Initial Reference To Adapter
        //
        PtRefAdapter( pAdapt );
// END_PTUSERIO
 
        NdisInitializeEvent(&pAdapt->Event);
        NdisAllocateSpinLock(&pAdapt->Lock);
        ...
    } while(FALSE);
    ...
    if (*Status != NDIS_STATUS_SUCCESS)
    {
       if (pAdapt != NULL)
       {
         if (pAdapt->BindingHandle != NULL)
         {
            NDIS_STATUS    LocalStatus;
            //
            // Close the binding to the adapter
            //
           NdisResetEvent(&pAdapt->Event);
            NdisCloseAdapter(&LocalStatus, pAdapt->BindingHandle);
            pAdapt->BindingHandle = NULL;
            if (LocalStatus == NDIS_STATUS_PENDING)
            {
               NdisWaitEvent(&pAdapt->Event, 0);
               LocalStatus = pAdapt->Status;
            }
            ASSERT (LocalStatus == NDIS_STATUS_SUCCESS);
         }
// BEGIN_PTUSERIO
          //
          // Remove Protocol's Reference To The Adapter
          //
          PtDerefAdapter( pAdapt );
// END_PTUSERIO
          pAdapt = NULL;
       }
    }
}
 
 
Modified Code: Protocol.c Module, PtUnbindAdapter Function
These modifications show the call to the new PtDerefAddapter function is called instead of NdisFreeMemory. In addition, some common code shared between PtUnbindAdapter and MPHalt has been absorbed into PtDerefAdapter.
 
VOID
PtUnbindAdapter(
    OUT PNDIS_STATUS        Status,
    IN NDIS_HANDLE            ProtocolBindingContext,
    IN NDIS_HANDLE            UnbindContext
    )
{
    ...
    if (pAdapt->MiniportHandle != NULL)
    {
        *Status = NdisIMDeInitializeDeviceInstance(pAdapt->MiniportHandle);
        if (*Status != NDIS_STATUS_SUCCESS)
        {
            *Status = NDIS_STATUS_FAILURE;
        }
    }
    else
    {
        //
        // We need to do some work here.
        // Close the binding below us
        // and release the memory allocated.
        //
        if(pAdapt->BindingHandle != NULL)
        {
            NdisResetEvent(&pAdapt->Event);
            NdisCloseAdapter(Status, pAdapt->BindingHandle);
            //
            // Wait for it to complete
            //
            if(*Status == NDIS_STATUS_PENDING)
            {
                 NdisWaitEvent(&pAdapt->Event, 0);
                 *Status = pAdapt->Status;
            }
            pAdapt->BindingHandle = NULL;
        }
        else
        {
            //
            // Both Our MiniportHandle and Binding Handle should not be NULL.
            //
            *Status = NDIS_STATUS_FAILURE;
            ASSERT(0);
        }
// BEGIN_PTUSERIO
        //
        //    Free the memory here, if was not released earlier(by calling the HaltHandler)
        //
        PtDerefAdapter( pAdapt );
// END_PTUSERIO
    }
    DBGPRINT(("<== PtUnbindAdapter: Adapt %p/n", pAdapt));
}
 
 
Modified Code: Miniport.c Module, MPHalt Function
These modifications show the call to the new PtDerefAddapter function is called instead of NdisFreeMemory. In addition, some common code shared between PtUnbindAdapter and MPHalt has been absorbed into PtDerefAdapter.
 
VOID
MPHalt(
    IN NDIS_HANDLE                MiniportAdapterContext
    )
{
    ...
    //
    // Delete the ioctl interface that was created when the miniport
    // was created.
    //
    (VOID)PtDeregisterDevice();
    if (pAdapt->BindingHandle != NULL)
    {
      NDIS_STATUS    LocalStatus;
      //
      // Close the binding to the adapter
      //
      NdisResetEvent(&pAdapt->Event);
      NdisCloseAdapter(&LocalStatus, pAdapt->BindingHandle);
      pAdapt->BindingHandle = NULL;
      if (LocalStatus == NDIS_STATUS_PENDING)
      {
         NdisWaitEvent(&pAdapt->Event, 0);
         LocalStatus = pAdapt->Status;
      }
      ASSERT (LocalStatus == NDIS_STATUS_SUCCESS);
    }
// BEGIN_PTUSERIO
   //
   // Remove Miniport's Reference To The Adapter
   //
   PtDerefAdapter( pAdapt );
// END_PTUSERIO
    DBGPRINT(("<== MiniportHalt: pAdapt %p/n", pAdapt));
}
五.适配器名柄 (Adapter Handle) 打开 / 关闭功能
在这部分,我们不仅会介绍打开一个适配器句柄,而且会讲述句柄的生命周期:包括如何处理 NDIS 在用户句柄关闭之前取消绑定关系情况的方法。
Win32 用户角度来看,打开适配器句柄的过程简单而且类似。这里将提供 PtOpenAdapter 函数,它以 UNICODE 格式的绑定关系名(从 PtEnumerateBindings 函数中返回)为参数,成功调用则返回一个有效句柄。跟适配器相关的操作就可在这个句柄上操作。当所有工作都结束后,调用 PtCloseAdapter 函数来关闭句柄。
打开句柄在驱动程序内部的工作步骤如下:
1)         搜索 pAdapterList 链表,在其中找 DeviceName 与所指定的名字相匹配的 ADAPT 结构。
2)         如找到,则分配一个 OPEN_CONTEXT 结构,用来管理句柄的相关信息。
3)         将适配器 (Adapter) 与句柄关联起来: A 将指向 OPEN_CONTEXT 的指针保存在 ADAPT 结构中。 B 将指向 ADAPT 结构的指针保存在 OPEN_CONTEXT 中,并将适配器的引用计数增一。 C 关联句柄与 OPEN_CONTEXT 结构,即将指向 OPEN_CONTEXT 结构的指针保存在 FileObject 中的 FsContext 字段中。
在接下来的一系列的 I/O 分发函数调用中, OPEN_CONTEXT 结构可以从 FileObject 结构的 Fscontext 字段中恢复。
以上的过程也决定了实现方式上的一个限制:驱动程序只能互斥访问每一个绑定关系,一个 PassThru 绑定关系上一次只能打开一个句柄。
关闭一个句柄的工作句括:在 DevCleanup 中添加取消在该句柄上的未完成 I/O 操作的代码和在 DevClose 中释放 OPEN_CONTEXT 结构。  
另外一个需要处理的与句柄有关的问题是:在打开了适配器句柄后, NDIS 可能随时取消与句柄相关的绑定关系。这种情况必须小心处理。  
基于此,我们必须在测试程序中做好相关的处理,从而不致于弄得一团糟。现在将焦点集中到 Load/Unload 测试上。如果你对 NDIS Tester 不熟悉的话,请参考: Stephan Wolf's article Testing Network Drivers with the NDIS Test Tool .
 
当然,要写出没有 BUG 的程序是不可能的(至少本文的作者有这个缺点)。但是,可以通过一些测试来消除一些错误。一种可以用来测试打开适配器代码的方法是:
1)         PassThru 小端口中连续运行 NDIS Tester Load/Unload 测试。
2)         当前重复打开一个 PassThru 适配器,等待 5 秒钟后又关闭。返复运行。
3)         PoolTag 工具检查是否有内存泄漏。
我们在此不详细描述测试过程,结果只有两种:( 1 )成功通过;( 2 )一开始根本就没法运行。
1. 驱动程序代码
用户态的 PtOpenAdapter 函数最终会调用驱动程序的 DevIoControl 函数对命令 IOCTL_PTUSERIO_OPEN_ADAPTER (在 IOCommon.h 中定义的)的分发函数, DevOpenAdapter 做要的工作。
DevOpenAdapter 做的第一件事就是调用一个新函数 PtLookuAdapterByName 去查找 ADAPT 结构,在这个结构中, DeviceName 与用户提供的绑定关系名相匹配。对这个函数有两个需要注意的地方:
名字比较对大小写敏感:函数 NdisEqualMemory 用来比较用户的绑定关系名与 ADAPT 结构中的 DeviceName 。用户可能想使用不区分大小的字符串比较函数,但是,由于这种比较是在拥有 Spinlock(IRQL==DISPATCH_LEVEL) 的条件下进行的,所以其它字符比较不能使用。
引用计数添加到 ADAPT 结构中:注意,引用计数应在 NdisReleaseSpinLock 调用前添加到 ADAPT 结构中。如果引用计数还没有增加,则完全可能导致 NDIS 无限绑定(或者是 ADAPT 结构指针在返回到调用者之前被错误释放掉)。
如果 PtLookupAdapterByName 成功找到一个与之匹配的 ADAPT 绑定,此时 NDIS 会分配一个 OPEN_CONTEXT 结构来管理特定的 OPEN CONTEXT 信息(例如:打开句柄)。 OPEN_CONTEXT 的分配与初始化由 DevAllocateOpenContext 函数来完成,计数工作则通过 DevRefOpenContext DevDerefOpenContext
这里我们需要对已经找到的用户句柄和特定的 PassThru 绑定关系建立关联。以下是要关联两个实体:
1)         用户态句柄:由 I/O 栈中 FILE_OBJECT
2)         特定的绑定关系:由 OPEN_CONTEXT 结构表示( pAdapt 字段指向 ADAPT 结构)。
标准的 DDK 并没有讲述太多的关于 I/O 栈中的 FILE_OBJECT 的用途。但是,在很多情况下,它的用途还是很大的。如果你曾经写过文件系统驱动,你就会发现 FsContext FsContext2 字段是多么重要。
简单地将 FILE_OBJECT 放置在 I/O 栈中代表 PassThru 的一个打开实例。在 FILE_OBJECT 与用户态句柄之间用一种一对一的联系(至少在简单情况下是这样)。最重要的是驱动程序可以自由的指定你所想要的值给 FsContext FsContext2 字段,这些值将会在 FILE_OBJECT 中被返回给驱动程序以备后来在同一句柄上对 I/O 分发函数的调用。
因此,为了建立句柄与绑定的关系,我们将 FsContext 字段设为指向 OPEN_CONTEXT 结构的指针。在接下来对 I/O 分发函数的调用中我们检查 FsContext 。如果它非空,那么就是我们正在使用的绑定关系的指针。
 
关闭适配器句柄
当一个用户态句柄关闭时,需要在 DevCleanup DevClose 过程中做一些工作。当然, DevClose 会调用 DevDerefOpenContext
               
处理在一个打开的适配器上意外取消绑定的情况:
最后,我们必须添加这样一种功能:处理在一个用户态句柄已经打开的情况下 NDIS 取消适配器绑定的情况。为了应付这种情况,我们添加了函数 DevOnUnbindAdapter ,用来通知已经打开句柄的用户。此函数必须等待所有的在该适配器上重要的 NDIS 操作完成,然后取消该句柄上的所有 pending I/O 。这里有个假设:当函数 DevOnUnbindAdapter 一返回,适配就会调用 NdisCloseAdapter 来关闭适配器。
我们在 MPHalt PtUnbindAdapter 函数调用 DevOnUnbindAdapter
< 点击链接查看相关代码 >
There are two important things to notice about this function:
 
Case Sensitive Name Comparison
The NdisEqualMemory function is used to compare the user's binding name with the ADAPT DeviceName field. It would be desirable to use case-insensitive string comparison functions. However, since the comparison is being performed with a spin lock held (IRQL == DISPATCH_LEVEL) string comparison functions are not allowed.
 
Reference Count Added To ADAPT Structure
Notice that a reference count is added to the ADAPT structure before NdisReleaseSpinLock is called. If the ref count was not added, it is entirely possible that NDIS could cause the binding to be unbound (and the ADAPT structure to be freed) before the ADAPT pointer could be returned to the caller.
PADAPT
PtLookupAdapterByName(
   IN PUCHAR   pNameBuffer,
   IN USHORT   NameBufferLength,
   IN BOOLEAN bUseVirtualName
   )
{
   PADAPT *ppCursor, pAdapt = NULL;
   //
   // Sanity Checks
   //
   if( !pNameBuffer || !NameBufferLength )
   {
      return( NULL );
   }
   //
   // Walk The Adapter List
   // ---------------------
   // Hold the global lock while walking. Otherwise, the adapter list could be altered at any point in
   // the list processing sequence.
   //
   NdisAcquireSpinLock( &GlobalLock );
   for( ppCursor = &pAdaptList; *ppCursor != NULL;
      ppCursor = &(*ppCursor)->Next
      )
   {
      __try
      {
         if( bUseVirtualName )
         {
            //
            // Check For Match Against Virtual Adapter Name
            //
            if( ( (*ppCursor)->DeviceName.Length == NameBufferLength) &&
                  NdisEqualMemory( (*ppCursor)->DeviceName.Buffer, pNameBuffer, NameBufferLength ))
            {
               //
               // Return Pointer To Found Adapter
               //
               pAdapt = (*ppCursor);
               break;
            }
         }
         else
         {
            //
            // Check For Match Against Lower Adapter Name
            //
            if( ( (*ppCursor)->LowerDeviceName.Length == NameBufferLength) &&
                  NdisEqualMemory( (*ppCursor)->LowerDeviceName.Buffer, pNameBuffer, NameBufferLength))
            {
               //
               // Return Pointer To Found Adapter
               //
               pAdapt = (*ppCursor);
               break;
            }
         }
      }
      __except( EXCEPTION_EXECUTE_HANDLER )
      {
         pAdapt = NULL;
         break;
      }
   }
   //
   // Add Reference To Adapter Memory
   // -------------------------------
   // As soon as the spinlock is released (below) and before returning to the caller it is possible
   // for NDIS to unbind the selected adapter from the PassThru protocol. The reference counting scheme
   // insures that the memory pointed to by pAdapt will remain valid until the last call to
   // PtDerefAdapter.
   //
   if( pAdapt )
   {
      PtRefAdapter( pAdapt );
   }
   NdisReleaseSpinLock( &GlobalLock );
   return( pAdapt );
}
 
 
New Code: PTExtend.h Module, OPEN_CONTEXT Structure
Used by the driver to manage information about a specific open context (i.e., open handle).
 
typedef
struct _OPEN_CONTEXT
{
   ULONG          RefCount;
   NDIS_SPIN_LOCK Lock;
   BOOLEAN        bAdapterClosed;
   PADAPT         pAdapt;
}
   OPEN_CONTEXT, *POPEN_CONTEXT;
 
Modified Code: PassThru.h Module, ADAPT Structure
Modify the ADAPT structure to add a member variable that points to the OPEN_CONTEXT structure associated with the adapter..
 
typedef struct _ADAPT
{
    ...
    NDIS_STRING         DeviceName;               // For initializing the miniport edge
// BEGIN_PTUSERIO
    ULONG               RefCount;                 // Used For ADAPT Reference Counting
    POPEN_CONTEXT       pOpenContext;
// END_PTUSERIO
    NDIS_EVENT          MiniportInitEvent;        // For blocking UnbindAdapter while
                                                  // an IM Init is in progress.
    ...
} ADAPT, *PADAPT;
 
New Code: PTExtend.c Module, DevRefOpenContext and DevDerefOpenContext Functions
These are the two OPEN_CONTEXT structure reference counting functions. It manages when the OPEN_CONTEXT structure is freed..
 
 
VOID
DevRefOpenContext( POPEN_CONTEXT pOpenContext )
{
   PtRefAdapter( pOpenContext->pAdapt );
 
   NdisInterlockedIncrement( &pOpenContext->RefCount );
}
VOID
DevDerefOpenContext( POPEN_CONTEXT pOpenContext )
{
   PADAPT pAdapt = NULL;
  if( !pOpenContext )
   {
      return;
   }
   pAdapt = pOpenContext->pAdapt;
   if( NdisInterlockedDecrement( &pOpenContext->RefCount) == 0 )
   {
      NdisFreeSpinLock( &pOpenContext->Lock );
      NdisFreeMemory(pOpenContext, 0, 0);
   }
   PtDerefAdapter( pAdapt );
}
New Code: PTExtend.c Module, DevAllocateOpenContext Function
This function allocates and initializes an OPEN_CONTEXT structure.
 
POPEN_CONTEXT
DevAllocateOpenContext( PADAPT pAdapt )
{
   POPEN_CONTEXT pOpenContext = NULL;
   NdisAllocateMemoryWithTag( &pOpenContext, sizeof( OPEN_CONTEXT ), TAG );
   if( !pOpenContext )
   {
      return( NULL );
   }
   //
   // Initialize The Open Context Structure
   //
   NdisZeroMemory( pOpenContext, sizeof( OPEN_CONTEXT ) );
   NdisAllocateSpinLock( &pOpenContext->Lock );
   NdisInitializeEvent( &pOpenContext->LocalRequest.RequestEvent );
   //
   // Add Initial Reference To Open Context
   // -------------------------------------
   // Note that we already have added an implicit reference to the adapter
   // because of the PtLookupAdapterByName call.
   //
   pOpenContext->RefCount = 1;
   pOpenContext->pAdapt = pAdapt;
   return( pOpenContext );
}
New Code: PTExtend.c Module, DevOpenAdapter Function
The DevIoControl dispatcher calls the DevOpenAdapter function to do the work related to opening an adapter handle.
 
NTSTATUS
DevOpenAdapter(
   IN PDEVICE_OBJECT    pDeviceObject,
   IN PIRP              pIrp,
   IN BOOLEAN           bUseVirtualName
   )
{
   PIO_STACK_LOCATION pIrpSp;
   NTSTATUS            NtStatus = STATUS_SUCCESS;
   ULONG               BytesReturned = 0;
   PUCHAR              pNameBuffer = NULL;
   ULONG               NameBufferLength;
   PADAPT              pAdapt;
   POPEN_CONTEXT       pOpenContext;
   pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
   pNameBuffer = pIrp->AssociatedIrp.SystemBuffer;
   NameBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
   //
   // Lookup Adapter By Name
   // ----------------------
   // If successful the lookup function has added a ref count to the found ADAPT
   // structure.
   //
   pAdapt = PtLookupAdapterByName( pNameBuffer, (USHORT )NameBufferLength, bUseVirtualName );
   if( !pAdapt )
   {
      NtStatus = STATUS_OBJECT_NAME_NOT_FOUND;
      goto CompleteTheIRP;
   }
   //
   // Fail Open If Unbind Is In Progress
   //
   NdisAcquireSpinLock(&pAdapt->Lock);
   if( pAdapt->UnbindingInProcess )
   {
      NdisReleaseSpinLock(&pAdapt->Lock);
      PtDerefAdapter( pAdapt );
      NtStatus = STATUS_INVALID_DEVICE_STATE;
      goto CompleteTheIRP;
   }
   NdisReleaseSpinLock(&pAdapt->Lock);
   if( pAdapt->pOpenContext )
   {
      PtDerefAdapter( pAdapt );
      NtStatus = STATUS_DEVICE_BUSY;
      goto CompleteTheIRP;
   }
   pOpenContext = DevAllocateOpenContext( pAdapt );
   if( !pOpenContext )
   {
      PtDerefAdapter( pAdapt );
      NtStatus = STATUS_INSUFFICIENT_RESOURCES;
      goto CompleteTheIRP;
   }
   //
   // Sanity Check For Concurrent Open Race Condition
   // -----------------------------------------------
  // At this point we enforce exclusive access on a per-binding basis.
   //
   // This logic deals with the situation where two concurrent adapter
   // opens could be in progress. We want an atomic mechanism that insures
   // that only one of the opens will be successful.
   //
   // This InterlockedXXX function performs an atomic operation: First it
   // compares pAdapt->pOpenContext with NULL, if they are equal, the function
   // puts pOpenContext into pAdapt->pOpenContext, and return NULL. Otherwise,
   // it return existing pAdapt->pOpenContext without changing anything.
   //
   // NOTE: This implementation is borrowed from the NDISPROT sample from
   // the Windows DDK.
   //
   if ( InterlockedCompareExchangePointer (& (pAdapt->pOpenContext), pOpenContext, NULL) != NULL)
   {
      PtDerefAdapter( pAdapt );
      NtStatus = STATUS_DEVICE_BUSY;
      goto CompleteTheIRP;
   }
   //
   // Associate This Handle With The Open Context
   //
   pIrpSp->FileObject->FsContext = pOpenContext;
 
   //
   // Complete The IRP
   //
CompleteTheIRP:
   pIrp->IoStatus.Information = BytesReturned;
   pIrp->IoStatus.Status = NtStatus;
   IoCompleteRequest(pIrp, IO_NO_INCREMENT);
   return NtStatus;
}
Modified Code: PTExtend.c Module, DevIoControl Function
In DevIoControl we add a call to dispatch IOCTL_PTUSERIO_OPEN_ADAPTER (defined in IOCommon.h) to the DevOpenAdapter handler
 
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;
    pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
    ioBuffer = pIrp->AssociatedIrp.SystemBuffer;
    inputBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
    outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;  
    FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode; 
    switch (FunctionCode)
    {
        case IOCTL_PTUSERIO_ENUMERATE:
         return( DevEnumerateBindings(
                  pDeviceObject,
                  pIrp
                  )
               );
        case IOCTL_PTUSERIO_OPEN_ADAPTER:
         return( DevOpenAdapter(
                  pDeviceObject,
                  pIrp,
                  FALSE        // Is Lower 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);
    } 
    return NtStatus;
}
Modified Code: PTExtend.c Module, DevClose Function
Here we null some pointers and call DefDerefOpenContext.
NTSTATUS
DevClose(
    IN PDEVICE_OBJECT    pDeviceObject,
    IN PIRP              pIrp
    )
{
    PIO_STACK_LOCATION pIrpSp;
    NTSTATUS            NtStatus = STATUS_SUCCESS;
    POPEN_CONTEXT       pOpenContext;
 
    pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
    pOpenContext = pIrpSp->FileObject->FsContext;
 
    //
    // Undo IRP_MJ_CREATE Operations
    //
    pIrpSp->FileObject->FsContext = NULL;
    pIrpSp->FileObject->FsContext2 = NULL;
               
    if( pOpenContext )
    {
      if( pOpenContext->pAdapt )
      {
         (pOpenContext->pAdapt)->pOpenContext = NULL;
      }
      DevDerefOpenContext( pOpenC ontext );
    }
 
    pIrp->IoStatus.Information = 0;
    pIrp->IoStatus.Status = NtStatus;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
 
    return NtStatus;
}
 
 
Modified Code: PTExtend.c Module, DevOnUnbindAdapter Function
This function is called from MPHalt or PtUnbindAdapter to notify the open handle logic of a "surprise unbind" event.
VOID
DevOnUnbindAdapter( POPEN_CONTEXT pOpenContext )
{
   PADAPT pAdapt = NULL;
   if( !pOpenContext )
   {
      return;
   }
   DBGPRINT(("==>Pt DevOnUnbindAdapter: Context %p/n", pOpenContext ));
 
   //
   // Set Flag That Will Cause Subsequent I/O To Be failed
   //
   pOpenContext->bAdapterClosed = TRUE;
   //
   // Wait For Outstanding NDIS Operations On The Handle To Complete
   //
 
   //
   // Cancel Pending I/O On The Handle
   //
 
   DBGPRINT(("<== Pt DevOnUnbindAdapter/n"));
}
 
Modified Code: Miniport.c Module, MPHalt Function
This function is modified to call DevOnUnbindAdapter to notify the open handle logic of a "surprise unbinding" event.
VOID
MPHalt(
    IN NDIS_HANDLE                MiniportAdapterContext
    )
{
    ...
    NdisReleaseSpinLock(&GlobalLock);
// BEGIN_PTUSERIO
    //
    // Notify Open Handle Logic About Surprise Inbind
    //
    DevOnUnbindAdapter( pAdapt->pOpenContext );
// END_PTUSERIO
    //
    // Delete the ioctl interface that was created when the miniport
    // was created.
    //
    (VOID)PtDeregisterDevice();
    ...
}
 
 
Modified Code: Protocol.c Module, PtUnbindAdapter Function
This function is modified to call DevOnUnbindAdapter to notify the open handle logic of a "surprise unbinding" event.
VOID
PtUnbindAdapter(
    OUT PNDIS_STATUS        Status,
    IN NDIS_HANDLE            ProtocolBindingContext,
    IN NDIS_HANDLE            UnbindContext
    )
{
    ...
    else
    {
        NdisReleaseSpinLock(&pAdapt->Lock);
    }
// BEGIN_PTUSERIO
    //
    // Notify Open Handle Logic About Surprise Inbind
    //
    DevOnUnbindAdapter( pAdapt->pOpenContext );
// END_PTUSERIO
#ifndef WIN9X
    //
    // Check if we had called NdisIMInitializeDeviceInstanceEx and
    // we are awaiting a call to MiniportInitialize.
    //
    ...
}
 
应用程序代码:
  打开 / 关闭一个适配器句柄的代码实现很简单。我们在 PtOpenControlChannel 返回的句柄基础上调用 DeviceIoControl 函数,通过命令 IOCTL_PTUSERIO_OPEN_ADAPTER 将存有绑定关系名的用户态缓冲区指针传给 PassThru 驱动程序,紧接着,驱动程序的 DevIoControl 分发函数会调用 DevOpenAdapter 函数来在内核中完成相关的工作。
新加代码: PTUserIo.cpp 中的 PtOpenAdapter PtCloseAdapter 函数。
以下就是用来在 PassThru 中打开基于绑定关系句柄的用户态函数:
HANDLE PtOpenAdapter( PWSTR pszAdapterName )
{
   HANDLE            hAdapter;
   BOOLEAN           bRc;
   ULONG             bytesReturned, nBufferLength;
   hAdapter = INVALID_HANDLE_VALUE;
   //
   // Open A Control Channel Handle On The PassThru Device
   //
   hAdapter = PtOpenControlChannel();
   if( hAdapter == INVALID_HANDLE_VALUE )
   {
      return( INVALID_HANDLE_VALUE );
   }
   //
   // Determine Length (Bytes) Of The Adapter Name
   //
   nBufferLength = wcslen( (PWSTR )pszAdapterName ) * sizeof( WCHAR );
   //
   // Call Driver To Make Open The Adapter Context
   //
   bRc = DeviceIoControl(
            hAdapter,
            (DWORD)IOCTL_PTUSERIO_OPEN_ADAPTER,
            pszAdapterName,
            nBufferLength,
            NULL,
            0,
            &bytesReturned,
            NULL
            );
   //
   // Check Results
   //
   if ( !bRc )
   {
      CloseHandle( hAdapter );
      return( INVALID_HANDLE_VALUE );
   }
   return( hAdapter );     // Success
}
BOOL PtCloseAdapter( HANDLE hAdapter )
{
   //
   // Close The Handle
   // ----------------
   // Future versions may pereform additional work in this routine...
   //
   return( CloseHandle( hAdapter ) );
}
处理事件通知 (Event Notifications)
 
你可能以为有关在驱动程序上打开 / 关闭 Win32 句柄的话题已经讲完了,其实还没有完。接下来我们讲下一个重要的话题:
注意: Win32 PnP( 即插即用 ) 机制可以随时停止一个 miniport 驱动或者取消协议驱动的绑定。事实上, PnP 可以在任何时候开始卸载 PassThru 驱动。
那就是说 PnP 可以引起任何一个已经打开的 Win32 句柄无效。
引用计数机制只能在有限的范围内解决这个问题。它至少可让 NDIS 在一个已经适配器句柄已经打开时安全地取消它。虽然有部分内存没有象我们预期的那样快被释放掉,但是系统也不会因此而崩溃。但是,这里还没有一种机制来通知上层应用程序该适配器句柄已经无效了。
需要注意的一点是:如果在 PassThru 设备上还有未关闭的句柄,那么 NDIS 就不会卸载这个驱动,即便是它已经取消了所有的该驱动程序上的绑定关系。
这问题有点需要提及:( 1 )在 PassThru 驱动上有 win32 句柄打开的前提下,部分 PnP 事件无法完成。( 2 )为了相对温和地处理这些事件,应该通知 Win32 应用程序。
在新闻组上经常问到的一个问题是:无法卸载驱动程序。可能的原因之一就是有一个未关闭的句柄存在。只要该句柄未关闭就无法卸载。
一个小技巧来处理这种问题就是:仅仅在需要打开它,即让打开的句柄在很快就关闭。这种重复打开方式就变成了处理问题的通知机制(注意:当未关闭该句柄时不能再次打开)。我们就是在 PTUserIo 程序中使用的这种方法。
另一情况是:有些程序需要长时间保持该打开的句柄(如数据包监控程序),这些程序就不能用上述方法,只能用其它的通知机制。
在这篇文章中,我们不打开实现这种通知机制,而是留到后续章节。当我们向驱动程序中添加需长时间保持句柄的程序时,才实现它。
 
在一个适配器句柄上查询信息
既然我们已经实现了一种在 PassThru 驱动中打开基于绑定的句柄的机制,接下来添加在其上通过 NDIS 请求查询信息的功能。
理论上, Win32 API 查询信息也是简单在 PtOpenAdapter 返回的句柄基础上调用 DeviceIoControl 。输入缓冲区主要用来向传递相关的 NDIS 对象标识( OID )给驱动程序。驱动程序的协议驱动部分则调用 NdisRequest 来查询对应 OID 并将结果写入用户提供 的输出缓冲区中。
但是,在原来的 PassThru 驱动程序中, NdisRequest 已经作为 MINIPORT 驱动的一部分了,因此,在 Win32 相关的 NdisRequest 中,我们就不能改变原业存在的功能关系。
另外,我们须期望,一个功能完备的 NDIS IM 驱动实际有三个独的 NdisRequest initiators
1.    Miniport MPQueryInformation /MPSetInformation Passthrough
2.    Win32-Initiated Requests :这是本节讨论的问题
3.    Autonomous Driver-Initiated Requests :这是以后讨论的问题。
要想实现简单,我们须注意以下约定:
PassThru 驱动适配器句柄上进行的查询是同步的,并且必须被 Win32 应用程序序列化。
如果我们做到了,则可以在 PTUserIo 程序中使用新的 PtQueryInformation API 函数来获取具有可读性的信息。以下测试在 Windows XP sp1 3 NDIS 小端口驱动程序上运行。
PassThru User I/O Test Application
Copyright (c) 2003 Printing Communications Assoc., Inc. (PCAUSA)
All rights reserved.
Driver Bindings:
   "/Device/{67A4853E-1940-43A3-A442-74701B5133B0}"
      Description: " Intel 8255x-based Integrated Fast Ethernet"
      Medium: 802.3
      Mac address = 00-00-39-14-92-A9
      Media Connect Status: Disconnected
   "/Device/{0611AD65-41D8-4BB1-8A8F-43008BB362A3}"
      Description: " NdisWan Adapter"
      Medium: 802.3
      Mac address = C0-F2-20-52-41-53
      Media Connect Status: Connected
   "/Device/{8DA82E8E-D091-4FB2-902A-673FBEC2DA7C}"
      Description: "3CRWE737A AirConnect Wireless LAN PC Card"
      Medium: 802.3
      Mac address = 00-50-DA-03-4E-6C
      Media Connect Status: Connected
1 驱动程序代码:
        在添加 Win32-initiated NDIS 查询功能之前,先看看原来的驱动程序中是如何处理对 PQueryInformation 的调用的。
        原驱动程序是基于这种事实的: NDIS 会序列化对这个函数的调用。这就意味着在任一个绑定关系上同时只有对 MPQueryInformation 调用。基于此, PassThru 驱动在 ADAPT 结构中对每一个绑定关系提供了一个 NDIS_REQUEST 结构,对 MPQueryInformation 的调用则通过 NDIS_REQUEST 结构来向下层的 miniport 传递查询。 NdisRequest 是在等待 NDIS_EVENT (同步信号量)上调用的,直到 PtRequestComplete 处理函数被调用。
        注意:要添加我们的 Win32-initiated NDIS 信息查询功能,只要对 PtRequestComplete 做适当的修改即可。
        幸运的是,要区分 miniport-initiated Requests Win32-initiated Requests ,所要做的工作就是简单的判断 NDIS_REQUEST 指针。如下所示:
VOID PtRequestComplete(
    IN NDIS_HANDLE            ProtocolBindingContext,
    IN PNDIS_REQUEST          NdisRequest,
    IN NDIS_STATUS            Status
    )
{
   PADAPT        pAdapt = (PADAPT)ProtocolBindingContext;
 
   if( NdisRequest != &(pAdapt->Request) )
   {
      //
      // Not A Miniport Request
      // ----------------------
      // Handle completion of this request differently....
      //
      return;
   }
   ...
}
我们对 NdisRequest Win32-initiated 调用机制使得完成请求增加了一个 Second level of indirection ,通过向 NDIS_REQUEST 结构添加外包结构来实现,如下所示。
typedef
struct _NDIS_REQUEST_EX
{
   NDIS_REQUEST                     Request;
   LOCAL_REQUEST_COMPLETE_HANDLER   RequestCompleteHandler;
   PVOID                            RequestContext;
   NDIS_STATUS                      RequestStatus;
   NDIS_EVENT                       RequestEvent;
}
   NDIS_REQUEST_EX, *PNDIS_REQUEST_EX;
NDIS_REQUEST_EX 结构包含了 request-specific request completion routine 相关的字段和 request-specific completion context 相关的字段。它这样使用:
            VOID
PtRequestComplete(
    IN NDIS_HANDLE            ProtocolBindingContext,
    IN PNDIS_REQUEST          NdisRequest,
    IN  NDIS_STATUS            Status
    )
{
    PADAPT        pAdapt = (PADAPT)ProtocolBindingContext;
    NDIS_OID      Oid = pAdapt->Request.DATA.SET_INFORMATION.Oid;
// BEGIN_PTUSERIO
   //
   // Handle Local NDIS Requests
   // --------------------------
  // Here we handle NDIS requests that do not originate from the miniport.
   //
   // Typically, these are requests that were initiated from user-mode but
   // could also be requests initiated autonomously by the NDIS IM driver.
   //
   if( NdisRequest != &(pAdapt->Request) )
   {
      PNDIS_REQUEST_EX pLocalRequest = (PNDIS_REQUEST_EX )NdisRequest;
      (*pLocalRequest->RequestCompleteHandler )( pAdapt, pLocalRequest, Status );
      return;
   }
// END_PTUSERIO
 
    //
    // Since our request is not outstanding anymore
    //
    ASSERT(pAdapt->OutstandingRequests == TRUE);
    ...   
}
到此为止,我们实现了如何处理 Request completion ,接下来将实现 Win32-initiated Requests 的驱动程序代码。
        1 )向 OPEN_CONTEXT 结构中添加一个 NDIS_REQUEST_EX 成员。这个成员用来在打开的适配器句柄上调用 NdisRequests
        2 )修改 DevAllocateOpenContext ,初始化 NDIS_REQUEST_EX NDIS_EVENT
        3 )添加一个新的 DevRequestComplete 处理函数,它是一个 Win32-initiated Requests Request-specific 处理函数
        4 )修改 DevIoControl 用来将 IOCTL_PTUSERIO_QUERY_INFORMATION 命令转发给 DevQueryInformation .
        5 )实现 DevQueryInformation . 用来调用 NdisRequest 和等待 NDIS_EVENT 上的完成事件。
“扩展微软 DDK 中的 NDIS IM 驱动的功能:添加一个 DeviceIoControl 接口(八) .
2 应用程序代码
        新代码: PTUserIo.cpp 中的 PtQueryInformation 函数
        以下是打开 PassThru 驱动上的基于绑定关系句柄的用户态函数:
                DWORD
PtQueryInformation(
   HANDLE   hAdapter,
   ULONG    OidCode,
   PVOID    InformationBuffer,
   UINT     InformationBufferLength,
   PULONG   pBytesWritten
   )
{
    DWORD       nResult = ERROR_SUCCESS;
    *pBytesWritten = 0;
    //
    // Make The DeviceIoControl Call
    //
    if( !DeviceIoControl(
        hAdapter,
        IOCTL_PTUSERIO_QUERY_INFORMATION,
        &OidCode,
        sizeof(OidCode),
        InformationBuffer,
       InformationBufferLength,
        pBytesWritten,
        NULL
        )
        )
    {
        //
        // DeviceIoControl returned an error
        //
        nResult = GetLastError();
    }
    return( nResult );
}
 
原作者的脚注:
                < >
版权信息:
                1 代码版权:
                2 文章版权:
                Copyright (c) 2003 Printing Communications Associates, Inc. (PCAUSA). All rights reserved.
PCAUSA does not grant the right to redistribute or publish this article without written permission.
                3. 下载原文的代码:
http://www.wd-3.com/downloads/ExtendingPassthru.zip
                4. 关于作者
                About the author:
Thomas F. Divine is founder of PCAUSA, a company which has been serving the Windows device driver community since 1992. PCAUSA licenses network device driver samples that illustrate specialized kernel mode programming technologies such an NDIS Intermediate drivers, TDI Clients and a variety of network data filtering techniques.
 
//
   // Handle Local NDIS Requests
   // --------------------------
   // Here we handle NDIS requests that do not originate from the miniport.
   //
   // Typically, these are requests that were initiated from user-mode but
   // could also be requests initiated autonomously by the NDIS IM driver.
   //
   if( NdisRequest != &(pAdapt->Request) )
   {
      PNDIS_REQUEST_EX pLocalRequest = (PNDIS_REQUEST_EX )NdisRequest;
      (*pLocalRequest->RequestCompleteHandler )( pAdapt, pLocalRequest, Status );
      return;
   }
// END_PTUSERIO
 
    //
    // Since our request is not outstanding anymore
    //
    ASSERT(pAdapt->OutstandingRequests == TRUE);
    ...   
}
 
 
New Code: PTExtend.c Module, DevQueryInformation Function
The DevIoControl dispatcher calls the DevQueryinformation function to do the work related to making a NdisRequest to query information on a PassThru adapter handle.
 
NTSTATUS
DevQueryInformation(
   IN PDEVICE_OBJECT    pDeviceObject,
   IN PIRP              pIrp,
   IN BOOLEAN           bUseVirtualName
   )
{
   PIO_STACK_LOCATION pIrpSp;
   NTSTATUS            NtStatus = STATUS_SUCCESS;
   ULONG               BytesReturned = 0;
   PUCHAR              ioBuffer = NULL;
   ULONG               inputBufferLength;
   ULONG               outputBufferLength;
   NDIS_OID            Oid;
   PADAPT              pAdapt;
   POPEN_CONTEXT       pOpenContext;
   PNDIS_REQUEST_EX    pLocalRequest;
   NDIS_STATUS         NdisStatus;
   pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
   ioBuffer = pIrp->AssociatedIrp.SystemBuffer;
   inputBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
   outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
   pOpenContext = pIrpSp->FileObject->FsContext;
   if( !pOpenContext )
   {
      NtStatus = STATUS_INVALID_HANDLE;
      goto CompleteTheIRP;
   }
 
   pAdapt = pOpenContext->pAdapt;
   if( !pAdapt )
   {
      NtStatus = STATUS_INVALID_HANDLE;
      goto CompleteTheIRP;
   }
   //
   // Sanity Check On Input Buffer/OID
   //
   if( inputBufferLength != sizeof( NDIS_OID ) )
   {
      NtStatus = STATUS_INVALID_PARAMETER;
      goto CompleteTheIRP;
   }
   Oid = *(PNDIS_OID )ioBuffer;
   //
   // Fail Open If Unbind Is In Progress
   //
   NdisAcquireSpinLock(&pAdapt->Lock);
   if( pAdapt->UnbindingInProcess )
   {
      NdisReleaseSpinLock(&pAdapt->Lock);
      NtStatus = STATUS_INVALID_DEVICE_STATE;
      goto CompleteTheIRP;
   }
   //
   // All other queries are failed, if the miniport is not at D0,
   //
   if (pAdapt->MPDeviceState > NdisDeviceStateD0)
   {
      NdisReleaseSpinLock(&pAdapt->Lock);
      NtStatus = STATUS_INVALID_DEVICE_STATE;
      goto CompleteTheIRP;
   }
   //
   // This is in the process of powering down the system, always fail the request
   //
   if (pAdapt->StandingBy == TRUE)
   {
      NdisReleaseSpinLock(&pAdapt->Lock);
      NtStatus = STATUS_INVALID_DEVICE_STATE;
      goto CompleteTheIRP;
   }
   NdisReleaseSpinLock(&pAdapt->Lock);
   //
   // Now (Finally) Make The NDIS Request...
   //
   //
   // May need to add ref counts to adapt and open context. Also, bump
   // a counter of outstanding requests...
   //
   DevRefOpenContext( pOpenContext );
   pLocalRequest = &pOpenContext->LocalRequest;
   pLocalRequest->Request.RequestType = NdisRequestQueryInformation;
   pLocalRequest->Request.DATA.QUERY_INFORMATION.Oid = Oid;
   pLocalRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer = ioBuffer;
   pLocalRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength = outputBufferLength;
   pLocalRequest->Request.DATA.QUERY_INFORMATION.BytesNeeded = 0;
   pLocalRequest->Request.DATA.QUERY_INFORMATION.BytesWritten = 0;
   pLocalRequest->RequestCompleteHandler = DevRequestComplete;
   pLocalRequest->RequestContext = pOpenContext;
   NdisResetEvent( &pLocalRequest->RequestEvent );
   NdisRequest(
      &NdisStatus,
      pAdapt->BindingHandle,
      (PNDIS_REQUEST )pLocalRequest
      );
   if( NdisStatus != NDIS_STATUS_PENDING )
   {
      DevRequestComplete( pAdapt, pLocalRequest, NdisStatus );
   }
   NdisWaitEvent( &pLocalRequest->RequestEvent, 0 );
   NdisStatus = pLocalRequest->RequestStatus;
   if( NdisStatus == NDIS_STATUS_SUCCESS )
   {
      BytesReturned = pLocalRequest->Request.DATA.QUERY_INFORMATION.BytesWritten;
      if( BytesReturned > outputBufferLength )
      {
         BytesReturned = outputBufferLength;
      }
      NtStatus = STATUS_SUCCESS;
   }
   else
   {
      NDIS_STATUS_TO_NT_STATUS( NdisStatus, &NtStatus);
   }
   DevDerefOpenContext( pOpenContext );
   //
   // Complete The IRP
   //
CompleteTheIRP:
   pIrp->IoStatus.Information = BytesReturned;
   pIrp->IoStatus.Status = NtStatus;
   IoCompleteRequest(pIrp, IO_NO_INCREMENT);
   return NtStatus;
}
 
 
Modified Code: PTExtend.c Module, DevIoControl Function
In DevIoControl we add a call to dispatch IOCTL_PTUSERIO_QUERY_INFORMATION (defined in IOCommon.h) to the DevQueryInformation handler
 
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;
 
    pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
 
    ioBuffer = pIrp->AssociatedIrp.SystemBuffer;
    inputBufferLength = pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
    outputBufferLength = pIrpSp->Parameters.DeviceIoControl.OutputBufferLength;
 
    FunctionCode = pIrpSp->Parameters.DeviceIoControl.IoControlCode;
 
    switch (FunctionCode)
    {
        case IOCTL_PTUSERIO_ENUMERATE:
         return( DevEnumerateBindings(
                  pDeviceObject,
                  pIrp
                  )
               );
        case IOCTL_PTUSERIO_OPEN_ADAPTER:
         return( DevOpenAdapter(
                  pDeviceObject,
                  pIrp,
                  FALSE        // Is Lower Adapter
                  )
               );
        case IOCTL_PTUSERIO_QUERY_INFORMATION:
         return( DevQueryInformation(
                  pDeviceObject,
                  pIrp,
                 FALSE        // Is Lower Adapter
                  )
               );
        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);
    }
 
    return NtStatus;
}
 
 
New Code: PTExtend.c Module, DevRequestComplete Function
This is the second-level NdisRequest completion handler called from PtRequestComplete for completion of non-local requests.
 
VOID
DevRequestComplete(
   IN PADAPT              pAdapt,
   IN PNDIS_REQUEST_EX    pLocalRequest,
   IN NDIS_STATUS         Status
   )
{
   POPEN_CONTEXT pOpenContext;
  
   DBGPRINT(("<== Pt DevRequestComplete/n"));
   pOpenContext = (POPEN_CONTEXT )pLocalRequest->RequestContext;
   pLocalRequest->RequestStatus = Status;
   NdisSetEvent( &pLocalRequest->RequestEvent );
   DBGPRINT(("<== Pt DevRequestComplete/n"));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值