最近看了好多文章,也写了好多博客,但是,最近少了很多实践的关系,这一部分是项目的问题,其实大部分还是自己的问题,今天,竟然,知道,随便修改一点微软的例子还可以赚钱,之前自己还真是没有想到的。后续,自己要好好的理解下例子,好好的实践一下。今天,让我知道,我做这么多,看这么多,都是有用的,有帮助的。所以,自己还是要立足这一块,好好的往里面深挖下去。

今天趁热打铁,来看一下微软的WDK中的有关WSK的例子,后续,主要的精力还是要放在这些例子的实现和理解上。闲话不说,首先来看驱动的入口函数。

NTSTATUS
  DriverEntry(
   _In_ PDRIVER_OBJECT DriverObject,
   _In_ PUNICODE_STRING RegistryPath
  )
  {
   NTSTATUS status;

WSK_CLIENT_NPI wskClientNpi;

//这里首先定义了一个结构体,WSK_CLIENT_NPI,我们先看一下这个结构体。
   
   typedef struct _WSK_CLIENT_NPI {
    PVOID                     ClientContext;
    const WSK_CLIENT_DISPATCH *Dispatch;
   } WSK_CLIENT_NPI, *PWSK_CLIENT_NPI;
   
            MSDN中的解释是,这个结构是是标识Network Programming Interface(NPI),执行是通过WSK客户端来实现的。

UNREFERENCED_PARAMETER(RegistryPath);

PAGED_CODE();
    
   // Allocate a socket context that will be used for queueing an operation
   // to setup a listening socket that will accept incoming connections
   WskSampleListeningSocketContext = WskSampleAllocateSocketContext(
                                            &WskSampleWorkQueue, 0);
           
   这里为套接字的操作定义了全局的上下文,用来保存数据。

if(WskSampleListeningSocketContext == NULL) {
    return STATUS_INSUFFICIENT_RESOURCES;
   }

// Register with WSK.
   //填充WSK_CLIENT_NPI结构体成员。
   wskClientNpi.ClientContext = NULL;
   wskClientNpi.Dispatch = &WskSampleClientDispatch;
   status = WskRegister(&wskClientNpi, &WskSampleRegistration);
   //看下这个WskRegister函数
   NTSTATUS WskRegister(
    _In_   PWSK_CLIENT_NPI WskClientNpi,
    _Out_  PWSK_REGISTRATION WskRegistration
   );
   
   这里这个输出的参数标识了WSK应用注册实例的内存地址的指针。后面我们需要使用这个地址的。这个是放在全局变量中。

if(!NT_SUCCESS(status)) {
    WskSampleFreeSocketContext(WskSampleListeningSocketContext);
    return status;
   }
             下面初始化了一个全局工作队列。
   // Initialize and start the global work queue
   status = WskSampleStartWorkQueue(&WskSampleWorkQueue);
   这里主要是做初始化的动作,创建了一个线程,并设置了一个例程,WskSampleWorkerThread。
   
   NTSTATUS
   WskSampleStartWorkQueue(
    _Out_ PWSKSAMPLE_WORK_QUEUE WorkQueue
   )
   {
    NTSTATUS status;
    HANDLE threadHandle;

PAGED_CODE();
    
    InitializeSListHead(&WorkQueue->Head);
    KeInitializeEvent(&WorkQueue->Event, SynchronizationEvent, FALSE);
    WorkQueue->Stop = FALSE;

status = PsCreateSystemThread(
      &threadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL,
      WskSampleWorkerThread, WorkQueue);

if(!NT_SUCCESS(status)) {
     return status;
    }

status = ObReferenceObjectByHandle(
      threadHandle, THREAD_ALL_ACCESS, NULL, KernelMode,
      &WorkQueue->Thread, NULL);
    
    ZwClose(threadHandle);

if(!NT_SUCCESS(status)) {
     WorkQueue->Stop = TRUE;
     KeSetEvent(&WorkQueue->Event, 0, FALSE);
    }

return status;
   }
   
   再看一下这个线程:我们创建这个线程的时候,将WorkQueue当成参数传送给例程。
   
   VOID
   WskSampleWorkerThread (
    _In_ PVOID Context
   )
   {
    PWSKSAMPLE_WORK_QUEUE workQueue;
    PSLIST_ENTRY listEntryRev, listEntry, next;
    
    PAGED_CODE();

workQueue = (PWSKSAMPLE_WORK_QUEUE)Context;

for(;;) {
        
     // Flush all the queued operations into a local list
     listEntryRev = InterlockedFlushSList(&workQueue->Head);

if(listEntryRev == NULL) {

// There's no work to do. If we are allowed to stop, then stop.
      if(workQueue->Stop) {
       DoTraceMessage(TRCINFO, "WorkerThread: WQ %p exit", workQueue);
       break;
      }

DoTraceMessage(TRCINFO, "WorkerThread: WQ %p wait", workQueue);

// Otherwise, wait for more operations to be enqueued.
      KeWaitForSingleObject(&workQueue->Event, 
       Executive, KernelMode, FALSE, 0);
       continue;
     }

DoTraceMessage(TRCINFO, "WorkerThread: WQ %p process", workQueue);

// Need to reverse the flushed list in order to preserve the FIFO order
     listEntry = NULL;
     while (listEntryRev != NULL) {
      next = listEntryRev->Next;
      listEntryRev->Next = listEntry;
      listEntry = listEntryRev;
      listEntryRev = next;
     }
                    //这个地方非常重要,看了半天终于知道了,这里,当ListEntry不为NULL的时候,会进入opHandler(socketOpContext);进行处理。
     // Now process the correctly ordered list of operations one by one
     while(listEntry) {

PWSKSAMPLE_SOCKET_OP_CONTEXT socketOpContext =
       CONTAINING_RECORD(listEntry, 
        WSKSAMPLE_SOCKET_OP_CONTEXT, QueueEntry);
        PWSKSAMPLE_OP_HANDLER_FN opHandler = socketOpContext->OpHandler;
            
       listEntry = listEntry->Next;

opHandler(socketOpContext);
     }
     }
      PsTerminateSystemThread(STATUS_SUCCESS);
   }
   
   if(!NT_SUCCESS(status)) {
    WskDeregister(&WskSampleRegistration);
    WskSampleFreeSocketContext(WskSampleListeningSocketContext);
    return status;
   }
            //我们再看这里,这里进行实例化的入队操作,ListEntry就不为NULL了。所以上面的并指定了例程,进行处理,这里定义的例程是WskSampleOpStartListen。
   //DriverEntry函数基本就完成了。
   // Enqueue the first operation to setup the listening socket
   WskSampleEnqueueOp(&WskSampleListeningSocketContext->OpContext[0],
                       WskSampleOpStartListen);
    
   // Everything has been initiated successfully. Now we can enable the
   // unload routine and return.
   DriverObject->DriverUnload = WskSampleUnload;

// Initialize software tracing
   WPP_INIT_TRACING(DriverObject, RegistryPath);
    
   DoTraceMessage(TRCINFO, "LOADED");
    
   return STATUS_SUCCESS;
  }

