编写 window 服务程序

编写 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 );

              } 

        } 

    }} 

原文地址:https://www.cnblogs.com/zhuawang/p/2073038.html