Windows驱动_WSK驱动之四WSK例程
最近看了好多文章,也写了好多博客,但是,最近少了很多实践的关系,这一部分是项目的问题,其实大部分还是自己的问题,今天,竟然,知道,随便修改一点微软的例子还可以赚钱,之前自己还真是没有想到的。后续,自己要好好的理解下例子,好好的实践一下。今天,让我知道,我做这么多,看这么多,都是有用的,有帮助的。所以,自己还是要立足这一块,好好的往里面深挖下去。
今天趁热打铁,来看一下微软的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例程相关推荐
- Windows驱动_WSK驱动之二WSK的操作
现在还真是有点犹豫,有太多的牵绊,毕竟现在这个年纪,不能就这样随随便便,不是说,我不够自信,只是,目前的条件是这样,这里真的是没有太多的机会.创业没有足够的人脉,和行业,客户,目前,天时,地利,人和, ...
- Windows驱动_WSK驱动之一WSK是什么
感觉自己还是不够成熟,才过了一天,就开始全盘否定自己,否定Windows内核编程,否定自己之前所做的一切努力.自己还是太容易受环境的影响了,在这全是Android的时代,在这APP满天飞的年代,在这物 ...
- Windows驱动_WSK驱动之三WSK编程注意事项
今天在天涯上看到一篇帖子,真的是好震撼,原来对于那些人来说,自己已经足够幸福了,我们无法去体会那样的痛苦,只有经历的人才才能真正感受,我都无法想象自己,在那种情况下,需要怎样一种担当啊.虽然,男人真的 ...
- Windows文件系统过滤驱动开发教程(4)
Windows文件系统过滤驱动开发教程 4.设备栈,过滤,文件系统的感知 前边都在介绍文件系统驱动的结构,却还没讲到我们的过滤驱动如何能捕获所有发给文件系统驱动的irp,让我们自己来处理?前面已经解释 ...
- Windows 文件系统过滤驱动开发教程 (第二版)
Windows 文件系统过滤驱动开发教程 (第二版) 楚狂人-2007-上海 (MSN:walled_river@hotmail.com) -1. 改版序....... ...
- Windows文件系统过滤驱动开发教程(0,1,2)
0. 作者,楚狂人自述 我长期网上为各位项目经理充当"技术实现者"的角色.我感觉Windows文件系统驱动的开发能找到的资料比较少.为了让技术经验不至于遗忘和引起大家交流的兴趣我以 ...
- 连接LilyPad之Windows平台的驱动
连接LilyPad之Windows平台的驱动 LilyPad和其他的Arduino控制板的不同之处是它是为电子织物和可穿戴设计的.那么,它的大小就必须要紧凑.所以,它并没有板载其他大多数板子都具有的U ...
- linux下装windows驱动,linux下安装windows xp无线网卡驱动
//如果不采取默认的安装路径,则可以用.configure --prefix="/etc/local"来指定安装目录(此目录是自建) . (5)查看安装后的版本ndiswrappe ...
- Windows CE设备驱动开发之电源管理
4.7电源管理 电源管理模块管理设备电源,从而全面改进操作系统的电源使用效率:它所有设备的电源使用,同时能与不支持电源管理的应用程序及驱动程序共存. 使用电源管理可以有效的减少目标设备的电源消耗,同时 ...
最新文章
- boot数据加解密 spring_springboot项目使用druid对数据库密码的加解密
- jQuery函数 - 左右抖动效果,用于提示
- 应用程序挂起、复原与终止— IOS开发
- python中有很多包管理工具那中不是_Python中的包管理工具PIP,pip
- TypeScript学习(四):对象的定义及使用
- h5微信f分享链接给对方获取对方手机号_怎么加回微信删除的人?偷偷恢复,亲测有效!...
- python语言的单行注释以单引号开头_知到智慧树大数据分析的python基础(山东联盟)(1)答案章节期末答案...
- dell笔记本驱动安装失败_W10系统声卡驱动程序安装失败的原因及解决方法
- 【面试概率】52张扑克牌,红桃A和黑桃A同时被一个人拿到的概率
- 去中心化自治组织DAO——Steemit社区介绍
- 用altium designer 如何按1:1比例输出PCB图?
- python模拟登录URP教务系统评教
- hypermesh生成MNF柔性体
- 第一节 模式识别的基本概念
- Postgresql默认用户名与密码
- 淘宝、一淘、淘宝商城 - 马云内部邮件谈分拆
- 自制微信机器人:群发消息、自动接收好友
- 高端大气上档次的管理后台模板
- HDLBits: 在线学习 SystemVerilog(十七)-Problem 106-114(移位寄存器)
- 正在同步文件夹收件箱中的服务器更改,Outlook2016 收件箱同步项目数不一致问题...