首先,要控制windows services,是比较容易的事情,一堆现成的例子。
SYNtService 就是一个很好的例子。
要控制一个窗口退出是十分容易的事情,PostMessage就可以解决问题。
要控制console退出,也有很多现成的方法。比较通用的方法:
1、TerminateProcess
最原始、最暴力的强制console退出的方法。console进程毫无还手之力就over了。但我希望console在推出之前至少能处理一下“后事”。
2、signal / raise
原来windows也有signal,不过kill换成了raise。但是相对Unix系列的signal功能就差很远了。而且有一个麻烦的地方就是raise只能对本console生效,而不能对指定的process。
可以在signal里面指定一个call back函数,在收到SIGINT/SIGTERM之类的时候,处理一下事情,然后通知各个线程结束。
既然不能raise其他进程,是否这个功能就不能用了呢?其实可以考虑一下CreateRemoteThread,然后在别人的进程里面raise……
3、SetConsoleCtrlHandler / GenerateConsoleCtrlEvent
这个是console专门用来处理Ctrl-C/Ctrl-Break/以及windows关机事件等的处理方法。比signal更强大。而且说明中写了,可以对其他的进程进行处理(还可以对进程组处理)。用网上的话说就是:很女子,很弓虽!
但是要注意,如果要对其创建的子进程进行处理的时候,创建子进程必须要使用CREATE_NEW_PROCESS_GROUP标志。另外一点,文档写的比较隐晦的就是,进程必须要有console窗口。否则,调用GenerateConsoleCtrlEvent会返回6,说ERROR_INVALID_HANDLE。
问题来了,Service本身是没有console窗口的,Service建立的子进程就必须要自带窗口了。但是一般为了美观,Service启动的进程都不想带有窗口。那就变成了子进程没有console窗口,GenerateConsoleCtrlEvent失效了。
在网上查了很多资料(怎么没看到很黄很暴力呢???),其中在
Louis K. Thomas
<louiNØSP@Msth@hotmÑOSP@Mail.coNÕSP@Mm> 的 SendSignal 提到一种做法,就是先获得kernel32!CtrlRoutine的入口,然后通过CreateRemoteThread的方法,让远程的console来执行kernel32!CtrlRoutine。但这种方法有个问题,在获得kernel32!CtrlRoutine的时候,也是使用GenerateConsoleCtrlEvent来获得。但是Service自己本身没有console窗口,一调用GenerateConsoleCtrlEvent也是出错。
而另一篇
google讨论组 文章,里面提到原来可以先AllocConsole、然后GenerateConsoleCtrlEvent、然后FreeConsole……于是解决方案就变成:
Service里面:
先AllocConsole
然后利用GenerateConsoleCtrlEvent获得kernel32!CtrlRoutine
然后FreeConsole
当需要结束进程的时候,就调用CreateRemoteThread,把kernel32!CtrlRoutine的代码注入到子process中执行
这样大家都看不到console窗口(service里面AllocConsole很快,看不到窗口出来,甚至怀疑根本就有没有窗口出来),同时又能通知子进程优雅地退出。
存在问题:如果我的机器作为服务器启动,即没有进入登录状态,不知道这样启动的Service会不会有问题呢??暂时还没有时间测试。