herowill
驱动牛犊
驱动牛犊
  • 注册日期2004-04-27
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
阅读:1782回复:6

求助!(应用程序如何调用passthru)

楼主#
更多 发布于:2004-05-11 08:54
我是一名新手,现在在做毕业设计。
应用passthru例子,对封包进行处理。
现在遇到一个问题:地层的驱动程序已近写好了,如何编写一个应用程序来调用该驱动程序???
请大家多多指教,谢谢!!!
cmymfc
驱动牛犊
驱动牛犊
  • 注册日期2003-11-30
  • 最后登录2012-06-20
  • 粉丝0
  • 关注0
  • 积分62分
  • 威望8点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2004-05-11 09:27
据说是用deviceIoControl
先创建一个有名字的event, 然后在另一边waitfor....event发生时另一边就会自动返回继续执行
cmymfc
驱动牛犊
驱动牛犊
  • 注册日期2003-11-30
  • 最后登录2012-06-20
  • 粉丝0
  • 关注0
  • 积分62分
  • 威望8点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2004-05-11 09:28
我的毕设也是这个,交个朋友吧,qq:41562758, 我毕设才刚开始
herowill
驱动牛犊
驱动牛犊
  • 注册日期2004-04-27
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
地板#
发布于:2004-05-11 10:13
好啊,我的qq180167016
lixiangying
驱动牛犊
驱动牛犊
  • 注册日期2002-05-03
  • 最后登录2007-09-09
  • 粉丝0
  • 关注0
  • 积分9分
  • 威望1点
  • 贡献值0点
  • 好评度1点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2004-05-11 11:32
 [url] http://www.wd-3.com/archive/ExtendingPassthru.htm[/url]
 :D别忘了给粉啊 :cool:
herowill
驱动牛犊
驱动牛犊
  • 注册日期2004-04-27
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分0分
  • 威望0点
  • 贡献值0点
  • 好评度0点
  • 原创分0分
  • 专家分0分
5楼#
发布于:2004-05-11 15:44
上面的连接打不开,可以转贴过来吗,谢谢!!
还有如何给分呢??
我是新手
lixiangying
驱动牛犊
驱动牛犊
  • 注册日期2002-05-03
  • 最后登录2007-09-09
  • 粉丝0
  • 关注0
  • 积分9分
  • 威望1点
  • 贡献值0点
  • 好评度1点
  • 原创分0分
  • 专家分0分
6楼#
发布于:2004-05-11 18:00
Extending The Microsoft
PassThru NDIS Intermediate Driver
Part 1 Adding a DeviceIoControl Interface

July 15, 2003
Thomas F. Divine
Copyright ?2003 by Printing Communications Assoc., Inc. (PCAUSA). All rights reserved

The Microsoft?Windows?Driver Development Kit (DDK) PassThru NDIS Intermediate (IM) driver sample provides an excellent introduction to the mechanics of implementing a skeleton NDIS IM filter driver. (Thanks, NDIS Development Team!!!). However, the PassThru sample stops short of actually illustrating any observable function. To be of any actual use you must take the next step and add functionality of your own to the skeleton driver. If you are new to Windows driver development and/or NDIS drivers, then the next step can be a big one.

This article starts with the Microsoft PassThru NDIS IM driver sample from the Windows DDK Build 3790 (Windows Server 2003) and extends it by adding these functions:

Basic DeviceIoControl Interface - Provides basic mechanism for a Win32 application to communicate with the PassThru driver.
Binding Enumeration Function - Allows Win32 applications to query for the PassThru driver\'s binding information.
ADAPT Structure Reference Counting - Add reference counting logic to driver ADAPT structure.
Adapter Handle Open/Close functions - Mechanism to create a user-mode handle that is associated with a specific named PassThru binding. The handle can be used to make NDIS requests, read and write I/O and other operations on the specified adapter.
Handle Event Notifications - Here we deal with the cases (such as PnP) where the driver must invalidate a Win32 handle that has already been successfully opened.
Querying Information on a Open Adapter Handle - Add a mechanism to make Win32-initiated NDIS requests to query information on an open adapter handle.
Later articles in this series will describe additional extensions to PassThru.

 

