编写 Window 服务程序
 
 

一、直观认识Windows服务。

       打开Windows“控制面板/管理工具/服务”,系统显示Windows服务列表。
       
 
 
       双击服务,可以显示和更改服务属性。在这个对话框中,可以控制服务的启动、暂停和停止。在这里还可以配置服务的启动类型,令服务在系统启动时自行启动。因此,Windows服务经常作为服务器程序运行。
       
 
       在故障恢复这个属性页,可以配置该服务失败后系统的相应。一些病毒程序就是在这里做文章,将病毒程序激活的。
       
 
 

二、Windows服务的开发要点

        Visual Studio的随机文档里,详细介绍了Windows服务程序的开发步骤,并且带有实例,笔者不再赘述。读者只需注意几个要点:
        1、创建一个派生自ServiceBase的入口类。这个入口类管理这个Windows服务的生存期。
 
     public class MyService : System.ServiceProcess.ServiceBase
     {
         ……
     }
 
        2、在入口类的main方法里将服务向Windows的服务控制器(Service Control Manager, SCM)注册,代码:
 
       ……
         System.ServiceProcess.ServiceBase[] ServicesToRun;
         ServicesToRun = new System.ServiceProcess.ServiceBase[] { new MyService() };
         System.ServiceProcess.ServiceBase.Run(ServicesToRun);
         ……
 
        3、重写 OnStart 、OnStop ,或OnPause 和 OnContinue 方法来响应服务状态的更改。通常需要重写OnStart 方法,结束服务时在 OnStop 方法中释放资源,酌情重写OnPause 和 OnContinue方法。
        4、Windows服务通常启动一个定时器来定时或轮询进行业务处理。
        5、Windows服务需要安装后才能使用。通常通过两个办法安装Windows服务:
  • 在命令行运行InstallUtil.exe;
  • 在Windows服务程序的代码中添加ProjectInstraller类的实例,里面包含ServiceProcessInstaller类和ServiceInstaller类的实例。
        上述两个办法在Framework的随机文档中均有描述,在此不再赘述。
        6、Windows服务在Windows的服务控制器(Service Control Manager, SCM)中运行,因此调试起来不像其他Visual Studio应用程序那样简单。关于Windows服务的调试,在Visual Studio的随机文档里面有介绍,在此不再赘述。

三、Windows服务的异常处理

        Windows服务没有用户界面,在运行过程中难以将异常通知给用户。通常情况下,Windows服务在运行过程中发生了异常,可能导致服务运行挂起,但没有任何提醒。
        推荐的一个做法是在Windows服务中捕获异常,并把异常信息写在Windows的事件日志中。打开Windows的“控制面板/管理工具/事件查看器”,系统显示Windows事件日志。
        
 
        在一个实际的应用中,笔者除了把异常和提示记录在事件日志中,还把严重错误自动通过邮件发送给相关人员。同时,所有记录在事件日志中的信息,还重定向到一个自行开发的控制台程序中,用以随时监控服务。
        
 

三、Windows事件日志的开发要点和技巧

        Visual Studio的随机文档里,在介绍Windows服务程序的开发步骤的同时,也介绍了如何向Windows服务中加入事件日志,笔者不再赘述。开发要点如下:
        1、在需要写入日志的类中创建EventLog的实例eventLog,在构造函数里加入代码:
     if (!System.Diagnostics.EventLog.SourceExists("mySource"))
     {        
     System.Diagnostics.EventLog.CreateEventSource("mySource","myEventLog");
     }
     eventLog.Source = " mySource ";
     eventLog.Log = " myEventLog ";
    2、在需要写事件日志的地方写日志,例如:
     protected override void OnStop()
     {
         eventLog.WriteEntry("In onStop.");
     }
 
         读者可以在实际应用中尝试使用下面的技巧。
         1、把写Windows事件日志的代码封装成独立的class,这样不仅在Windows服务中,而且在其他的业务代码中都可以使用Windows事件日志。代码见附件。
        2、为方便调试和跟踪,Visual Sdudio提供了Trace类。在应用程序的debug编译版本中,用Trace类可以把调试和跟踪信息写到控制台。有一个技巧,可以同时把写入Trace的内容写入Windows事件日志。要点如下:
        首先声明一个事件监听类EventLogTraceListener的实例,
          static private  EventLogTraceListener cTraceListener = new EventLogTraceListener( m_eventLog );
        将EventLogTraceListener的实例加入Trace的监听列表:
          Trace.Listeners.Add( cTraceListener );
        此后,凡是写入Trace的调试信息,均写入Windows事件日志中。如果不希望将Trace继续写入事件日志,运行下面代码即可:
          Trace.Listeners.Remove( cTraceListener );
   
        3、写入事件日志的信息,还可以同时写入其他应用程序窗体中的显示控件。
        首先打开窗体的设计视图,从工具箱/组件中选择EventLog并加入窗体,配置EventLog的EnableRaisingEvents属性为True。
        加入EventLog的EntryWritten事件处理方法,该事件的第二个参数类行为System.Diagnostics.EntryWrittenEventArgs,其中包含了Windows事件日志条目中的必要内容,将该内容显示在窗体中的某个显示控件中即可。示例代码如下:
/// <summary>
/// 监听事件日志
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void eventLog_EntryWritten(object sender,
     System.Diagnostics.EntryWrittenEventArgs e)
{
     try
     {
         // 把日志内容写到名为listEventLog的List控件中
         listEventLog.Items.Insert( 0,
              e.Entry.TimeWritten + " " +
              e.Entry.Message );
 
         // List控件保存不超过500行的日志
         while( listEventLog.Items.Count > 500 )
         {
              listEventLog.Items.RemoveAt( listEventLog.Items.Count-1 );
         }
     }
     catch( Exception ex )
     {
         MessageBox.Show( ex.Message );
     }
}

四、与Windows服务的通讯

       在应用程序或其他服务中,可以与Windows服务通讯,包括:
  • 管理Windows服务的生命期,即开启、停止、暂停和重启服务;
  • 获得Windows服务的属性和状态;
  • 获得特定计算机上的服务列表;
  • 向特定的服务发送命令。
        这些操作是通过ServiceController 类完成的。ServiceController是一个可视化控件,可以在工具箱中找到。
        比较有意思的是ServiceController 中ExecuteCommand这个方法,调用这个方法,可以向Windows服务发送命令,指挥Windows服务的一些操作。例如,在Windows服务的入口类中有一个复写OnCustomCommand()的方法:
         /// <summary>
         /// 执行用户自定义消息
         /// </summary>
         /// <param name="command">消息编号</param>
         protected override void OnCustomCommand( int command )
         {
              try
              {
                   switch( command )
                   {
                       case 1: // 业务操作
                            doBusiness1();
                            break;
                       case 2: //业务操作
                            doBusiness2();
                            break;
                       default:
                            ……
                            break;
                   }
              }
              catch( Exception ex )
              {
                   // 错误信息
                   string strErrorMsg = string.Format("异常:{0}/n", ex.Message );
                   // 写日志
                   TLineEventLog.DoWriteEventLog( strErrorMsg, EventType.Error );
                   // 给管理员发邮件
                   CMail.SendMail(
PropertyManager.strMailFromAddress, PropertyManager.strMailAdminAddress, "",
                       "异常信息提示",
strErrorMsg );
                   // 写Trace
                   Trace.WriteLine( strErrorMsg );
              }
         }
        在另外一个应用程序中通过ServiceController的ExecuteCommand()方法向这个Windows服务发送命令:
            myController.ExecuteCommand(2);
        Windows服务将执行业务方法:doBusiness2();
        应该承认,利用ServiceController与Windows服务通讯的功能目前还十分薄弱。通过ExecuteCommand只能与Windows服务进行简单而有限的通讯。
        笔者在实际的应用中,分别用一个命令行程序、一个控制台程序和一个Webservice和Windows服务进行通讯,启动、停止服务,或通过ExecuteCommand控制服务的行为。
        
 

附件:操纵Windows事件日志的通用类

using System;
using System.Diagnostics;
using System.Configuration;
 
namespace MYCOMMON.EVENTLOG
{
     public enum EventType { Error,Information,Warning }
     /// <summary>
     ///
     /// </summary>
     public class TLineEventLog
     {
         // 任务日志
         static private EventLog m_eventLog = new EventLog();
         // 源名称,从配置文件中读取
         static private string m_strEventSource =
              ConfigurationSettings.AppSettings["f_eventLog.Source"].ToString().Trim();
         // 日志名称,从配置文件中读取
         static private string m_strEventLog =
              ConfigurationSettings.AppSettings["f_eventLog.Log"].ToString().Trim();
 
