W2K信号(Signals)的设备驱动
Unix下的信号提供了一个简单的IPC机制,也就是当进程收到一个信号后会异步(asynchronous) 地调用你的信号处理函数(也叫做句柄),不管你的代码是否已经处在执行的过程之中。 而在Windows 2000(译者注:版本高于W2k的Windows平台)下就需要用到一个设备驱动,以便你能使用异步过程调用(asynchronous procedure calls , 简称APCs或者APC) 来达成同样的效果.
By Panagiotis E.
August 01, 2001
URL:http://www.ddj.com/windows/184416344
翻译:Lymons (lymons@gmail.com)
在Windows和基于Unix的操作系统之间的一个重要的不同就是对程序员自定义的信号处理函数的支持。尽管标准C库已经为信号处理[2]提供了基本的支持,但这些函数对于那些想主要依靠信号来实现进程间通信(IPC)的程序员来讲还不够。 实际上,在Windows的上下文中缺乏这样的一个机制,导致了进程(线程)间的异步通信的实现困难,另外还需要运用一些特殊的数据结构,如事件,而且还需要创建一个进程,专门用于不断地去轮巡一些条件[6],查看其状态是否发生改变。 在本文中,我将向大家介绍SignalsLib库,它被用于在Win32平台下进行信号处理。这个库的核心是一个设备驱动,该驱动提供了一个给目标进程发送信号并让其异步执行信号处理函数的机制,即使是目标进程并不处在消息等待的状态。
跟大多文章一样,本文为信号处理提供了一份概要性的说明,
1. 信号和基本机制
2. 库和驱动的设计与实现
3. 该机制下的性能测量
4. 面向程序员的API接口
5. 以及相关的扩展和可能用法。
信号的概述
信号是把异步事件通知给进程的一个机制。它的基本思想是为每一个独特的信号关联上一个整数代码,并且任何进程都能给任何一个信号注册一个处理函数(回调函数)(通过指定该信号的整数代码). 当一个进程发送一个特定的信号给其他的进程(该进程已经为这个信号注册了信号处理函数)的时候,这个目标进程当前正在做的操作将会被中断,转而去执行这个被注册的信号处理函数。
信号机制有点儿类似于,在一个信号中断中进行一个中断处理,不管目标进程是否正在执行某一段代码。跟中断处理函数一样,信号处理函数需要认真仔细地编码 --- 普通的代码一定不能访问和信号处理函数中一样的数据,除非它们都是用同步原语(synchronization primitives)来避免相互之间的破坏。信号提供了简单和方便的进程间通信。通常传统的应用在下面的情况:
1. 通知一个服务它将轮转(rotate)它的日志文件;
2. 通知一个父进程子进程已经完成初始化并已经准备开始实行下面的工作;
3. 通知一个进程它将暂时暂停自己的操作;
4. 通知一个进程它将在关机时尽可能快地执行清除操作
5. 等等。
每个信号都被关连上一个将被执行的动作,通过内核调度来使接收到这个信号的进程来执行它的行为。对于大多数的信号,缺省的动作时终止进程的运行,尽管一个进程能被要求执行一些来自系统选择的动作。这些可能的可选动作无非就是:
1. 忽略这个信号。 在这种情况下,进程将不会接收到这个信号的通知。
2. 恢复缺省的信号动作。
3. 执行一个指定的信号处理函数。这种场合下,当希望一个指定的信号到达时,想要某个进程去执行一些定制动作,就可以去注册这个定制函数。当关联的信号发生时就可以异步地调用它了。在这个信号处理函数返回之后,将在被中断的代码的地方继续执行原来的操作。
Windows支持信号
Win32为信号支持提供了一个十分特殊的函数 SetConsoleCtrlHandler(). 这个函数让一个控制台的程序能捕获很多的系统自身的信号(如,用户按下了Ctrl-C, 用户注销, 等)。 但它没有提供任何的程序员自定义的信号,也没有提供任何的进程间通信 — 这就很严格的意味着操作系统只能把很少的一些特定事件通知给一个进程。
Windwos提供的仅有的与信号机制类似的是异常处理这种机制。然而,标准C要求给著名的signal()/raise() 的Unix 函数以及一些受约束的信号[2]提供支持. signal()给指定的信号设定被调用的信号处理函数。raise() 是给当前的进程发送特定的信号,调用为这个信号注册的处理函数,或者与该信号关联的缺省动作。 另外,这些信号是不可扩展的,并且它们只能在给定的进程内部进行传播。 (标准C并没有为向其他进程发送信号而定义标准函数)
作为信号的替代品,Windows支持异步过程调用,简称为APCs (Asynchronous Procedure Calls). 一个APC 就是一个内核定义的控制对象,代表着一个过程/函数可以被异步的调用. APCs 有着下列的几个特征[7]:
1. 一个 APC 总是运行在一个指定的线程的上下文中.
2. 一个 APC 运行在 OS 的预设时间内.
3. APCs 能够抢占当前正在运行的线程.
4. APC 例程也能被他们自己抢占.
在内核中APCs 有三个不同的类型[2,3]:
用户模式(User-mode) APCs. 用户模式 APCs 默认是被禁止的; 也就是对于用户模式的线程它们虽然被放置到队列进行排队,但它们并不会被执行,除了在程序中一些明确定义的点上。具体的就是,它们能够在下面两种情况被执行:
1. 当一个应用调用等待服务(wait service)并且触发了告警发生机制的时候;
2. 或者调用告警测试服务(test-alert service)的时候.
常态内核模式(Normal kernel-mode) APCs. 除了默认情况下是可以被执行的之外,其他的它们更像用户模式的APCs。也就是,当线程已经开始执行一个内核模式的APC,或者驻留在一个临界区代码之中时,是不能被执行的。除此之外,它们都是可以被执行的。
特殊内核模式(Special kernel-mode) APCs. 它们是不能被阻塞的, 除了线程运行在IRQL (interrupt request level)唤起的状态下。 特殊内核模式的APCs 在内核态中能在IRQL的 APC_LEVEL级别下运行。它们被用来强制让一个线程在它的上下文中去执行一个过程。 特殊内核模式的APC 能够抢占常态内核模式APC的执行。
Win32 API [4] 提供了 QueueUserAPC()这个函数, 它允许一个应用把线程的APC对象放置到队列中。正在排队中的APC是让一个指定的线程去调用APC函数的请求。当用户模式的APC被放到队列中时,线程则不能直接去调用这个APC函数,除非它是处在一个告警使能的状态。 不幸的是,一个线程只能使用下列的 Win32 API函数的之一才能使自己进入到告警使能的状态: SleepEx(), SignalObjectAndWait(), WaitForSingleObjectEx(), WaitForMultipleObjectEx(), 或 MsgWaitForMultipleObjectsEx().
在内核态[1], 程序员可以使用KeInitializeApc()来初始化一个APC对象, 定义目标线程,以及一个内核态和用户态的回调函数, 以及APC (内核 或者 用户)类型, 最后是传递给这两个函数的参数。接着, 这个目标线程的 APC 被排队(KeInsertQueueApc()) 到队列中,并且在线程没有进入到告警使能的状态时也能够被执行。
SignalsLib 函数库的接口
这个函数库给支持信号处理提供了适宜的接口,必要的数据结构和机制。在构建过程的期间,用户能够给每个线程定义选项以及全局信号处理函数表的用途。
signals.h (Listing 1) 定义了可用的信号,信号的整形代码是从零开始直到MAX_SIGNALS-1. 在这个头文件中为信号函数库中的两个如下的函数声明了接口。
1. SetSignalHandler() 给指定的信号设置一个函数(handler). 该函数如果执行失败则返回0, 成功则返回非零值。
2. SendSignalToThread() 给指定的线程发送一个信号. 你必须给想接收这个信号的线程指定该线程的句柄,以及想要发送的信号(整形代码)。该函数成功则返回0,否则返回非零。
testapp.c (Listing 2) 描述了如何去使用这个信号函数库。 这个应用创建了一个设定信号和对应的信号处理函数的线程,以及发送这个信号给子线程的主线程,最终导致这个已经安装的信号处理函数被执行。当然,这个驱动必须事先被安装到系统里并且已被装载到内存中,否则当函数库的DLLMain()函数被执行的时候,会输出相应的错误消息。
设计和实现
SignalsLib 库由DLL文件和内核态的设备驱动组成。这个 DLL 给应用程序提供了一个用户态的接口, 当我们想要让目标线程排队一个内核模式的APC并去调用关联的内核态的函数的时候,就需要用到这个设备驱动。应用程序仅仅是简单的调用SetSignalHandler() 和SendSignalToThread()这两个函数就可以, 然而 — 这个 DLL 隐藏了所有的与设备驱动进行通信的细节。
SetSignalHandler() 函数很简单 — 它仅仅是存储了在信号处理函数的全局数组中的相应位置,也就是一个函数指针。 当一个信号确实是被触发的时候,内部函数SignalsDriverRoutine()将会被调用,并且访问这个全局数组来决定调用哪个信号处理函数。这两个函数在signals.c (Listing 3)有定义.
SendSignalToThread() 函数是DLL与设备驱动进行通信的地方。 DllMain() 在DLL第一次被载入的时候会获得一个设备驱动的句柄, 并且在DLL被卸载的时候会释放这个句柄。 SendSignalToThread() 函数在调用DeviceIoControl()函数(该函数是传递一个SIGINFO结构体给这个设备驱动)的时候会使用这个句柄:
typedef struct _SIGINFO
{
HANDLE hThread; /**//* target thread */
ULONG SigNo; /**//* signal number */
ULONG SigFunc; /**//* address of DriverRoutine */
} SIGINFO, *PSIGINFO;
注意这个 SigFunc 不是一个单独信号处理函数的地址,而是SignalsDriverRoutine()函数的地址,该函数的功能是查询和调用DLL中的正确的信号处理函数。
当SendSignalToThread() 函数传递这个信息给DeviceIoControl()的时候, 它将导致这个驱动的中断服务程序被调用。该驱动的主要源代码都在sigdrv.c (Listing 4)文件里面. 然后驱动中断服务例程会调用SigDriverSendTheSignal()函数来负责为目标线程来排队一个相应的内核模式的APC. SigDriverSendTheSignal() 中有一个指向这个目标线程的ETHREAD 的数据结构体 [2]的指针. 然后调用 KeInitializeApc() 函数去初始化一个内核模式的APC并且调用KeInsertQueueApc() 来把目标线程的 APC 插入到队列中。
这个被放入到队列中的 APC 包含一个指向sigdrv.c (Listing 4)文件中的一个函数UserApcCallBack()的指针. 这个函数将会在用户态中被调用并且传递SIGINFO 结构体. UserApcCallBack() 使用 SIGINFO 中的信息来调用DLL 函数 SignalsDriverRoutine(), 而它就是那个查询且调用与指定信号关联的信号处理函数的函数。
性能评估
编程时到底是选择常态还是特殊内核模式的APC,是跟你所期望的功能有关,而跟性能无关。 如果你认为你的信号处理函数能被其他的被触发的信号所抢占是一件重要的事情的话,那么你应该选择使用特殊内核模式的APC而不是常态内核模式APCs.
这个APC 机制执行的相当棒;一旦目标线程被调度后这个信号处理函数就会很快的被调用,一般情况下就是几微秒之间的事儿。其中值得注意的一个重要的事情就是,内核模式APC能立刻完成信号的传送,这与系统载入的(或者说在系统中运行的)线程数量无关。他是通过改变线程的优先级来减少这个响应时间。例如, SendSignalToThread() 函数能够提升目标线程的优先级。
结论
主要是为了在Win32应用中能够使能相同线程内部或者不同线程间的异步通信,我实现了这个用户自定义信号的基本机制。与Unix系统调用signal() 和kill() 相似的,最终的,通过DLL和设备驱动协同工作也能提供这两个重要的信号处理的接口,并且也支持了相似的SIGUSR1 和SIGUSR2这两个信号.
作为这个库的扩展将来很可能会实现一些其他的Unix信号,如SIGSTOP, SIGCONT, 和 SIGTERM, 并且支持POSIX 标准。 也愿意把这个机制集成到标准C库中去。这个库也协助实现了POSIX函数pthread_kill() ,这就使得应用程序在用户模式下(或者是从内核到用户模式)的POSIX线程间需要通知机制时,就让开发工作变的很容易, 仅仅是让设备驱动知道驱动例程的函数地址即可。 尽管这个POSIX 标准为应用程序的所有的线程定义了全局信号处理函数,这个库也能容易的为每个线程提供信号处理的支持,通过使用线程的局部存储数据;为了简单,在当前这个版本中没有这么做。
为了发表这篇文章我尽量让代码编写的简洁, 代码的实现是假设所有参与进来的进程都共享同一个signals.dll的实例(也就是它们之间是父子进程的关系). 更明确的说, SendSignalToThread() 函数总是传递SignalsDriverRoutine() 的函数地址(调用进程的上下文中)给设备驱动,但是设备驱动则会尝试使用目标进程(很可能是另外一个进程)上下文中的那个地址. 如果目标进程已经把signals.dll 载入到了一个不同的地址而不是调用进程载入进来的地址,这将会导致一个灾难发生。如果一个特别的进程不能把signals.dll 载入到一个缺省的地址,你可以选择另外一个地址,直到你找到与已经载入的DLL地址没有冲突的另外一个地址。 你能更优雅地解决这个问题通过修订这个接口以便让设备驱动能够有足够的信息来定位指定线程中的正确的回调函数的地址。
最后,另外一个基于用户模式APC的实现在这里没有描述。不过SendSignalToThread() 可以调用 Win32的 QueueUserAPC() 函数然后通过内核模式的设备驱动,来设置目标线程成告警使能状态。 你可以对与ETHREAD数据结构[2]的基地址相偏移0x4a 个字节的一个内存地址进行设定来实现的。
References
[1] E. N. Dekker and J. M. Newcomer. Developing Windows NT Device Drivers: A Programmer’s Handbook (Addison-Wesley, 1999).
[2] Microsoft Corporation. Microsoft Developer Network Library, msdn.microsoft.com/library.
[3] D. A. Solomon. Inside Windows NT, Second Edition (Microsoft Press, 1998).
[4] J. Ritcher. Advanced Windows: The Professional Developer’s Guide to the Win32 API for Windows NT 4.0 and Windows 95 (Microsoft Press, 1995).
[5] www.cmkrnl.com/arc-userapc.html
[6] www.microsoft.com/msj/0799/nerd/nerd0709.html
[7] www.osr.com/insider/1998/apc.html
[8] Microsoft Visual Studio\VC98\CRT\SRC\WINSIG.C
Panagiotis Hadjidoukas is a postgraduate student at High Performance Information Systems Laboratory, Department of Computer Engineering & Informatics, at the University of Patras in Greece. You can reach him by email at peh@hpclab.ceid.upatras.gr or at the web page of his laboratory at http://www.hpclab.ceid.upatras.gr.
Listing 1: signals.h — Interface to signal library
/**//* Number of supported signals */
#define MAX_SIGNALS 4
/**//* Signal names */
#define SIGNAL0 0
#define SIGNAL1 1
#define SIGNAL2 2
#define SIGNAL3 3
#ifndef SIGNALSLIBAPI
#define SIGNALSLIBAPI __declspec(dllimport)
#endif
/**//* Set a signal handler */
SIGNALSLIBAPI DWORD SetSignalHandler(DWORD, PVOID);
/**//* Send a signal to a thread */
SIGNALSLIBAPI DWORD SendSignalToThread(HANDLE, DWORD);
/**//* End of File */
Listing 2: testapp.c — Source for signal demonstration program
#define STRICT 1
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "..\dll\signals.h"
/**//* Global variables to avoid any optimization by the compiler */
long sum1 = 0, sum2 = 0;
volatile unsigned long WaitFlag = 0;
VOID __stdcall fSignal0(VOID)
{
printf("Thread (%ld):Inside the handler \
of signal SIGNAL0!!!\n", GetCurrentThreadId());
return;
}
DWORD WINAPI ThreadRoutine(LPVOID Param)
{
long i;
/**//* Set a hanndler for SIGNAL0 */
SetSignalHandler(SIGNAL0, fSignal0);
/**//* Do some stuff. */
for (i = 0; i < 10000000; i++) sum1++;
/**//* Wait until the main thread sets the flag */
while (WaitFlag == 0);
return 0;
}
HANDLE hThread;
DWORD ThreadId;
int main()
{
ULONG i = 0;
DWORD Data = 1; /**//* Not actually used */
/**//* Create the target thread */
hThread = CreateThread(NULL,0,ThreadRoutine,&Data,0,&ThreadId);
/**//* Let the target thread to run for a while */
Sleep(1000);
/**//* Send a signal to the target thread */
printf("Thread (%ld): Sends a signal to the \
target thread\n", GetCurrentThreadId());
SendSignalToThread(hThread, SIGNAL0);
/**//* Do some stuff and wait for a while */
for (i = 0; i < 10000000; i++) sum2++;
Sleep(1000);
/**//* Set the flag. The handler must have been executed by now */
WaitFlag = 1;
/**//* Wait for the thread's termination */
WaitForSingleObject(hThread, INFINITE);
return 0;
}
/**//* End of File */
Listing 3: signals.c — Source for signal DLL
#define STRICT 1
#include <windows.h>
#include <stdio.h>
#include <winioctl.h>
#include <string.h>
#include <stdlib.h>
#include "..\driver\sigdrv.h"
#define SIGNALSLIBAPI __declspec(dllexport)
#include "signals.h"
/**//* Global array of signal handler */
VOID (__stdcall *functable[MAX_SIGNALS])(VOID);
/**//* Handle to the device driver */
HANDLE hDevice;
/**//* Set a signal thread */
DWORD SetSignalHandler(DWORD SignalNumber, PVOID f)
{
if (SignalNumber < MAX_SIGNALS)
{
functable[SignalNumber] = f;
return 0;
}
return 1;
}
/**//* Call the appropriate signal handler */
DWORD WINAPI SignalsDriverRoutine(DWORD SignalNumber)
{
if (functable[SignalNumber] != NULL)
(functable[SignalNumber])();
else
printf("NULL signal function!\n");
return 0;
}
/**//* this information is sent to the device driver */
typedef struct _SIGINFO
{
HANDLE hThread; /**//* target thread */
ULONG SigNo; /**//* signal number */
ULONG SigFunc; /**//* address of DriverRoutine */
} SIGINFO, *PSIGINFO;
SIGINFO siginfo;
/**//* Send a signal to the targer thread */
DWORD SendSignalToThread(HANDLE hThread, DWORD SignalNumber)
{
DWORD cbReturned;
/**//* Initialize a SIGINFO structure */
siginfo.hThread = hThread;
siginfo.SigNo = SignalNumber;
siginfo.SigFunc = (unsigned long)SignalsDriverRoutine;
/**//* Send the information to the driver */
if (!DeviceIoControl (hDevice,
(DWORD)IOCTL_SIGDRV_SEND_SIGNAL,
(PSIGINFO) &siginfo,
sizeof(SIGINFO),
NULL,
0,
&cbReturned,
0
) )
{
return 1;
}
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD fdwReason,
LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
if ((hDevice =
CreateFile(
"\\\\.\\Global\\SIGDRV",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
)) == INVALID_HANDLE_VALUE)
{
printf ("Can't get a handle to the driver\n");
return FALSE;
}
break;
case DLL_PROCESS_DETACH:
CloseHandle(hDevice);
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
/**//* End of File */
Listing 4: sigdrv.c — Kernel-mode driver for signal library
#define STRICT 1
#include "ntddk.h"
#include "string.h"
#include "sigdrv.h"
#define SIGDRV_DEVICE_NAME_U L"\\Device\\Sigdrv"
#define SIGDRV_DOS_DEVICE_NAME_U L"\\DosDevices\\SIGDRV"
// Debugging macros
#ifdef DBG
#define SigDrvKdPrint(_x_) \
DbgPrint("SigDrv.sys: ");\
DbgPrint _x_;
#else
#define SigDrvKdPrint(_x_)
#endif
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING registryPath);
VOID SigDrvUnload(
IN PDRIVER_OBJECT DriverObject);
NTSTATUS SigDrvDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp);
NTSTATUS SigDrvSendTheSignal(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PVOID ioBuffer,
IN ULONG inputBufferLength,
IN ULONG outputBufferLength);
void KeInitializeApc(
PKAPC Apc,
PKTHREAD Thread,
CCHAR ApcStateIndex,
PKKERNEL_ROUTINE KernelRoutine,
PKRUNDOWN_ROUTINE RundownRoutine,
PKNORMAL_ROUTINE NormalRoutine,
KPROCESSOR_MODE ApcMode,
PVOID NormalContext);
void KeInsertQueueApc(
PKAPC Apc,
PVOID SystemArgument1,
PVOID SystemArgument2,
UCHAR unknown);
// Information the driver receives from user mode
typedef struct _SIGINFO
{
HANDLE hThread; // handle of targer thread
ULONG SigNo; // which signal
ULONG SigFunc; // signals' driver-routine of the dll
} SIGINFO, *PSIGINFO;
void KernelApcCallBack(
PKAPC Apc,
PKNORMAL_ROUTINE NormalRoutine,
PVOID NormalContext,
PVOID SystemArgument1,
PVOID SystemArgument2)
{
ExFreePool(Apc); // just free the kernel memory
return;
}
void UserApcCallBack(PVOID arg1, PVOID arg2, PVOID arg3)
{
PSIGINFO psiginfo = (PSIGINFO) arg3;
ULONG (*SignalDriverRoutine)(ULONG);
// take the user mode address of the function
SignalDriverRoutine = (unsigned long (__stdcall *)
(unsigned long)) psiginfo->SigFunc;
// call the driver-routine
SignalDriverRoutine(psiginfo->SigNo);
return;
}
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
PDEVICE_OBJECT deviceObject=NULL;
NTSTATUS ntStatus;
WCHAR deviceNameBuffer[]=SIGDRV_DEVICE_NAME_U;
UNICODE_STRING deviceNameUnicodeString;
WCHAR deviceLinkBuffer[]=SIGDRV_DOS_DEVICE_NAME_U;
UNICODE_STRING deviceLinkUnicodeString;
RtlInitUnicodeString (&deviceNameUnicodeString,
deviceNameBuffer);
ntStatus = IoCreateDevice (
DriverObject,0,&deviceNameUnicodeString,
FILE_DEVICE_SIGDRV,0,FALSE,&deviceObject);
if (!NT_SUCCESS(ntStatus))
{
SigDrvKdPrint(("IoCreateDevice failed:%x\n", ntStatus));
return ntStatus;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SigDrvDispatch;
DriverObject->DriverUnload = SigDrvUnload;
RtlInitUnicodeString (&deviceLinkUnicodeString,
deviceLinkBuffer);
ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString,
&deviceNameUnicodeString);
if (!NT_SUCCESS(ntStatus))
{
SigDrvKdPrint (("IoCreateSymbolicLink failed\n"));
IoDeleteDevice (deviceObject);
}
return ntStatus;
}
NTSTATUS SigDrvDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION irpStack;
PVOID ioBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG ioControlCode;
NTSTATUS ntStatus;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
irpStack = IoGetCurrentIrpStackLocation(Irp);
ioBuffer = Irp->AssociatedIrp.SystemBuffer;
inputBufferLength =
irpStack->Parameters.DeviceIoControl.InputBufferLength;
outputBufferLength =
irpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
SigDrvKdPrint (("IRP_MJ_CREATE\n"));
break;
case IRP_MJ_CLOSE:
SigDrvKdPrint (("IRP_MJ_CLOSE\n"));
break;
case IRP_MJ_DEVICE_CONTROL:
ioControlCode =
irpStack->Parameters.DeviceIoControl.IoControlCode;
switch (ioControlCode)
{
case IOCTL_SIGDRV_SEND_SIGNAL:
Irp->IoStatus.Status = SigDrvSendTheSignal(
DeviceObject,
ioBuffer,
inputBufferLength,
outputBufferLength);
if (NT_SUCCESS(Irp->IoStatus.Status))
{
Irp->IoStatus.Information = sizeof(PVOID);
SigDrvKdPrint(("Signal was sent\n"));
}
else
{
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
SigDrvKdPrint(("Signal failed to be sent\n"));
}
break;
default:
SigDrvKdPrint (("unknown IRP_MJ_DEVICE_CONTROL\n"));
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
}
break;
}
ntStatus = Irp->IoStatus.Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
VOID SigDrvUnload(IN PDRIVER_OBJECT DriverObject)
{
WCHAR deviceLinkBuffer[] = SIGDRV_DOS_DEVICE_NAME_U;
UNICODE_STRING deviceLinkUnicodeString;
RtlInitUnicodeString (&deviceLinkUnicodeString,
deviceLinkBuffer);
IoDeleteSymbolicLink (&deviceLinkUnicodeString);
IoDeleteDevice (DriverObject->DeviceObject);
return;
}
NTSTATUS SigDrvSendTheSignal(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PVOID IoBuffer,
IN ULONG InputBufferLength,
IN ULONG OutputBufferLength)
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PVOID virtualAddress;
SIGINFO *psiginfo = (PSIGINFO) IoBuffer;
PETHREAD uThread = NULL;
PKAPC kApc;
// take a pointer to the kernel thread structure
ntStatus = ObReferenceObjectByHandle(
psiginfo->hThread, THREAD_ALL_ACCESS,
NULL, KernelMode, &uThread, NULL);
if (NT_ERROR(ntStatus)) {
SigDrvKdPrint (("ObReferenceObjectByHandle Failed\n"));
return ntStatus;
}
// Allocate an KAPC structure from NonPagedPool
kApc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
KeInitializeApc(kApc,
(PKTHREAD) uThread, 0,
(PKKERNEL_ROUTINE) &KernelApcCallBack, 0,
(PKNORMAL_ROUTINE) &UserApcCallBack,
KernelMode, (PVOID) 0);
KeInsertQueueApc (kApc, (PVOID) (ULONG) 10, (PVOID) psiginfo, 0);
ObDereferenceObject((PVOID) uThread);
return ntStatus;
}
/**//* End of File */