Extended PassThru Driver (PassThruEx) Overview
Our starting point is the Microsoft PassThru NDIS IM driver sample from the Windows DDK Build 3790 (Windows Server 2003). The baseline PassThru consists of these key files:

\\PassThru - Windows DDK Build 3790 PassThru project folder
\\Driver - PassThru driver sources
PassThru.c - DriverEntry routine and any routines common to the PassThru miniport and protocol
PassThru.h - Header for PassThru
Miniport.c - Miniport related functions of the PassThru driver
Protocol.c - Protocol related functions of the PassThru driver
Precomp.h - Precompiled header file
Sources - Source files for Build utility
In addition, there is the PassThru.htm file that provides important information including:

Building the Sample
Installing The Sample
Code Tour
This series of articles add functionality to PassThru in s series of fairly small steps. Each step includes a description of the functionality being added and a description of the driver modifications needed to implement the new functionality. We will also develop a companion Win32 application to illustrate use of the extended functions.

New modules and header files will be added to house code the implements new functions. Most of the new driver code will be added in a module named PTExtend.c. A companion Win32 console application named \"PassThru User I/O\" (PTUserIo) is developed to illustrate user-mode functionality. The overall structure of the completed PassThruEx project looks like this:

\\PassThruEx - Extended PassThru project folder
\\Driver - PassThruEx driver sources
PassThru.c - DriverEntry routine and any routines common to the PassThru miniport and protocol
PassThru.h - Header for PassThru
Miniport.c - Miniport related functions of the PassThru driver
Protocol.c - Protocol related functions of the PassThru driver
Precomp.h - Precompiled header file
Sources - Source files for Build utility
IOCommon.h - Header shared between driver and user-mode application
PTExtend.c - Module containing new functions
\\Test - PassThruEx Win32 console test application
PTUserIo.cpp - Win32 console test application
PTUtils.cpp - Less important support functions
Sources to the modified PassThru driver and the companion test application are available for download. I would like to thank Microsoft for providing permission to include sources to the PassThru to driver for your benefit.

 

Adding the Basic DeviceIoControl Interface
Hopefully most readers will already be familiar with the IRP-based interface that is most commonly used to implement user/driver programming interfaces. Applications use the basic Win32 functions CreateFile, DeviceIoControl, ReadFile, WriteFile and CloseHandle for the user-mode end of the interface.

Drivers create a device object and a Win32-visible symbolic link name that can be opened in user-mode using CreateFile. The driver registers IRP-based functions that are (eventually) called to implement the kernel-mode end of the interface.

 

Driver Code
The foundation for the device I/O control interface has already been laid in the baseline PassThru driver sample. The NdisMRegisterDevice function is called from the PtRegisterDevice routine in PassThru.c to create a device object and a Win32-visible symbolic link name and to register I/O request handlers.

Baseline Code: PassThru.c Module, PtRegisterDevice Function
The code fragments below are from the baseline PassThru driver.

        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
                    );
 
Modified Code: PassThru.c Module, PtRegisterDevice Function
In the extended PassThru driver we eliminate use of the PtDispatch function (delete PtDispatch code from PassThru.c and remove its prototype from PassThru.h). In its place we call four explicit dispatch functions: 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
                    );
The new dispatch functions are implemented in PTExtend.c module under the \\PassThruEx\\Driver folder. At this point the dispatch handlers are simple stubs.

Click here to view the skeleton I/O dispatch handlers.

 

Application Code
Although the driver skeleton I/O dispatch handlers are simple stubs, they are sufficiently functional to allow us to start building and testing the Win32 application. At this point we can add code that open and closes a handle on the PassThru device symbolic link.

The Win32 test application is called \"PassThru User I/O\". It is a MFC console application implemented in the PTUserIo.cpp module under the \\PassThruEx\\Test folder.

The key user-mode function added at this step is the PtOpenControlChannel function. It uses CreateFile to open a handle on the \"\\\\.\\PassThru\" filespec name. A handle on a PassThruEx \"control channel\" is an ordinary handle to the device that is not associated with a specific adapter binding. Control channel handles are used to access global information such as the driver\'s binding list.

