之前写的后台的控制程序,我采用的都是用VC或者是C#写完之后然后采用AlwaysUp或者是nssm把自己写的后台程序再给做成一个Windows的服务程序。但后来想了想为什么不自己直接写一个Windows的服务程序呢。所以就开始了写服务程序的历程。
首先大致的说一下C#语言写一个Windwos服务程序的方法。说实话C#想写一个Windows服务程序相对的要简单的多。因为C#已经都给准备好了。就在创建项目的选择Windows服务项目就可以了。然后再添加一个服务安装的组件,改一下相关的配置,如:服务名、启动类型、启动权限或者叫启动时的用户。这些信息就可以了。在写代码的时候只要在开始的里面写上服务启动时需要做的工作,在结束里面写上服务停止时需要收尾的工作就大功告成了。
下面主要是说一下采用VC写Windows服务程序。这个我个人觉得相对来说有几个坑需要避。
第一个坑就是如果你从网上查的话都会说用VC写Windows服务程序的入口主函数是ServiceMain()这个函数。而实际并不是,实际上的程序入口还是从main()这个函数开始的。只不过在这个函数中我们不能做太多的事情,必须要尽快的调用StartServiceCtrlDispatcher()这函数来启动ServiceMain()函数。而且ServiceMain()这个函数的名字也并不一定就是这个,可以自定义。只不过你在网上查的人都会采用这个函数名而已。并非VC语言的规定或者约束。这个函数也只不过是在调用StartServiceCtrlDispatcher()函数的时候的一个参数而已叫什么名字并不重要。
第二坑就是服务状态的变换问题,只有你有ServiceMain()函数中调用了SetServiceStatus()函数来告诉Windows操作系统你的服务程序是开始启动了还时正在运行之中还是正在停止之中才可以。在这方面我从网上查了好长时间没有一个写的清楚的。我开始的时候以为这个函数没什么大用于是把相关的代码给删掉了,结果就会发生服务程序的状态无法改变。比如你在启动服务的时候没有及时的状态改成正在运行的话那么Windows的服务控制台就会一直启动这个服务直到启动超时而报错最后服务的状态就会卡在启动状态无法将服务停止或者删除。如果你能将服务状态进入到正在运行状态,但在发出停止服务的时候如果不能将服务的状修改成停止的话那么Windows也会一直停止这个服务直到停止超时而报错。而且这个服务的状态也会卡在停止的状态无法改变。
第三个坑就是在ServiceMain()函数中在用SetServiceStatus()函数把服务的状态修改成“正在运行”的状态之后必须要调用WaitForSingleObject()函数把这个服务的主线程给卡住然就是是一直等待发出服务停止的信号。所以在真正的主线程中你是无法再做其它的任何的工作的。故如果要想写好一个完整的Windwos服务程序就必须在ServiceMain()函数调用WaitForSingleObject()函数之前将你想做的事情都给加载完毕之后再调用WaitForSingleObject()函数将主线程卡住一直等待停止服务的指令。当停止服务的指令来临的时候再调用SetServiceStatus()函数把服务的状态修改成已经停止。
第四个这个不算是坑了这个算是一个常识。就是服务程序的调试问题。如果想调试Windows服务程序那么就只能采用“附加到进程”的方式。即先从Windows的服务管理器中启动服务,然后从Visual Studio的“调试”菜单中选择“附加到进程”的选项然后再选择自己程序的那个进程就可以了。
总结一下,那就是不论是VC写Windows服务程序还是C#写Windwos服务程序,都会有一个开始服务的初始化函数。而初始化函数中不能有死循环,但一般的服务程序又必须要用死循环,所以一个完整的Windwos服务程序一定是一个多线程的,在初始化的时候就启动其它的想要加载的死循环的线程。还会有一个程序的销毁函数,同样在销毁函数中也不能有无法完成的循环。最后就是Windwos服务程序的调试必须采用附加到进程的方式来进行。