在看,这个
  VOID
  WskSampleOpStartListen(
  _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext
  )
  {
   NTSTATUS status;
   WSK_PROVIDER_NPI wskProviderNpi;

PAGED_CODE();

// This is the very first operation that runs in the worker thread.
   // This operation is made up of multiple WSK calls, and no other
   // operations will be processed until this compound operation is
   // finished synchronously in the context of the worker thread.
            
   //这里首先调用的是WskCaptureProviderNPI,这个函数在MSDN中的解释是,当注册的客户端在WSK子系统可以应用的时候捕捉提供的NPI,这里设置的是无限等待。
   NTSTATUS WskCaptureProviderNPI(
    _In_   PWSK_REGISTRATION WskRegistration,
    _In_   ULONG WaitTimeout,
    _Out_  PWSK_PROVIDER_NPI WskProviderNpi
   );
   这里还有个返回值,就是指向的是我们需要调用的一些函数指针。我们先来看一下这个结构体。
   
   typedef struct _WSK_PROVIDER_NPI {
    PWSK_CLIENT                 Client;
    const WSK_PROVIDER_DISPATCH *Dispatch;
   } WSK_PROVIDER_NPI, *PWSK_PROVIDER_NPI;
   
   继续看:
   
   typedef struct _WSK_PROVIDER_DISPATCH {
    USHORT                    Version;
    USHORT                    Reserved;
    PFN_WSK_SOCKET            WskSocket;
    PFN_WSK_SOCKET_CONNECT    WskSocketConnect;
    PFN_WSK_CONTROL_CLIENT    WskControlClient;
   #if (NTDDI_VERSION >= NTDDI_WIN7)
    PFN_WSK_GET_ADDRESS_INFO  WskGetAddressInfo;
    PFN_WSK_FREE_ADDRESS_INFO WskFreeAddressInfo;
    PFN_WSK_GET_NAME_INFO     WskGetNameInfo;
   #endif 
   } WSK_PROVIDER_DISPATCH, *PWSK_PROVIDER_DISPATCH;
   
   这两个结构体很重要,我们后续还要再来看。

// Capture the WSK Provider NPI
   status = WskCaptureProviderNPI(
                &WskSampleRegistration, 
                WSK_INFINITE_WAIT,
                &wskProviderNpi);

if(NT_SUCCESS(status)) {

// Create a listening socket
    WskSampleSetupListeningSocket(&wskProviderNpi, SocketOpContext);

// Release the WSK provider NPI since we won't use it anymore
    WskReleaseProviderNPI(&WskSampleRegistration);
   }
   else {
    // WskCaptureProviderNPI will fail if WskDeregister is called
    // from the Driver Unload routine before the WSK subsystem
    // becomes ready.
    DoTraceMessage(TRCINFO, 
     "OpStartListen: WskCaptureProviderNPI failed 0x%lx", status);
    }
  }
  
  这里,WskSampleSetupListeningSocket所有操作的注册都是在这个里面进行。
  
  VOID
  WskSampleSetupListeningSocket(
   _In_ PWSK_PROVIDER_NPI WskProviderNpi,
   _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext
  )
  {
   NTSTATUS status;
   WSK_EVENT_CALLBACK_CONTROL callbackControl;
   KEVENT compEvent;
   ULONG optionValue;
   PWSK_SOCKET listeningSocket = NULL;
   PWSK_PROVIDER_LISTEN_DISPATCH dispatch = NULL;
   PWSKSAMPLE_SOCKET_CONTEXT socketContext = SocketOpContext->SocketContext;
   PIRP irp = SocketOpContext->Irp;

PAGED_CODE();
    
   KeInitializeEvent(&compEvent, SynchronizationEvent, FALSE);

if(socketContext->Socket != NULL) {

// If there's already a socket then we must be getting called because
    // the WskAcceptevent indicated a NULL AcceptSocket, i.e. the listening
    // socket is no longer functional. So, we will close the existing
    // listening socket and try to create a new one.

if(socketContext->StopListening) {
     // Listening socket is already being closed because the driver
     // is being unloaded. So, there's no need to recreate it.
     return;
    }
        
    IoReuseIrp(irp, STATUS_UNSUCCESSFUL);

IoSetCompletionRoutine(irp, 
     WskSampleSyncIrpCompletionRoutine,
     &compEvent, TRUE, TRUE, TRUE);

((PWSK_PROVIDER_LISTEN_DISPATCH)
     socketContext->Socket->Dispatch)->
     WskCloseSocket(socketContext->Socket, irp);

KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL);

ASSERT(NT_SUCCESS(irp->IoStatus.Status));
    socketContext->Socket = NULL;

IoReuseIrp(irp, STATUS_UNSUCCESSFUL);
   }
   else 
   {
       //这里,我们自动使能套接字的事件回调函数,当套接字被创建的时候,套接字上的事件回调被自动使能。
    //这里WskAcceptEvent事件回调被自动使能。
    // First, configure the WSK client such that WskAcceptEvent callback
    // will be automatically enabled on the listening socket upon creation.
    callbackControl.NpiId = (PNPIID)&NPI_WSK_INTERFACE_ID;
    callbackControl.EventMask = WSK_EVENT_ACCEPT;
    status = WskProviderNpi->Dispatch->WskControlClient(
                            WskProviderNpi->Client,
                            WSK_SET_STATIC_EVENT_CALLBACKS,
                            sizeof(callbackControl),
                            &callbackControl,
                            0,
                            NULL,
                            NULL,
                            NULL);
    if(!NT_SUCCESS(status)) {
     DoTraceMessage(TRCERROR, 
    "SetupListeningSocket: WSK_SET_STATIC_EVENT_CALLBACKS FAIL 0x%lx",
    status);
    goto failexit;        
    }
   }
    
   // Create a listening socket over AF_INET6 address family. Put the socket
   // into dual-family mode by setting IPV6_V6ONLY option to FALSE so that 
   // AF_INET traffic is also handled over the same socket.

