最近看了好多文章,也写了好多博客,但是,最近少了很多实践的关系,这一部分是项目的问题,其实大部分还是自己的问题,今天,竟然,知道,随便修改一点微软的例子还可以赚钱,之前自己还真是没有想到的。后续,自己要好好的理解下例子,好好的实践一下。今天,让我知道,我做这么多,看这么多,都是有用的,有帮助的。所以,自己还是要立足这一块,好好的往里面深挖下去。
今天趁热打铁,来看一下微软的WDK中的有关WSK的例子,后续,主要的精力还是要放在这些例子的实现和理解上。闲话不说,首先来看驱动的入口函数。
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
WSK_CLIENT_NPI wskClientNpi;
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;