         // 调试信息写入日志
         static private EventLogTraceListener cTraceListener =
              new EventLogTraceListener( m_eventLog );
 
         // 缺省构造函数。配置文件读取失败时,提供默认的源名称和日志名称
         public TLineEventLog()
         {
              if( m_strEventSource.Length == 0 )
                   m_strEventSource = "mySource";
 
              if( m_strEventLog.Length == 0 )
                   m_strEventLog    = "myLog";
 
              m_eventLog.Source = m_strEventSource;
              m_eventLog.Log    = m_strEventLog;
         }
 
         // 构造函数。提供源名称和日志名称。
         public TLineEventLog( string strEventSource, string strEventLog )
         {
              m_strEventSource = strEventSource;
              m_strEventLog    = strEventLog;
              m_eventLog.Source = m_strEventSource;
              m_eventLog.Log    = m_strEventLog;
     }
 
         /// <summary>
         /// 写事件日志
         /// </summary>
         /// <param name="strMessage">事件内容</param>
         /// <param name="eventType">事件类别,错误、警告或者消息</param>
         static public void DoWriteEventLog( string strMessage, EventType eventType )
         {
              if (!System.Diagnostics.EventLog.SourceExists( m_strEventSource ))
              {         
                   System.Diagnostics.EventLog.CreateEventSource(
                       m_strEventSource,m_strEventLog );
              }
 
              EventLogEntryType entryType = new EventLogEntryType();
              switch(eventType)
              {
                   case EventType.Error:      
                       entryType = EventLogEntryType.Error;
                       break;
                   case EventType.Information:
                       entryType = EventLogEntryType.Information;
                       break;
                   case EventType.Warning:    
                       entryType = EventLogEntryType.Warning;
                       break;
                   default:                   
                       entryType = EventLogEntryType.Information;
                       break;
              }
              m_eventLog.WriteEntry( strMessage, entryType );
         }
 
         /// <summary>
         /// 写事件日志,默认为消息
         /// </summary>
         /// <param name="strMessage">事件内容</param>
         static public void DoWriteEventLog( string strMessage )
         {
              if (!System.Diagnostics.EventLog.SourceExists( m_strEventSource ))
              {        
                   System.Diagnostics.EventLog.CreateEventSource(
                       m_strEventSource,m_strEventLog );
              }
              m_eventLog.WriteEntry( strMessage );
         }
 
         /// <summary>
         /// 调试信息写入日志
         /// </summary>
         public static void OpenTrace()
         {
              if( cTraceListener != null )
              {
                   if( !Trace.Listeners.Contains( cTraceListener ) )
                   {
                       Trace.Listeners.Add( cTraceListener );
                   }
              }
         }
 
         /// <summary>
         /// 调试信息不写入日志
         /// </summary>
         public static void CloseTrace()
         {
              if( Trace.Listeners.IndexOf(cTraceListener) >= 0 )
              {
                   Trace.Listeners.Remove( cTraceListener );
              }
         }
     }
}
 
作者简介:张昱,联想利泰软件公司(原联想软件设计中心) e-zhangyu@vip.sina.com