IoSetCompletionRoutine(irp,
    WskSampleSyncIrpCompletionRoutine,
    &compEvent, TRUE, TRUE, TRUE);

// We do not need to check the return status since the actual completion
   // status will be captured from the IRP after the IRP is completed.
   //这个是创建套接字的函数,我们先看一下这个原型。这里指定了派遣例程WskSampleClientListenDispatch。

NTSTATUS APIENTRY WskSocket(
    _In_      PWSK_CLIENT Client,
    _In_      ADDRESS_FAMILY AddressFamily,
    _In_      USHORT SocketType,
    _In_      ULONG Protocol,
    _In_      ULONG Flags,
    _In_opt_  PVOID SocketContext,
    _In_opt_  const VOID *Dispatch,
    _In_opt_  PEPROCESS OwningProcess,
    _In_opt_  PETHREAD OwningThread,
    _In_opt_  PSECURITY_DESCRIPTOR SecurityDescriptor,
    _Inout_   PIRP Irp
   );

WskProviderNpi->Dispatch->WskSocket(
                WskProviderNpi->Client,
                AF_INET6,
                SOCK_STREAM,
                IPPROTO_TCP,
                WSK_FLAG_LISTEN_SOCKET,
                socketContext,
                &WskSampleClientListenDispatch,
                NULL, // Process
                NULL, // Thread
                NULL, // SecurityDescriptor
                irp);
    
   KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL);

