zoukankan      html  css  js  c++  java
  • 编写 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 );

                  } 

            } 

        }} 

  • 相关阅读:
    触摸事件传递与响应者链条
    运动事件Motion Events
    手势识别
    MVC模式
    单例模式
    观察者模式(一对多)
    关于多线程的介绍
    Sandbox简介和路径获取
    NSFileManager和NSFileHandle使用
    归档储存
  • 原文地址:https://www.cnblogs.com/zhuawang/p/2073038.html
Copyright © 2011-2022 走看看