首页 > 开发 > 综合 > 正文

Visual C#中调用Windows服务初探

2024-07-21 02:27:17
字体:
来源:转载
供稿:网友

 windows服务是独立于登录用户而工作的windows应用程序,它通常在计算机启动时开始执行,且常常连续执行,直到计算机关闭为止。像exchange server,iis和杀毒软件等都使用这种方式,这样就可以独立于某一用户而且可以在任何用户登录前来运行,同时也可以服务于所有的进程,从而以一种服务的形式存在。

  正因为windows服务有着这么多的特性,因此,当需要一些特殊功能的时候就可以考虑使用windows服务来解决问题。比如下面我们要做的这个例子。对于我们这些程序设计人员,计算机是在一起工作时间最长的伙伴,每天都会对着它的屏幕八个小时以上,还不包括下班后在家打游戏的时间,因此,保护眼睛是最重要的了。问题的起因来源于本人周六去眼科对激光手术的复查,大夫一再向我强调眼睛的自我调节能力,就是说只要你能保证你每隔一个小时左右就闭眼休息或向远处眺望,离开电脑屏幕,那么已经治好的近视就不会反弹。本人虽是自律性比较强的人,但在计算机屏幕面前就不再如此了,往往几个小时也不抬头一次,为了眼睛的健康,我决定把这个艰巨的任务交由计算机来完成,让它在一小时左右自动提醒我休息五分钟。如此一来,岂不是再也不用顾虑这件事了。

  功能虽然简单,但要写个程序放在启动组里每天自动运行也不是一个好的办法,正巧以前也没做过windows服务,不如索性来试一试,同进也看看.net为我们提供了多么先进的功能吧,于是决定,就用c#来做一个提醒我保护眼睛的windows服务,取名就叫careeye吧。

  运行visual studio.net 2003,建立一个c#的windows服务项目,在careeye.cs的设计视图提示可以把需要的控件和组件拖动到这上面,假如想要做系统日志的话当然就可以把eventlog组件拖过来了,不过这个程序好像不需要这些东西,还是算了吧。那么计时要不要采用计时器控件呢?想了一下,这个控件虽然好用,但太常用了,本着学习新知识的原则,最恰当的恐怕就是线程了,而且在以后做其他windows服务的时候线程肯定是必需的,所以还是用线程好,这样我只要在线程中完成对时间的监测,把线程的启动和停止交给服务的启动和停止,呵,很方便啊。

  再来看careeye.cs的源程序,一大堆没见过的东西,不过仔细分析一下也就没什么了。careeye类派生于servicebase类,因此继承了基本服务类的特性,显然main()方法会由scm(服务控制管理程序)调用,在这个方法中run一个新的careeye实例,这样就运行了一个windows服务,onstart()和onstop()明显是用于启动和停止服务的响应函数了。

  注意在main()方法中有一个servicebase[]的数组,它是为那些一个服务进程包含多个服务准备的,对于这个程序来说,它只有一个careeye服务,因此完全可以把这个数组删除,而只是使用system.serviceprocess.servicebase.run(new careeye());一句就够了。

  接下来为了使用线程,需要引入system.threading命名空间,为了使用对话框,还需要引入system.windows.forms命名空间,这是为了将来提示用户时显示对话框而准备的。

  下面为类careeye添加一个成员字段private thread mainthread;同时在构造函数中对其进行初始化:

mainthread=new thread(new threadstart(threadfunc));
mainthread.priority=threadpriority.lowest; 

  这里把线程的优先级设到最低,这样不会耗用过多的系统性能。这个线程对象使用threadfunc作为线程函数,因此将这个线程函数补充完整:

public static void threadfunc()
{
 int lasthour=datetime.now.hour;
 while (true)
 {
  system.threading.thread.sleep(60000);
  if (datetime.now.hour-1==lasthour)
  {
   messagebox.show("为了爱护您的眼睛,请您暂时休息5分钟并向远处眺望!","警告",messageboxbuttons.ok,messageboxicon.warning,messageboxdefaultbutton.button1,
messageboxoptions.defaultdesktoponly);
   lasthour=datetime.now.hour;
  }
 }
}

  余下的代码就简单了,只要在onstart中启动线程,在onstop中停止线程就行了。

  以上的服务程序虽然很简单,线程的处理上也不很恰当,也违背了很多服务程序的原则比如界面显示等,但对于本人的需求而言是足够了,因此就如此制作了。如果你有需要,当然可以把对话框改为其他的提醒方式如响铃等,线程也可以使用内核对象同时使用更好的处理方法。

  windows服务就做完了,余下的就是要测试了,但发现这个exe无法运行,它会提示你该exe需要使用安装程序来安装服务,看来不可能写一个程序就算是windows服务了,还要把它注册到windows才行。

  接下来,右击careeye.cs的设计视图,添加安装程序,(vs.net想得就是挺周到的),这下又出来一批代码,不过好在不用改代码了,只要把account的账户类型设成localsystem,把starttype设成手动启动就行了,这里用手动是为了方便调试,以后可以改成自动类型。

  编译完后,还是无法运行,此处还需要一步,就是运行installutil来安装这个服务,其安装和卸载的用法为:

installutil careeye.exe
installutil /u careeye.exe 

  安装完后能过系统的服务管理器你就可以看到你的服务了,只要点击启动就可以把它启动,把时间向前改一个小时它就会提醒你需要休息了,呵呵,够简单了吧。

  如果你想制作成安装包分发给自己的朋友,只需要再添加个部署项目就行了,不过为了完成自注册,要在自定义操作编辑器中的安装阶段添加一个自定义的安装操作,把installerclass属性设成true即可。

  余下的事情,就是自己动手试试吧,这回不用担心用眼超时了!
以下是careeye.cs的源程序:

using system;
using system.collections;
using system.componentmodel;
using system.data;
using system.diagnostics;
using system.serviceprocess;
using system.threading;
using system.windows.forms;
namespace careeye
{
 public class careeye : system.serviceprocess.servicebase
 {
  private thread mainthread;
  /// <summary>
  /// 必需的设计器变量。
  /// </summary>
  private system.componentmodel.container components = null;

  public careeye()
  {
   // 该调用是 windows.forms 组件设计器所必需的。
   initializecomponent();

   // todo: 在 initcomponent 调用后添加任何初始化
   mainthread=new thread(new threadstart(threadfunc));
   mainthread.priority=threadpriority.lowest;
  }

  // 进程的主入口点
  static void main()
  {
   //system.serviceprocess.servicebase[] servicestorun;

   // 同一进程中可以运行多个用户服务。若要将
   //另一个服务添加到此进程,请更改下行
   // 以创建另一个服务对象。例如,
   //
   // servicestorun = new system.serviceprocess.servicebase[] {new careeye(), new myseconduserservice()};
   //
   //servicestorun = new system.serviceprocess.servicebase[] { new careeye() };

   system.serviceprocess.servicebase.run(new careeye());
  }

  /// <summary>
  /// 设计器支持所需的方法 - 不要使用代码编辑器
  /// 修改此方法的内容。
  /// </summary>
  private void initializecomponent()
  {
   //
   // careeye
   //
   this.servicename = "careeye";

  }

  /// <summary>
  /// 清理所有正在使用的资源。
  /// </summary>

  protected override void dispose( bool disposing )
  {
   if( disposing )
   {
    if (components != null)
    {
     components.dispose();
    }
   }
   base.dispose( disposing );
  }

  /// <summary>
  /// 设置具体的操作,以便服务可以执行它的工作。
  /// </summary>
  protected override void onstart(string[] args)
  {
   // todo: 在此处添加代码以启动服务。
   mainthread.start();
  }

  /// <summary>
  /// 停止此服务。
  /// </summary>
  protected override void onstop()
  {
   // todo: 在此处添加代码以执行停止服务所需的关闭操作。
   mainthread.abort();
  }
  public static void threadfunc()
  {
   int lasthour=datetime.now.hour;
   while (true)
   {
    system.threading.thread.sleep(60000);
    if (datetime.now.hour-1==lasthour)
    {
     messagebox.show("为了爱护您的眼睛,请您暂时休息5分钟并向远处眺望!","警告",messageboxbuttons.ok,messageboxicon.warning,messageboxdefaultbutton.button1,messageboxoptions.defaultdesktoponly);
lasthour=datetime.now.hour;
    }
   }
  }
 }

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表