if(!NT_SUCCESS(irp->IoStatus.Status)) {
    DoTraceMessage(TRCERROR, "SetupListeningSocket: WskSocket FAIL 0x%lx",
     irp->IoStatus.Status);
    goto failexit;
   }
   
  //当函数成功的时候,irp->IoStatus.Information包含一个指向创建的套接字的指针。
   
   typedef struct _WSK_SOCKET {
    const VOID *Dispatch;
   } WSK_SOCKET, *PWSK_SOCKET;
   
   返回的套接字上Dispatch,会提供一个跟套接字关联的函数列表。
   
   Socket category      Dispatch table structure 
   
   Basic socket      WSK_PROVIDER_BASIC_DISPATCH 
 
   Listening socket     WSK_PROVIDER_LISTEN_DISPATCH 
 
   Datagram socket      WSK_PROVIDER_DATAGRAM_DISPATCH 
 
   Connection-oriented socket   WSK_PROVIDER_CONNECTION_DISPATCH 
   
   因为是监听套接字,所以是WSK_PROVIDER_LISTEN_DISPATCH
   
   typedef struct _WSK_PROVIDER_LISTEN_DISPATCH {
    WSK_PROVIDER_BASIC_DISPATCH Basic;
    PFN_WSK_BIND                WskBind;
    PFN_WSK_ACCEPT              WskAccept;
    PFN_WSK_INSPECT_COMPLETE    WskInspectComplete;
    PFN_WSK_GET_LOCAL_ADDRESS   WskGetLocalAddress;
   } WSK_PROVIDER_LISTEN_DISPATCH, *PWSK_PROVIDER_LISTEN_DISPATCH;
   
   typedef struct _WSK_PROVIDER_BASIC_DISPATCH {
    PFN_WSK_CONTROL_SOCKET WskControlSocket;
    PFN_WSK_CLOSE_SOCKET   WskCloseSocket;
   } WSK_PROVIDER_BASIC_DISPATCH, *PWSK_PROVIDER_BASIC_DISPATCH;
   
 
      listeningSocket = (PWSK_SOCKET)irp->IoStatus.Information;
   dispatch = (PWSK_PROVIDER_LISTEN_DISPATCH)listeningSocket->Dispatch;
    
   // Set IPV6_V6ONLY to FALSE before the bind operation