The skeleton _tmain function calls PtOpenControlChannel. If the PassThruEx driver with the new dispatch handlers is installed, then PtOpenControlChannel should succeed. If a valid handle is returned it is simply closed.

Click here to view Win32 code to open and close a handle on the PassThru device.

 

Adding a Binding Enumeration Function
The first useful function to be added to PassThru is a call to query the driver for a list of its current bindings. The binding names that are returned by this query identify the bindings that can be used in other binding-specific functions.

Implementation of this function is straightforward. A DeviceIoControl call will be made on the handle returned by PtOpenControlChannel. The call will pass a user-mode output buffer to the PassThru driver on IOCTL_PTUSERIO_ENUMERATE (defined in IOCommon.h). The driver\'s DevIoControl dispatcher will call a function that will fill the user\'s output buffer with an array of binding name strings.

When the DeviceIoControl call returns the user-mode application will have the driver\'s binding names.

 

Driver Code
The essential work of saving binding names is already done in the baseline PassThru driver. All we need to do is to add a handler for IOCTL_PTUSERIO_ENUMERATE that fills the user\'s buffer with the binding names.

The binding names are saved in the ADAPT structure that is created by the PtBindAdapter function for each successfully opened binding. The virtual adapter name (name passed to NdisIMInitializeDeviceInstanceEx) is saved in the DeviceName field of the ADAPT structure.

The ADAPT structures themselves are kept in a singly-linked list whose list head is the global variable pAdaptList.

All we have to do to implement the PassThruEx binding enumeration function is fill a user-provided buffer with DeviceName strings fetched from the list of ADAPT structures. In the PTExtend module we add a function called DevEnumerateBindings to fill the user\'s buffer with binding names. This function is called from the DevIoControl IOCTL dispatcher for the case IOCTL_PTUSERIO_ENUMERATE.

The buffer will be filled with an array of null-terminated Unicode strings with the end of the list being identified by an empty Unicode string.

There are a few additional details to attend to. It is necessary to account for the possibility that the adapter list could change while we were walking the list and copying binding names into the user\'s buffer. The baseline PassThru driver already has a spinlock for this purpose. We simply hold the GlobalLock while we are examining the adapter list. In addition, add appropriate sanity checks and __try/__except handlers to head off potential problems.

To examine the driver\'s binding enumeration code, follow the links below.

Click here to view DevEnumerateBindings handler

 

Application Code
Now we need to write user-mode code that illustrates how to call the driver for this information and how to display the binding information.

Implementation of this function is straightforward. A DeviceIoControl call will be made on the handle returned by PtOpenControlChannel. The call will pass a user-mode output buffer to the PassThru driver on IOCTL_PTUSERIO_ENUMERATE (defined in IOCommon.h). The driver\'s DevIoControl dispatcher will call a function that will fill the user\'s output buffer with an array of binding name strings..

First we make a trivial function PtEnumerateBindings that is a wrapper around DeviceIoControl. This function makes the IOCTL_PTUSERIO_ENUMERATE request to pass the user\'s output buffer to the driver. If successful, it returns a buffer that has been filled with the driver binding names.

The binding name buffer is an array of null-terminated Unicode strings with the end of the list being identified by an empty Unicode string.

The skeleton _tmain function calls PtEnumerateBindings. If the call is successful it loops through the buffer and displays binding names on the console.

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 (IOCTL_NDIS_QUERY_GLOBAL_STATS) 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
Click here to view Win32 code to query and display binding names.

 

Adding ADAPT Structure Reference Counting
At this point we really want to move on and add the logic to open a handle to a specific PassThru binding. However, there is a problem that must we must anticipate and deal with before we can proceed.

One of the key objects managed in the PassThru driver is the ADAPT structure, defined in the PassThru.h header file. The ADAPT structure contains essential information about each binding that as been successfully opened. In the baseline PassThru driver the \"life cycle\" of the ADAPT structure is controlled by the NDIS wrapper. The ADAPT structure is allocated and initialized in the  PtBindAdapter function in the Protocol.c module. It can be freed in the PtUnbindAdapter or in the MPHalt handler. Since NDIS is responsible for calling each of these three functions, the creation and destruction of each ADAPT structure is accomplished safely.

