0
点赞
收藏
分享

微信扫一扫

C#GJBC-32.4.3Windows服务项目


 


32.4.3  Windows服务项目


使用C# Windows服务的新项目创建向导可以创建Windows服务,该项目命名为QuoteService,其窗口如图32-7所示。注意,在选择项目时不要误选为Web服务项目。




图   32-7


在单击 OK 按钮开始创建 Windows 服务应用程序之后,就会出现一个外表与 Windows Forms 应用程序相似的设计器,但是不能在其中插入 Windows Forms 组件,因为应用程序不能直接在屏幕上显示任何信息,本章的后面将使用设计器添加性能计数器和事件日志等其他组件。


选择这个服务的属性,可以打开如图 32-8 所示的属性编辑窗口。




图   32-8


使用服务属性可以配置如下值:


       AutoLog指定启动和停止服务的事件自动写到日志文件中。


●       CanPauseAndContinue 、 CanShutdown 和 CanStop 指定服务可以处理具体的暂停、继续、关闭和停止服务的请求。


●       ServiceName 是写到注册表中的服务名称,使用这个名称可以控制服务。


●       CanHandlePowerEvent 选项对运行在膝上计算机的服务有效。如果启用这个选项,服务就可以响应低电源事件,改变服务的操作方式。


提示:


不管项目的名称是什么,默认的服务名称都是WinService1。可以只安装一个WinService1服务。如果在测试过程中出现了安装错误,有可能已经安装了WinService1服务。因此,在服务开发的初始阶段,一定要用属性编辑器把服务的名称改为比较适当的名称。


使用属性编辑器改变上述属性,在InitalizeComponent()方法中设置ServiceBase派生类的值。Windows Forms应用程序中也使用InitalizeComponent()方法,对于服务而言,这个方法的使用方式与Windows Forms应用程序相似。


向导将生成代码,但是我们将把文件名改为QuoteService.cs,把命名空间的名称改为Wrox.ProCSharp.WinServices,并把类名改为QuoteService。后面将详细讨论这些代码。


1. ServiceBase类


ServiceBase类是所有.NET服务的基类。QuoteService类就是从ServiceBase类派生出来的;QuoteService类使用一个未标注的帮助类System.ServiceProcess.NativeMethods与SCM进行通信,System.ServiceProcess.NativeMethods是Win32 API调用的包装类。ServiceBase类是私有的,因此,不能在这里的代码中使用它。


图 32-9 显示了 SCM 、 QuoteService 类和 System.ServiceProcess 命名空间中的类是怎样相互作用的。在这个图中,垂直方向为对象的生命线,水平方向为通信情况,通信是按照时间的先后顺序而进行的。


SCM启动应该启动的服务进程。首先调用Main()方法。在示例服务的Main()方法中,调用ServiceBase基类的Run()方法。Run()使用SCM中的NativeMethods.StartServiceCtrl Dispatcher()注册ServiceMainCallback()方法,并把记录写到事件日志中。


接下来,SCM在服务程序中调用已注册的ServiceMainCallback()方法。ServiceMainCallback()本身使用NativeMethods.RegisterServiceCtrlHandler[Ex]()在SCM中注册处理程序,并在SCM中设置服务的状态。之后调用OnStart()方法。在OnStart()中,必须执行启动代码。如果OnStart()执行成功,就把字符串Service Started Sucessful写到事件日志中。


处理程序是在ServiceCommandCallback()方法中执行的。当改变了对服务的请求时,SCM就调用ServiceCommandCallback()方法。ServiceCommandCallback()方法再把请求发送给OnPause()、OnContinue()、OnStop()、OnCustomCommand()和OnPowerEvent()。



图   32-9


2. 主函数


现在讨论服务进程中由应用程序向导生成的主函数。在主函数中,声明了一个元素为ServiceBase类的数组ServicesToRun。创建QuoteService类的一个实例,并作为ServicesToRun数组的第一个元素。如果在这个服务进程中要运行多个服务,就需要把具体服务类的多个实例添加到数组中。然后把ServicesToRun数组传递给ServiceBase类的静态方法Run()。使用ServiceBase的Run()方法,可以把SCM引用供给服务的入口点。服务进程的主线程现在处于停滞状态,等待服务的结束。