编写 Window 服务程序的更多相关文章

  1. 如何编写Window服务程序(C# )

    虚拟需求:编写一个Window服务,并注册到操作系统的服务里.让他隔30秒运行一下(写当前日期到一个文本里) 步骤: 创建一个Window 窗体应用程序项目(Greatwall.Mes.Windows ...

  2. C# 编写Window服务基础(一)

    一.Windows服务介绍: Windows服务以前被称作NT服务,是一些运行在Windows NT.Windows 2000和Windows XP等操作系统下用户环境以外的程序.在以前,编写Wind ...

  3. 用 C 语言编写 Windows 服务程序的五个步骤

    Windows 服务被设计用于需要在后台运行的应用程序以及实现没有用户交互的任务.为了学习这种控制台应用程序的基础知识,C(不是C++)是最佳选择.本文将建立并实现一个简单的服务程序,其功能是查询系统 ...

  4. C语言编写Windows服务程序

    原文:C语言编写Windows服务程序 #include <Windows.h> #include <stdio.h> #define SLEEP_TIME 5000 // 间 ...

  5. 用C语言编写Windows服务程序的五个步骤

    Windows 服务被设计用于需要在后台运行的应用程序以及实现没有用户交互的任务.为了学习这种控制台应用程序的基础知识,C(不是C++)是最佳选择.本文将建立并实现一个简单的服务程序,其功能是查询系统 ...

  6. 编写windows服务程序

    2012-11-02 08:54 (分类:计算机程序) windows服务是一个运行在后台并实现勿需用户交互的任务的控制台程序,对于隐藏程序有很大帮助. 用了几天时间概括了编写windows服务程序的 ...

  7. C#编写window服务,一步一步(1)

    Window服务是啥,这里就不废话了,如何用在哪里用也不废话了,这里我这篇文章只是详述了我在vs2012中创建window服务的经过,希望对你有所帮助. 另外:我在编写服务过程中参考了 Profess ...

  8. C#编写Windows服务程序图文教程

    安装服务程序C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe  要安装的服务程序路径(如F:\***.exe)卸载服务程序C: ...

  9. C#编写Windows服务程序 (服务端),client使用 消息队列 实现淘宝 订单全链路效果

    需求: 针对 淘宝提出的 订单全链路 产品接入 .http://open.taobao.com/doc/detail.htm?id=102423&qq-pf-to=pcqq.group oms ...

随机推荐

  1. 构建高性能web站点--读书大纲

    用户输入你的站点网址,等了半天..还没打开,裤衩一下就给关了.好了,流失了一个用户.为什么会有这样的问题呢.怎么解决自己站点“慢”,体验差的问题呢. 在这段等待的时间里,到底发生了什么?事实上这并不简 ...

  2. mipmap 目录和drawable 目录有什么区别

    Q :最近使用studio 发现drawle-hdpi 都没有了换成了mipmap-hdpi,这两个目录有什么区别呢,哪个比较好呢??? A: 我简单总结一下: 使用上没有任何区别,你把它当drawa ...

  3. 如何用HTML5+PhoneGap写个Path项目

    最近Path这个应用很火爆,网上也出现了不少仿Path菜单的项目.即使在原生APP里边,Path的效果也是非常赞的.我突然想,Web APP是不是也能做出类似Path那样的效果呢?于是就有了OPath ...

  4. xampp 提示 This setting can be configured in the file "httpd-xampp.conf".

    错误提示如下: New XAMPP security concept: Access to the requested object is only available from the local ...

  5. 模式自由(Schema-free)和数据存储的非格式化趋势

    最近遐想,数据存储的非格式化趋势. 格式化表格到自由的XML存储 数年以前,多家数据库厂商开始XML数据库存储.XML数据作为一种自描述的半结构化数据为Web的数据管理提供了新的数据模型,如果将XML ...

  6. mysql安装与配置

    想在个人电脑上安装mysql学习用.在此做下记录 步骤一: MySQL安装文件分为两种,一种是msi格式的,一种是zip格式的.如果是msi格式的可以直接点击安装,按照它给出的安装提示进行安装(相信大 ...

  7. 顺序表的基本操作(C)

    在顺序存储结构实现基本操作:初始化.创建.插入.删除.查找.遍历.逆置.合并运算. 运行示例: 请输入线性表La的长度: 请输入线性表La中的元素(共5个) *** 此时线性表La中的元素 *** * ...

  8. spring开发相关网址

    jar包下载地址:http://repo.springsource.org/libs-release-local/org/springframework/spring/

  9. 推荐一款C#反编译软件(开源)

    大二的时候老师要求做过一个小项目,大概4个人左右一组.当时交流不是特别到位,项目在一个同学的电脑上建成了就一直在他的电脑上(所以好东西不要烂在你的硬盘里),也不知道什么源码管理,可悲到项目做完我还没有 ...

  10. (转载)OC学习篇之---Foundation框架中的NSObject对象

    前一篇文章讲到了OC中的代理模式,而且前几篇文章就介绍了OC中的类相关知识,从这篇文章开始我们开始介绍Foundation框架. OC中的Foundation框架是系统提供了,他就相当于是系统的一套a ...