C# 开发一个简单的 Window Service 入门

开始

创建项目

创建一个 Windows 服务的项目

9924aa85109d403a9bbf63f5607c3f21-QQ20180302143821.png

创建成功后的目录结构

b45de38afaa24a7cae9f6a09a3998219-QQ20180302144035.png

App.config 应用程序配置文件
Program.cs 程序入口文件
Service1.cs WindowService 主文件

准备代码

创建日志类

创建一个日志类LogHeper.cs

class LogHelper
{
  public static void Info(string msg)
  {
    if (!Directory.Exists("C:\\DemoServiceLog"))
      Directory.CreateDirectory("C:\\DemoServiceLog");
    string file = "C:\\DemoServiceLog\\" + DateTime.Now.ToString("yyyy-MM-dd") + ".log";
    if (!File.Exists(file))
    {
      StreamWriter sw = File.AppendText(file);
      sw.Close();
    }
    while (true)
    {
      try
      {
        StreamWriter sw = File.AppendText(file);
        sw.WriteLine(DateTime.Now.ToString("HH🇲🇲ss.s") + " " + msg);
        sw.Close();
        break;
      }
      catch (Exception e)
      {
        System.Threading.Thread.Sleep(50);
        continue;
      }
    }
  }
}

调整 Service 类

在强迫症的驱使下将Service1.cs改名为Service.cs

同时Program.cs类中也需要指定Service.cs

static class Program
{
  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
  static void Main()
  {
    ServiceBase[] ServicesToRun;
    ServicesToRun = new ServiceBase[]
    {
      new Service()
    };
    ServiceBase.Run(ServicesToRun);
  }
}

是的,它可以在 ServiceBase 里面指定多个服务。在这个 Demo 里面我们只运行一个。

编写服务程序的事件

右键Service.cs类查看代码

public partial class Service : ServiceBase
{
  public Service()
  {
    InitializeComponent();
  }
  protected override void OnStart(string[] args)
  {
    LogHelper.Info("Service is start...");
  }

  protected override void OnStop()
  {
    LogHelper.Info("Service is stop...");
  }
}

默认重写了两个事件 ,一个服务启动 (OnStart) 事件,一个服务停止事件 (OnStart)

d66b558757174980942987d02eabb003-image.png

还支持 暂停,继续,电源关闭等事件

需要注意一点的是,在这些事件中的代码千万不能出现堵塞以及死循环的代码。否则会导致启动或停止失败。甚至卸载都会出现问题。

曾不小心在启动事件中,监听了一个端口,堵塞在那里,导致启动不成功。卸载过程中提示目标服务已被标记为删除,最后通过删除注册表信息才得以卸载。

服务的安装与卸载

服务的信息配置

目前一个简单的 WindowService 算是完成。在启动和关闭的时候将记录在日志当中。

那么怎么让他安装在用户的机器上呢?

其实也很简单,首先双击Service.cs

在激活的标签页里面右键添加安装程序

项目目录里面会多出一个ProjectInstaller.cs项,双击后出现

da408e922b5f4aea9e15d6263ab017f2-image.png

选中serviceProcessInstaller1 选择AccountLocalSystem

2477c88926024799ac1a138d5ea2d4d6-image.png

选中serviceInstaller1 后配置服务信息

e6a50049a5ce458b809623c5ef2b22d7-image.png

Description 服务描述
DisplayName 给用户的一个友好名称
ServiceName 服务的名称

服务安装与卸载批处理文件

创建一个 install.bat 文件

@ECHO OFF
echo 准备安装服务
pause
echo 安装服务...
echo ---------------------------------------------------
%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe %~dp0\DemoService.exe
net start DemoService
sc config DemoService start=auto
echo ---------------------------------------------------
echo 安装服务成功!
pause

调用.NET Framework 的 installutil.exe 程序进行安装服务
安装成功后通过 net start DemoService 启动服务
sc config Demoservice start=auto 配置服务的启动类型为自动,相当于开机启动

创建一个 uninstall.bat 文件

@ECHO OFF
echo 准备卸载服务
pause
echo 卸载服务...
echo ---------------------------------------------------
%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\installutil.exe %~dp0\DemoService.exe -u
sc delete DemoService
Net Stop DemoService
echo ---------------------------------------------------
echo 卸载服务成功!
pause

将这两个文件与项目生成出来的 DemoService.exe 放在同级目录下,右键已管理员方式运行即可完成安装与卸载。

在 C 盘产生的日志文件中也记录了我们在代码中输出的信息

Win+R 输入 service.msc 即可找到安装成功的服务

bdcef4d38fd243dba5ad4479379113c9-image.png

其中 Window Service Demo只是一个友好的名称而并非服务真正的名称

真正的名称在我们刚刚在serviceInstaller1配置的ServiceName

结语

一个简单的 Window Service 就已经完成了。虽然他并没有完成什么特别令人有成就感的行为,也没有做出什么惊天地泣鬼神的操作。

但这只是一个引子。一个 Window Service 入门的引子。通过 Window Service 我们可以做很多有意思的事情。

我主要是做 JAVA WEB 开发的,大家都知道浏览器最初设计为了安全考虑,浏览器上所执行的脚本是不允许和用户的机器交互的,也仅仅是缓存一些东西而已,对用户威胁不大。

举一个简单的例子,浏览器上传文件,可以说是都是用户自主触发的,都是经过用户的同意的。正常情况下是不可能自动将用户的文件上传上去。

最早浏览器与客户端交互的一个中间件叫做OCX控件或ActiveX控件,或许还有其他的控件。但是他们只能在那个令人诟病的 IE 浏览器上使用。

后面我将会写一篇关于通过 Window 服务让浏览器与客户端交互的一个案例。满足一些特殊需求。