When we create our user-mode handle to a specific PassThru binding we will actually be creating a mapping between the handle and a specific ADAPT structure. We will need to access information contained in the ADAPT structure whenever we use our new API to perform operations on an adapter handle.

Hopefully the problem that must be dealt with is becoming clear. If the associated ADAPT structure is freed before the user-mode handle is closed, then the system can crash.

 We must provide a mechanism to insure that the ADAPT structure exists as long as the user-mode handle is open.

The most common method for controlling the \"life cycle\" of a temporary object is called \"reference counting\". When the ADAPT structure is created in PtBindAdapter the ADAPT reference count is set to 1. Once the reference count falls to zero, the ADAPT structure is freed.

 

Driver Code
First we add a RefCount member variable to the ADAPT structure.

Then we provide two functions that manipulate the RefCount:

PtRefAdapter safely increments RefCount by 1
PtDerefAdapter decrements and tests RefCount; if RefCount falls to zero then NdisFreeMemory is called to free the ADAPT memory.
Here are the basic implementations of these two functions:

VOID
PtRefAdapter( PADAPT pAdapt )
{
   NdisInterlockedIncrement( &pAdapt->RefCount );
}
VOID
PtDerefAdapter( PADAPT pAdapt )
{
   if( NdisInterlockedDecrement( &pAdapt->RefCount) == 0 )
   {
      NdisFreeMemory(pAdapt, 0, 0);
   }
}
We modify PtBindAdapter to set the initial ADAPT RefCount to 1. And finally we replace calls to NdisFreeMemory with calls to PtDerefAdapter in PtUnbindAdapter and MPHalt.

From a logical perspective at this point it looks like we have simply provided a more complicated way to call NdisFreeMemory. However, when we add the logic to open a handle to a specific PassThru binding we can use PtRefAdapter and PtDerefAdapter to insure that the AD APT structure persists as long as the user-mode handle is open.

It is also worthwhile to note that PtDerefAdapter can do more then simply free the memory being referenced. In the actual implementation of PtDerefAdapter we consolidate some duplicate code from PtUnbindAdapter and MPHalt into PtDerefAdapter.

Click here to view ADAPT reference counting code fragments

 

Adapter Handle Open/Close Functions
Actually, we\'ll address more then simply \"opening an adapter handle\" here. We\'ll also address the \"life cycle\" of a the handle - including ways to deal with the case that NDIS unbinds the adapter after the user-mode handle has been opened.

From the Win32 API perspective the process of opening an adapter handle will be simple and familiar. We\'ll provide a function PtOpenAdapter that takes a Unicode binding name (returned from PtEnumerateBindings) and returns a valid handle if successful. Adapter-specific operations (which we haven\'t yet invented...) can be performed on the adapter handle. When we\'re done we\'ll call PtCloseAdapter to close the adapter handle.

The basic steps for opening a handle within the driver are:

Search the pAdaptList to find the ADAPT structure whose DeviceName matches the specified binding name.
If match found, allocate an OPEN_CONTEXT structure used in the driver to manage handle-specific information.
Associate the handle with the adapter:
Save pointer to OPEN_CONTEXT in the ADAPT structure.
Save pointer to ADAPT structure in OPEN_CONTEXT structure. Increment the adapter reference count.
Associate handle with OPEN_CONTEXT by saving pointer to OPEN_CONTEXT in the FileObject FsContext field.
In subsequent calls to the driver I/O dispatcher the OPEN_CONTEXT structure can be recovered from the FileObject FsContext field.

The simplicity of the implementation introduces one restriction that should be noted:

 The driver enforces exclusive access on a per-binding basis. Only one adapter handle can be opened at a time on each PassThru binding.

Closing the handle involves adding logic in DevCleanup to cancel pending I/O on the handle and logic in DevClose to free (actually, dereference) the OPEN_CONTEXT structure.

There is one additional problem concerning adapter handles that must be dealt with.

 After the adapter handle has been opened it is entirely possible that NDIS can unbind the adapter associated with the handle.

This situation must be dealt with carefully.

 At this point it is worthwhile to crank up the NDIS Tester to insure that we haven\'t screwed up to badly. Focus on the Load/Unload tests for now. If you aren\'t familiar with NDIS Tester, see Stephan Wolf\'s article Testing Network Drivers with the NDIS Test Tool.