optionValue = 0;

IoReuseIrp(irp, STATUS_UNSUCCESSFUL);    
   IoSetCompletionRoutine(irp,
    WskSampleSyncIrpCompletionRoutine,
    &compEvent, TRUE, TRUE, TRUE);

// We do not need to check the return status since the actual completion
   // status will be captured from the IRP after the IRP is completed.
   
   这里,我们调用WskControlSocket对套接字进行了相关的设置。
   dispatch->WskControlSocket(
                listeningSocket,
                WskSetOption,
                IPV6_V6ONLY,
                IPPROTO_IPV6,
                sizeof(optionValue),
                &optionValue,
                0,
                NULL,
                NULL,
                irp);
    
   KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL);

if(!NT_SUCCESS(irp->IoStatus.Status)) {
    DoTraceMessage(TRCERROR, "SetupListeningSocket: IPV6_V6ONLY FAIL 0x%lx",
     irp->IoStatus.Status);
    goto failexit;
   }

// Bind the socket to the wildcard address. Once bind is completed,
   // WSK provider will make WskAcceptEvent callbacks as connections arrive.
   IoReuseIrp(irp, STATUS_UNSUCCESSFUL);
   IoSetCompletionRoutine(irp,
    WskSampleSyncIrpCompletionRoutine,
    &compEvent, TRUE, TRUE, TRUE);

// We do not need to check the return status since the actual completion
   // status will be captured from the IRP after the IRP is completed.
   //将套接字绑定到一个本地地址上,来进行侦听,当数据报上相关的事件发生时,会调用相关的事件回调函数。
   //上面我们指定了WskSampleClientListenDispatch
   
   // IPv6 wildcard address and port number 40007 to listen on
   SOCKADDR_IN6 IPv6ListeningAddress = {
                    AF_INET6, 
                    0x479c, // 40007 in hex in network byte order 
                    0, 
                    IN6ADDR_ANY_INIT,
                    0};
     
   NTSTATUS APIENTRY WskBind(
    _In_        PWSK_SOCKET Socket,
    _In_        PSOCKADDR LocalAddress,
    _Reserved_  ULONG Flags,
    _Inout_     PIRP Irp
   );

dispatch->WskBind(
                listeningSocket,
                (PSOCKADDR)&IPv6ListeningAddress,
                0,
                irp);

KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL);

if(!NT_SUCCESS(irp->IoStatus.Status)) {
    DoTraceMessage(TRCERROR, "SetupListeningSocket: WskBind FAIL 0x%lx",
    irp->IoStatus.Status);
    goto failexit;
   }

// Store the WSK socket pointer in the socket context only if everthing
   // has succeeded. Otherwise, the listening WSK socket, if one was created
   // successfully, will be closed inline below.
   ASSERT(socketContext->Socket == NULL);
   socketContext->Socket = listeningSocket;

DoTraceMessage(TRCINFO, "SetupListeningSocket: %p %p", 
    socketContext, SocketOpContext);
    
   return;
    
failexit:

if(listeningSocket) {

IoReuseIrp(irp, STATUS_UNSUCCESSFUL);
    IoSetCompletionRoutine(irp, 
    WskSampleSyncIrpCompletionRoutine,
     &compEvent, TRUE, TRUE, TRUE);

dispatch->WskCloseSocket(listeningSocket, irp);
   KeWaitForSingleObject(&compEvent, Executive, KernelMode, FALSE, NULL);
   }
  }
  
  // Listening socket callback which is invoked whenever a new connection arrives.
  NTSTATUS
  WSKAPI 
  WskSampleAcceptEvent(
   _In_  PVOID         SocketContext,
   _In_  ULONG         Flags,
   _In_  PSOCKADDR     LocalAddress,
   _In_  PSOCKADDR     RemoteAddress,
   _In_opt_  PWSK_SOCKET AcceptSocket,
   _Outptr_result_maybenull_ PVOID *AcceptSocketContext,
   _Outptr_result_maybenull_ CONST WSK_CLIENT_CONNECTION_DISPATCH **AcceptSocketDispatch
  )
  {
   PWSKSAMPLE_SOCKET_CONTEXT socketContext = NULL;
   PWSKSAMPLE_SOCKET_CONTEXT listeningSocketContext;
   ULONG i;

UNREFERENCED_PARAMETER(Flags);
   UNREFERENCED_PARAMETER(LocalAddress);
   UNREFERENCED_PARAMETER(RemoteAddress);
    
   listeningSocketContext = (PWSKSAMPLE_SOCKET_CONTEXT)SocketContext;

if(AcceptSocket == NULL) {
   
       //这里解释得很清楚了,当接收到一个空的套接字的时候,我们应该关闭它,WSK子系统只会这样做一次。
    //所以在WskSampleOpStartListen函数里面,我们关闭存在的套接字,再创建一个新的。主要就把刚才的流程再走了一次
    //所以,我们重点看WskSampleOpReceive.
    // If WSK provider makes a WskAcceptEvent callback with NULL 
    // AcceptSocket, this means that the listening socket is no longer
    // functional. The WSK client may handle this situation by trying
    // to create a new listening socket or by restarting the driver, etc.
    // In this sample, we will attempt to close the existing listening
    // socket and create a new one. Note that the WskSampleAcceptEvent
    // callback is guaranteed to be invoked with a NULL AcceptSocket
    // by the WSK subsystem only *once*. So, we can safely use the same
    // operation context that was originally used for enqueueing the first
    // WskSampleStartListen operation on the listening socket. The
    // WskSampleStartListen operation will close the existing listening
    // socket and create a new one.
    WskSampleEnqueueOp(&listeningSocketContext->OpContext[0], 
                           WskSampleOpStartListen);
     return STATUS_REQUEST_NOT_ACCEPTED;
   }

// Allocate socket context for the newly accepted socket.
   socketContext = WskSampleAllocateSocketContext(
                        &WskSampleWorkQueue, WSKSAMPLE_DATA_BUFFER_LENGTH);
    
   if(socketContext == NULL) {
    return STATUS_REQUEST_NOT_ACCEPTED;
   }

socketContext->Socket = AcceptSocket;

DoTraceMessage(TRCINFO, "AcceptEvent: %p", socketContext);
    
   // Enqueue receive operations on the accepted socket. Whenever a receive
   // operation is completed successfully, the received data will be echoed
   // back to the peer via a send operation. Whenever a send operation is 
   // completed, a new receive request will be issued over the connection.
   // This will continue until the connection is closed by the peer.
   for(i = 0; i < WSKSAMPLE_OP_COUNT; i++) {
    _Analysis_assume_(socketContext == socketContext->OpContext[i].SocketContext);
    WskSampleEnqueueOp(&socketContext->OpContext[i], WskSampleOpReceive);
   }
    
   // Since we will not use any callbacks on the accepted socket, we specify no
   // socketContext or callback dispatch table pointer for the accepted socket.
   *AcceptSocketContext = NULL;
   *AcceptSocketDispatch = NULL;

return STATUS_SUCCESS;
  }
  这个函数最难得理解的就是设置了常量指针WSK_PROVIDER_CONNECTION_DISPATCH,但是我们创建的是监听套接字。
  这里重要的就是几个结构体。
  
  typedef struct _WSK_BUF {
   PMDL   Mdl;
   ULONG  Offset;
   SIZE_T Length;
  } WSK_BUF, *PWSK_BUF;
  
  从远程传输地址上接收数据
  NTSTATUS APIENTRY WskReceive(
   _In_     PWSK_SOCKET Socket,
   _In_     PWSK_BUF Buffer,
   _In_     ULONG Flags,
   _Inout_  PIRP Irp
  );

