(转)驱动开发之五 --- TDI之三 【译文】

http://hi.baidu.com/combojiang/item/778f34ad3c88e9ac29ce9d32

接上

步骤2:打开连接上下文

第二步是打开连接上下文。在你建立的连接中,执行后续的操作会用到这个句柄。这也是由ZwCreateFile完成,也是在相同的设备"\Device\Tcp"上执行。实际上这个设备允许你打开三个不同的句柄。这三个句柄是传输句柄,连接上下文句柄和控制句柄。需要注意一个常犯的错误的是,一个句柄打开成功,但实际上确是一个错误打开的句柄。这是由于他们是由"Extended Attributes"来决定打开哪个句柄的。显然,如果驱动程序不能识别EA("Extended Attributes")的值,那么它就会打开一个默认的句柄类型,控制句柄。这是MSDN中关于创建部分的文档描述的。

 

下面的代码演示了打开一个连接上下文。注意你需要制定一个"CONNECTION_CONTEXT"类型的指针,使用这个指针指向用户定义的数据。后面你会注意到一些事件回调会提供这个指针给你。这也是你可以使用这个上下文值的本质。

 

NTSTATUS TdiFuncs_OpenConnection(PHANDLE pTdiHandle, PFILE_OBJECT *pFileObject)

{

    NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;

    UNICODE_STRING usTdiDriverNameString;

    OBJECT_ATTRIBUTES oaTdiDriverNameAttributes;

    IO_STATUS_BLOCK IoStatusBlock;

    char DataBlob[sizeof(FILE_FULL_EA_INFORMATION) +

              TDI_CONNECTION_CONTEXT_LENGTH + 300] = {0};

    PFILE_FULL_EA_INFORMATION pExtendedAttributesInformation =

                    (PFILE_FULL_EA_INFORMATION)&DataBlob;

    UINT dwEASize = 0;

        

    /*

     * Initialize the name of the device to be opened. ZwCreateFile

     * takes an OBJECT_ATTRIBUTES structure as the name of the device

     * to open. This is then a two step process.

     *

     * 1 - Create a UNICODE_STRING data structure from a unicode string.

     * 2 - Create a OBJECT_ATTRIBUTES data structure from a UNICODE_STRING.

     *

     */

 

    RtlInitUnicodeString(&usTdiDriverNameString, L"\\Device\\Tcp");

    InitializeObjectAttributes(&oaTdiDriverNameAttributes,

            &usTdiDriverNameString,

            OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,

            NULL, NULL);

 

    /*

     * The second step is to initialize the Extended Attributes data structure.

     *

     * EaName    = TdiConnectionContext, 0, Your User Defined Context Data

     *                                              (Actually a pointer to it)

     * EaNameLength = Length of TdiConnectionContext

     * EaValueLength = Entire Length

     */

     RtlCopyMemory(&pExtendedAttributesInformation->EaName,

                        TdiConnectionContext, TDI_CONNECTION_CONTEXT_LENGTH);

 

     pExtendedAttributesInformation->EaNameLength =

                                          TDI_CONNECTION_CONTEXT_LENGTH;

     pExtendedAttributesInformation->EaValueLength =

                                       TDI_CONNECTION_CONTEXT_LENGTH;

              /* Must be at least TDI_CONNECTION_CONTEXT_LENGTH */

 

     dwEASize = sizeof(DataBlob);

 

     NtStatus = ZwCreateFile(pTdiHandle,

         FILE_READ_EA | FILE_WRITE_EA, &oaTdiDriverNameAttributes,

         &IoStatusBlock, NULL,

         FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, 0,

         pExtendedAttributesInformation, dwEASize);

 

     if(NT_SUCCESS(NtStatus))

     {

          NtStatus = ObReferenceObjectByHandle(*pTdiHandle,

                          GENERIC_READ | GENERIC_WRITE,

                          NULL, KernelMode,

                         (PVOID *)pFileObject, NULL);      

 

          if(!NT_SUCCESS(NtStatus))

          {

              ZwClose(*pTdiHandle);

          }

     }

 

     return NtStatus;

}

 

步骤3:关联传输地址和连接上下文

在你执行任何操作之前,你需要关联这两个句柄,传输句柄和连接上下文句柄。这个通过给设备发送一个IOCTL来完成。是否你还记得先前我们是怎样发送IOCTL的?我们需要分配一个IRP,设置参数和发送它到设备。由于TDI头文件提供了宏和其他的函数可以帮助我们简化这个步骤。TdiBuildInternalDeviceControlIrp实际是一个宏,它内部调用了IoBuildDeviceIoControlRequest. 给这个宏的一些参数实际被忽略了,但是对于注释来讲还是有用的(就像提供的IOCTL一样)。这个API很简单,我们为了演示目的使用它。然而使用其它机制来创建IRP,就像IoAllocateIrp,有一些优点,这个将在后面叙述。其他的宏被用于给底层的驱动简单的设置IO_STACK_LOCATION的参数。

 

这里有一件事情你需要注意,这里跟我们上次谈到的有些不同的是"STATUS_PENDING"。本篇后面将会讨论这个。

下面的代码演示如何使用它。

NTSTATUS TdiFuncs_AssociateTransportAndConnection(HANDLE hTransportAddress,

                                     PFILE_OBJECT pfoConnection)

{

    NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;

    PIRP pIrp;

    IO_STATUS_BLOCK IoStatusBlock = {0};

    PDEVICE_OBJECT pTdiDevice;

    TDI_COMPLETION_CONTEXT TdiCompletionContext;

 

    KeInitializeEvent(&TdiCompletionContext.kCompleteEvent,

                                NotificationEvent, FALSE);

 

    /*

     * The TDI Device Object is required to send these

     *                 requests to the TDI Driver.

     */

 

    pTdiDevice = IoGetRelatedDeviceObject(pfoConnection);

    

    /*

     * Step 1: Build the IRP. TDI defines several macros and functions

     *         that can quickly create IRP's, etc. for variuos purposes.

     *          While this can be done manually it's easiest to use the macros.

     *

    * http://msdn.microsoft.com/library/en-us/network/hh/network/

     *        34bldmac_f430860a-9ae2-4379-bffc-6b0a81092e7c.xml.asp?frame=true

     */

    pIrp = TdiBuildInternalDeviceControlIrp(TDI_ASSOCIATE_ADDRESS,

          pTdiDevice, pfoConnection, &TdiCompletionContext.kCompleteEvent,

          &IoStatusBlock);

 

    if(pIrp)

    {

        /*

         * Step 2: Add the correct parameters into the IRP.

         */

        TdiBuildAssociateAddress(pIrp, pTdiDevice,

                            pfoConnection, NULL, NULL, hTransportAddress);

 

        NtStatus = IoCallDriver(pTdiDevice, pIrp);

 

        /*

         * If the status returned is STATUS_PENDING this means that the IRP

         * will not be completed synchronously and the driver has queued the

         * IRP for later processing. This is fine but we do not want

         * to return this thread, we are a synchronous call so we want   

         * to wait until it has completed. The EVENT that we provided will

        * be set when the IRP completes.

         */

 

        if(NtStatus == STATUS_PENDING)

        {

            KeWaitForSingleObject(&TdiCompletionContext.kCompleteEvent,

                                   Executive, KernelMode, FALSE, NULL);

            /*

             * Find the Status of the completed IRP

             */

 

            NtStatus = IoStatusBlock.Status;

        }

 

    }

 

    return NtStatus;

}

posted @ 2013-01-15 11:08  himessage  阅读(899)  评论(0编辑  收藏  举报