It is not possible to write bug-free code (at least the author has this weakness...). However, we can invent tests that can help flush out at least some faults. One test that we ran on the open adapter logic was:

Run the NDIS Tester Load/Unload test continuously on the PassThru miniport.
Concurrently run a repetitive test that opens a PassThru adapter handle for five seconds and then closes it.
Use the PoolTag utility to make sure that there aren\'t gross memory leaks.
We won\'t dwell on the details of this test except to say that 1.) eventually it passed with flying colors and 2.) initially it didn\'t.

 

Driver Code for Opening an Adapter Handle
The user-mode PtOpenAdater function will eventually call the driver\'s DevIoControl dispatcher on IOCTL_PTUSERIO_OPEN_ADAPTER (defined in IOCommon.h). The DevOpenAdapter function is then called to do the work.

The first thing that DevOpenAdapter does is to call a new function PtLookupAdapterByName to find the ADAPT structure whose DeviceName matching the binding name provided by the user in the input buffer. 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.
If PtLookupAdapterByName succeeds in finding a matching ADAPT binding, then an OPEN_CONTEXT structure is allocated to manage information about the specific open context (i.e., open handle). OPEN_CONTEXT is a allocated and initialized by the DevAllocateOpenContext function and reference counting is done using DevRefOpenContext and DevDerefOpenContext.

At this point we need to make an association between the user-mode handle and specific PassThru binding that we have found. Here are the two items to be associated:

User-Mode Handle - Represented by the FILE_OBJECT of the I/O stack location.
Specific PassThru Binding - Represented by the OPEN_CONTEXT structure (with pAdapt field pointing to the specified ADAPT structure).
The standard DDK doesn\'t actually say much about the use of the FILE_OBJECT in the I/O stack location. However, its use is vital in many situations. If you have ever written a file system driver then you can appreciate how precious the two FsContext and FsContext2 fields really are.

Simply put, the FILE_OBJECT in the I/O stack location represents an open instance of a PassThru device object. There is a one-to-one correspondence between the FILE_OBJECT and the user-mode handle (at least in simple cases...). Of most significance is the fact that the driver is free to assign whatever values it desires to the FsContext and FsContext2 fields and these values will be returned to the driver in the FILE_OBJECT for subsequent calls to the I/O dispatcher on the same handle.

So, to make the handle-binding association we set the FsContext field to be a pointer to our OPEN_CONTEXT structure. On subsequent calls to the I/O dispatcher we can examine FsContext. If it is not NULL, then it is a pointer to an OPEN_CONTEXT structure identifying the binding to be used.

 

Driver Code for Closing an Adapter Handle
When a user-mode handle is closed there will be some work to do in the DevCleanup and DevClose routines. The DevClose routine will, of course, make a call to DevDerefOpenContext.

 

Handling Surprise Unbinding On an Open Adapter Handle
Finally we must add the ability to deal with the case that NDIS unbinds the adapter after the user-mode handle has been successfully opened. To accommodate this we add function DevOnUnbindAdapter that notifies the open handle logic about the unbind. The code in this function must wait for all outstanding NDIS operations on the adapter to complete. Then it should cancel all pending I/O on the handle. The assumption must be that as soon as DevOnUnbindAdapter returns the adapter will be closed with a call to NdisCloseAdapter.

We add calls to DevOnUnbindAdapter from MPHalt and from PtUnbindAdapter.

Click here to view driver open handle code fragments

 

Application Code
Implementation of the user-mode code for opening and closing a PassThru adapter handle is straightforward. A DeviceIoControl call is made on a handle returned by PtOpenControlChannel. The call will pass a user-mode input buffer containing the binding name to the PassThru driver on IOCTL_PTUSERIO_OPEN_ADAPTER (defined in IOCommon.h). The driver\'s DevIoControl dispatcher will call the DevOpenAdapter function that does the kernel-mode work.

 

New Code: PTUserIo.cpp Module, PtOpenAdapter and PtCloseAdapter Functions
These are the user-mode functions that are used to open a binding-specific handle to the PassThru driver.

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