// Operation handler for issuing a receive request on a connected socket
  VOID
  WskSampleOpReceive(
  _In_ PWSKSAMPLE_SOCKET_OP_CONTEXT SocketOpContext
  )
  {
   PWSKSAMPLE_SOCKET_CONTEXT socketContext;

PAGED_CODE();

socketContext = SocketOpContext->SocketContext;

if(socketContext->Closing || socketContext->Disconnecting) {
    // Do not call WskReceive if socket is being disconnected
    // or closed. The operation context will not be used any more.
    DoTraceMessage(TRCINFO, "OpReceive: %p %p SKIP", 
     socketContext, SocketOpContext);
   }
   else 
   {
    WSK_BUF wskbuf;
    CONST WSK_PROVIDER_CONNECTION_DISPATCH *dispatch;

dispatch = socketContext->Socket->Dispatch;

wskbuf.Offset = 0;
    wskbuf.Length = SocketOpContext->BufferLength;
    wskbuf.Mdl = SocketOpContext->DataMdl;

IoReuseIrp(SocketOpContext->Irp, STATUS_UNSUCCESSFUL);
    IoSetCompletionRoutine(SocketOpContext->Irp,
     WskSampleReceiveIrpCompletionRoutine,
     SocketOpContext, TRUE, TRUE, TRUE);

DoTraceMessage(TRCINFO, "OpReceive: %p %p %Iu", 
     socketContext, SocketOpContext, wskbuf.Length);
    
    // No need to check the return status here. The IRP completion
    // routine will take action based on the completion status.
    dispatch->WskReceive(
     socketContext->Socket,
     &wskbuf,
     0,
     SocketOpContext->Irp);
   }
  }
  再看一下,接收的完成例程。
  
  // IRP completion routine for WskReceive requests
  _Use_decl_annotations_
  NTSTATUS
  WskSampleReceiveIrpCompletionRoutine(
   PDEVICE_OBJECT Reserved,
   PIRP Irp,
   PVOID Context
  )
 {
  PWSKSAMPLE_SOCKET_OP_CONTEXT socketOpContext;
  PWSKSAMPLE_SOCKET_CONTEXT socketContext;

UNREFERENCED_PARAMETER(Reserved);

_Analysis_assume_(Context != NULL);

socketOpContext = (PWSKSAMPLE_SOCKET_OP_CONTEXT)Context;
  socketContext = socketOpContext->SocketContext;

DoTraceMessage(TRCINFO, 
   "ReceiveIrpCompletionRoutine: %p %p 0x%lx %Iu", 
   socketContext, socketOpContext, Irp->IoStatus.Status, 
   Irp->IoStatus.Information);

if(!NT_SUCCESS(Irp->IoStatus.Status)) {
   // Receive failed. Enqueue an operation to close the socket
   WskSampleEnqueueOp(socketOpContext, WskSampleOpClose);
  }
  else 
  {
   if(Irp->IoStatus.Information == 0) {
            // Successful receive completion with 0 bytes means the peer
            // has gracefully disconnected its half of the connection.
            // So, we enqueue an operation to disconnect our half.
            WskSampleEnqueueOp(socketOpContext, WskSampleOpDisconnect);
   }
   else {
            // Receive has completed with some data. So, we enqueue an
            // operation to send the data back. Note that the data
            // buffer is attached to the operation context that is being
            // queued. We just need to remember the actual length of
            // data received into the buffer.
            socketOpContext->DataLength = Irp->IoStatus.Information;
            WskSampleEnqueueOp(socketOpContext, WskSampleOpSend);                    
   }
  }
    
   return STATUS_MORE_PROCESSING_REQUIRED;
  }