下面是自动生成的代码:


    


      static void Main()


      {


         System.ServiceProcess.ServiceBase[] ServicesToRun;


   


         // More than one user Service may run within the same process. To


         // add another service to this process, change the following line


         // to create a second service object. For example,


         //


         //   ServicesToRun = New System.ServiceProcess.ServiceBase[]


         //   {


         //      new WinService1(), new MySecondUserService()


         //   };


         //


 


         ServicesToRun = new System.ServiceProcess.ServiceBase[]


         {      


            new QuoteService()


         };


         System.ServiceProcess.ServiceBase.Run(ServicesToRun);


      }


如果进程中只有一个服务,就可以删除数组。Run()方法接收从ServiceBase派生出来的单个对象,因此Main()方法可以简化为:


System.ServiceProcess.ServiceBase.Run(new QuoteService());


如果有多个服务,例如Windows程序Services.exe就包含多个服务,并且需要那些服务有共享的初始化,则共享的初始化必须在Run()方法运行之前完成。因为主线程处于停滞状态,直到服务进程停止为止,以后的指令在服务结束之前就不能执行。


初始化花费的时间不应该太长,通常不应该超过 30 秒。如果执行初始化代码所花费的时间过多,则服务控制管理器就认为服务启动失败了。初始化时间不应该超过 30 秒,必须是针对速度最慢的机器而言。如果初始化的时间过长,就应该在另一线程中进行初始化,以便主线程及时地调用 Run() 。然后,事件对象可以用信号通知线程已经完成了它的工作。


3. 服务的启动


在服务启动时,调用OnStart()方法。这时,可以启动套接字服务器。为了使用QuoteServer,必须引用QuoteServer.dll程序集。调用OnStart()的线程不能停滞下来,OnStart()方法必须返回给调用者(即ServiceBase类的ServiceMainCallback()方法)。ServiceBase类注册处理程序,并在调用OnStart()之后把服务成功启动的消息通知给SCM:


protected override void OnStart(string[] args)



{



quoteServer = new QuoteServer(@"c:/ProCSharp/Services/quotes.txt",



5678);



quoteServer.Start();



}



quoteServer变量声明为类中的私有成员:



namespace Wrox.ProCSharp.WinServices



{



public class QuoteService : System.ServiceProcess.ServiceBase



{



private System.ComponentModel.Container components = null;



private QuoteServer quoteServer;


4. 处理程序方法


当停止服务时,就调用OnStop方法。应该在OnStop方法中停止服务的功能:



protected override void OnStop()



{



quoteServer.Stop();



}

除了 OnStart() 和 OnStop() 之外,还可以重写服务类中的下列处理程序:


       OnPause():在暂停服务时,调用这个方法。


       OnContinue():当服务从暂停状态返回到正常操作时,调用这个方法。为了调用已重写的OnPause()方法和OnContinue()方法,CanPauseAndContinue属性必须设置为true。


       OnShutdown() :当 Windows 操作系统关闭时,调用这个方法。通常情况下, OnShutdown()方法的行为应该与OnStop()方法相似。如果需要更多的时间关闭服务,则可以请求更多的时间。与OnPause()和OnContinue()相似,必须设置一个属性使行为有效,即CanShutdown属性必须设置为true。


       OnCustomCommand() :这个处理程序可以为服务控制程序发送过来的定制命令提供服务。OnCustomCommand()方法有一个用于获取定制命令号码的int参数,号码的取值范围是128至256,小于128的值是为系统预留的。在我们的服务中,使用定制命令号码为128的命令重新读取引用文件:

protected override void OnPause()



{



quoteServer.Suspend();



}



protected override void OnContinue()



{



quoteServer.Resume();



}



protected override void OnShutdown()



{



OnStop();



}



public const int commandRefresh = 128;



protected override void OnCustomCommand(int command)



{



switch (command)



{



case commandRefresh:



quoteServer.RefreshQuotes();



break;



default:



break;



}



}

举报

相关推荐

0 条评论