Handle Event Notifications
Just when you thought we had exhausted the topic of opening and closing Win32 handles to the PassThru driver we find that there is another major issue to consider.

 The Win32 Plug-and-Play (PnP) mechanism can halt a miniport or unbind a protocol binding at any time. Indeed, PnP can initiate unloading of the PassThru driver at any time.

The bottom line here is that PnP can cause events that invalidate a Win32 handle to the PassThru driver after it has been successfully opened.

The reference counting logic helps deal with this situation to a limited extent. It is at least \"safe\" for NDIS to unbind adapters while a Win32 handle is open to an adapter. A little memory is not released as soon as desired, but the system should not crash. However, at this point there is no mechanism to inform the application that the adapter handle is invalid.

Of more significance is that if NDIS unbinds all adapters on the PassThru driver and attempts to unload the driver, the unload will not happen until all handles on the PassThru device are closed. It is important to notify the application of this situation to allow the PassThru driver to be unloaded.

There are two aspects of this issue that must be accommodated:

Some PnP events will not complete while a Win32 handle is open on the PassThru device.
The Win32 application needs to be notified of these events in order to handle them gracefully.
A fairly frequent topic on the newsgroups is: \"My driver can\'t be unloaded\". One possible reason could be that a Win32 handle remains open; as long as the Win32 handle to a driver remains open, the driver can\'t be unloaded.

One trivial approach to this problem is to only open Win32 handles to the PassThru driver for brief intervals. In this case, the handles are closed most of the time and the process of re-opening the handles becomes the notification mechanism (re-opening the handle will fail...). In its current state the PTUserIo application uses this approach; Win32 handles are only opened only briefly and are closed when the application exits. No problem.

On the other hand, there are applications (such as packet monitoring applications) which necessarily must keep handles opened for extended intervals. These types of applications need a mechanism that allows the driver to inform the application of events that invalidate the Win32 handle.

We are not going to develop the handle-invalidating notification mechanism in Part 1 of this series of articles. Instead we will deal with it in later topics when we actually add features that require a PassThru handle to remain open for extended periods.

 

Querying Information on an Adapter Handle
Now that we have developed a mechanism to open a binding-specific handle on the PassThru driver it\'s time to do something with it. Adding the capability to make NDIS requests to query information is first on the list. Once we can query information on a handle then we can fetch and display human-readable information about the adapter.

Conceptually the Win32 API to query information is a simple DeviceIoControl call on the handle opened by PtOpenAdapter. The input buffer is used to pass the NDIS object identifier (OID) of interest to the driver. The driver\'s protocol section will then make an NdisRequest call to query information on the specified OID and fill the user\'s output buffer with the returned information.

However, we must realize that the baseline PassThru driver functionality is already making NdisRequests as part of the miniport pass-through logic. Our Win32-initiated NdisRequests must not break the existing functionality.

In addition, we need to anticipate that a fully functional NDIS IM driver will actually have three independent NdisRequest initiators:

Miniport MPQueryInformation/MPSetInformation pass-through (exists in baseline driver)

Win32-Initiated Requests (subject of this topic)

Autonomous Driver-Initiated Requests (future topic)

The simplicity of the implementation introduces restrictions that should be noted:

 Queries made on an open adapter handle on the PassThru driver are synchronous and must be serialized by the Win32 application.

When we are done we can modify the PTUserIo application to fetch human-readable information using the new PtQueryInformation API. This test was run on a Windows XP SP1 system with three NDIS miniports installed:

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
 

Driver Code
Before we can add our Win32-initiated NDIS query information extensions we need to understand how the baseline PassThru driver handles calls to the MPQueryInformation handler.

The baseline PassThru driver implementation depends on the fact that NDIS serializes calls to this function. This means that there can never be more then one call to MPQueryInformation in progress on any PassThru binding at any given time. Based on this fact the PassThru driver provides a single NDIS_REQUEST structure in the ADAPT structure for each binding. Calls to MPQueryInformation use the per-adapter NDIS_REQUEST structure to make the NdisRequest that passes the query to the lower-level miniport. The NdisRequest call is made synchronously waiting on a NDIS_EVENT until the PtRequestComplete handler is called.

 Making an appropriate modification to PtRequestComplete is the key change that must be made to the baseline code to add our Win32-initiated NDIS query information implementation.