Windows驱动_WSK驱动之四WSK例程相关推荐

  1. Windows驱动_WSK驱动之二WSK的操作

    现在还真是有点犹豫,有太多的牵绊,毕竟现在这个年纪,不能就这样随随便便,不是说,我不够自信,只是,目前的条件是这样,这里真的是没有太多的机会.创业没有足够的人脉,和行业,客户,目前,天时,地利,人和, ...

  2. Windows驱动_WSK驱动之一WSK是什么

    感觉自己还是不够成熟,才过了一天,就开始全盘否定自己,否定Windows内核编程,否定自己之前所做的一切努力.自己还是太容易受环境的影响了,在这全是Android的时代,在这APP满天飞的年代,在这物 ...

  3. Windows驱动_WSK驱动之三WSK编程注意事项

    今天在天涯上看到一篇帖子,真的是好震撼,原来对于那些人来说,自己已经足够幸福了,我们无法去体会那样的痛苦,只有经历的人才才能真正感受,我都无法想象自己,在那种情况下,需要怎样一种担当啊.虽然,男人真的 ...

  4. Windows文件系统过滤驱动开发教程(4)

    Windows文件系统过滤驱动开发教程 4.设备栈,过滤,文件系统的感知 前边都在介绍文件系统驱动的结构,却还没讲到我们的过滤驱动如何能捕获所有发给文件系统驱动的irp,让我们自己来处理?前面已经解释 ...

  5. Windows 文件系统过滤驱动开发教程 (第二版)

    Windows 文件系统过滤驱动开发教程 (第二版)       楚狂人-2007-上海 (MSN:walled_river@hotmail.com)          -1.  改版序....... ...

  6. Windows文件系统过滤驱动开发教程(0,1,2)

    0. 作者,楚狂人自述 我长期网上为各位项目经理充当"技术实现者"的角色.我感觉Windows文件系统驱动的开发能找到的资料比较少.为了让技术经验不至于遗忘和引起大家交流的兴趣我以 ...

  7. 连接LilyPad之Windows平台的驱动

    连接LilyPad之Windows平台的驱动 LilyPad和其他的Arduino控制板的不同之处是它是为电子织物和可穿戴设计的.那么,它的大小就必须要紧凑.所以,它并没有板载其他大多数板子都具有的U ...

  8. linux下装windows驱动,linux下安装windows xp无线网卡驱动

    //如果不采取默认的安装路径,则可以用.configure --prefix="/etc/local"来指定安装目录(此目录是自建) . (5)查看安装后的版本ndiswrappe ...

  9. Windows CE设备驱动开发之电源管理

    4.7电源管理 电源管理模块管理设备电源,从而全面改进操作系统的电源使用效率:它所有设备的电源使用,同时能与不支持电源管理的应用程序及驱动程序共存. 使用电源管理可以有效的减少目标设备的电源消耗,同时 ...

最新文章

  1. boot数据加解密 spring_springboot项目使用druid对数据库密码的加解密
  2. jQuery函数 - 左右抖动效果,用于提示
  3. 应用程序挂起、复原与终止— IOS开发
  4. python中有很多包管理工具那中不是_Python中的包管理工具PIP,pip
  5. TypeScript学习(四):对象的定义及使用
  6. h5微信f分享链接给对方获取对方手机号_怎么加回微信删除的人?偷偷恢复,亲测有效!...
  7. python语言的单行注释以单引号开头_知到智慧树大数据分析的python基础(山东联盟)(1)答案章节期末答案...
  8. dell笔记本驱动安装失败_W10系统声卡驱动程序安装失败的原因及解决方法
  9. 【面试概率】52张扑克牌,红桃A和黑桃A同时被一个人拿到的概率
  10. 去中心化自治组织DAO——Steemit社区介绍
  11. 用altium designer 如何按1:1比例输出PCB图?
  12. python模拟登录URP教务系统评教
  13. hypermesh生成MNF柔性体
  14. 第一节 模式识别的基本概念
  15. Postgresql默认用户名与密码
  16. 淘宝、一淘、淘宝商城 - 马云内部邮件谈分拆
  17. 自制微信机器人:群发消息、自动接收好友
  18. 高端大气上档次的管理后台模板
  19. HDLBits: 在线学习 SystemVerilog(十七)-Problem 106-114(移位寄存器)
  20. 正在同步文件夹收件箱中的服务器更改,Outlook2016 收件箱同步项目数不一致问题...

热门文章

  1. RDSDRDSPolarDBPolarDB-X的区别
  2. VxWorks操作系统shell命令与调试方法总结
  3. 【2013年度CSDN博客之星】评奖:全文五言句,俺也拉个票
  4. java new object_java 正则
  5. 酷家乐母公司群核科技冲刺美股上市:2020年亏损约3亿元,曾因违规多次被通报
  6. S19文件格式详解(总结)以及与hex文件的互转
  7. 51Nod基础组(Python)
  8. mac 特殊符号如何打
  9. 中职计算机公开课说课稿,校内公开课说课稿
  10. Data Wrangling