Fortunately, all that is needed in PtRequestComplete to distinguish between miniport-initiated requests and Win32-initiated requests is a simple test on the NDIS_REQUEST pointer, as illustrated below:

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;
   }
   ...
}
The mechanism that we will add to make our Win32-initiated call to NdisRequest will add a second level of indirection for completing requests. This is done by adding a containing structure around the basic NDIS_REQUEST structure:

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;
The extended NDIS_REQUEST_EX structure includes fields for request-specific request completion routine and request-specific completion context. It would be used in the PtRequestComplete handler like this:

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);
    ...    
}
Now that handling of request completion is taken care of we can implement the rest of the driver code for making the Win32-initiated requests:

Add a NDIS_REQUEST_EX member to the OPEN_CONTEXT structure. This member is used to make NdisRequests on the open adapter handle.
Modify DevAllocateOpenContext to initialize the NDIS_REQUEST_EX NDIS_EVENT.
Add a new DevRequestComplete handler, which is the request-specific handler for Win32-initiated requests.
Modify DevIoControl to dispatch IOCTL_PTUSERIO_QUERY_INFORMATION to DevQueryInformation.
Implement DevQueryInformation to make the NdisRequest call and wait for completion on an NDIS_EVENT.
 

Click here to view driver query-handling code fragments

 

Application Code
Implementation of the user-mode code for querying NDIS information on an open adapter handle is straightforward. A DeviceIoControl call is made on a handle returned by PtOpenAdapter. The call will pass a user-mode input buffer containing the OID value to be queried to the PassThru driver on IOCTL_PTUSERIO_QUERY_INFORMATION (defined in IOCommon.h). The driver\'s DevIoControl dispatcher will call the DevQueryInformation function that does the kernel-mode work.

New Code: PTUserIo.cpp Module, PtQueryInformation Function
This the user-mode function that is used to to open a binding-specific handle to the PassThru driver.

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

Author\'s Footnote
Frankly, writing this article took quite a bit longer then I expected...

Because I have been working with NDIS since the days of NDIS 2.0, TSRs and \"Windows for Workgroups\" (how many remember that?) I thought that the introductory topic of adding a DeviceIoControl API to PassThru and at least getting to the point of making synchronous NDIS requests would be \"a walk in the park\". However, the additional task of explaining each step really slowed me down - and that was actually a good thing. As I wrote some of the explanations I noticed faults in the code that really should be corrected.

I hope that the material presented in this series of articles so far is fairly robust and of interest to at least some novice NDIS developers.

 

Copyright (c) Information
This article and the companion code are intended to be used for the benefit of the reader of this article. The companion code is intended to help the reader derive and improve their own NDIS Intermediate driver for Windows platforms.

The Code
Here is the general form of the Copyright Notice found in the companion code for this article:

Companion Sample Code for the Article
\"Extending the Microsoft PassThru NDIS Intermediate Driver\"

Portions Copyright (c) 1992-2000 Microsoft Corporation; used by permission.
Portions Copyright (c) 2003 Printing Communications Associates, Inc. (PCAUSA)

The right to use this code in your own derivative works is granted so long
as 1.) your own derivative works include significant modifications of your
own, 2.) you retain the above copyright notices and this paragraph in its
entirety within sources derived from this code.
This product includes software developed by PCAUSA. The name of PCAUSA
may not be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS\'\' AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 

The Article
This article is:

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.

 

Download the PassThruEx Part 1 Source Code
It is certainly intended that you should be able to incorporate the ideas and code presented in this article into your own code. At the same time, for a free code like this it is essential that you understand that the author does not provide any warranty whatsoever that the sample code is fit for any purpose whatsoever.

 In other words: This article and the accompanying sample code are provided for educational purposes only. Use at your own risk.

[ Click here to download PassThruEx source code... ]

 [url]http://www.wd-3.com/downloads/ExtendingPassthru.zip [/url]
你的第一个帖子下面有给分按扭啊。

[编辑 -  5/11/04 by  lixiangying]
游客

返回顶部