一、Windows服务应用程序简介
- Windows服务应用程序
- 在自己的Windows会话中长期运行的可执行应用程序
- Windows服务的特点
- 可以随Windows启动而启动
- 可以长期在后台执行
- 不需要交互界面
- 不需要担心将交互界面关闭后程序也随之关闭
- 需要先安装在windows上才能启动应用程序
二、创建Windows服务应用程序
2.1 基于ServiceBase创建服务
2.1.1 使用Visual Studio中的Windows服务模板新建项目
2.1.1.1 文件结构
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/23d4515469aaf055ed7407b7e92b73f1.png)
namespace WindowsService1
{
static class Program
{
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
}
}
- Service1.cs
- 继承自ServiceBase
- 包含构造函数、启动、停止
- 分部类
namespace WindowsService1
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
...
}
protected override void OnStop()
{
}
}
}
- Service1.Designer.cs
- 组件设计器自动生成的代码
- 类的文件名为Service1.Designer.cs,但类名为Service1,且为分部类
namespace WindowsService1
{
partial class Service1
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region 组件设计器生成的代码
private void InitializeComponent()
{
this.ServiceName = "Service1";
}
#endregion
}
}
2.1.1.2 项目结构
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/4f8ed5a639af4bc78375d04e9cb6a27a.png)
- Service1.cs
- 包含设计和代码视图,类似WinForms
- 双击打开设计视图
- 在视图中双击或右键空白位置或者点击链接可跳转至代码视图
- 属性对应于Service1.Designer.cs中的InitializeComponent()的设置
- 可通过属性对服务进行重命名
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8b8224b0f65985c6f0d2b7192b4d4171.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c6b238d8f6ac8af7f6aa62e3be940a62.png)
2.1.2 添加自定义事件日志功能
2.1.2.1 添加EventLog组件
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0f6f0207d0fbc56850eafe2f97a06e20.png)
- Visual Studio会自动在Service1.Designer.cs中添加创建EventLog对象的代码
partial class Service1
{
private void InitializeComponent()
{
this.eventLog1 = new System.Diagnostics.EventLog();
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();
this.ServiceName = "Service1";
((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();
}
private System.Diagnostics.EventLog eventLog1;
}
2.1.2.2 初始化EventLog对象
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("MySource"))
{
System.Diagnostics.EventLog.CreateEventSource(
"MySource","MyNewLog");
}
eventLog1.Source = "MySource";
eventLog1.Log = "MyNewLog";
}
}
2.1.2.3 写入事件日志
public partial class Service1 : ServiceBase
{
...
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("In OnStart.");
}
}
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5604518dd0c92c10d0e2994cd8f40dd2.png)
2.1.3 添加安装程序
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/159d9896d54fa7e9420b1d156601fe9f.png)
2.1.3.1 ProjectInstaller.cs
- Visual Studio自动生成ProjectInstaller.cs
- 该类继承自System.Configuration.Install.Installer
- 自动创建ServiceProcessInstaller和ServiceInstaller的对象
- ServiceProcessInstaller
- ServiceInstaller
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/de9bffc090e98cdb954a6d2c1dbe17dc.png)
namespace WindowsService1
{
[RunInstaller(true)]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
}
}
2.1.3.2 ProjectInstaller.Designer.cs
- ProjectInstaller.Designer.cs
namespace WindowsService1
{
partial class ProjectInstaller
{
#region 组件设计器生成的代码
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
this.serviceInstaller1.Description = "这是描述";
this.serviceInstaller1.DisplayName = "这是displayname";
this.serviceInstaller1.ServiceName = "Service1";
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.serviceInstaller1});
}
#endregion
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
}
}
2.1.3.3 ServiceProcessInstaller
- ServiceProcessInstaller
- 由安装实用程序(例如InstallUtil.exe)调用
- 一个服务可执行文件可能包含两个服务
- “ Hello-World Service 1”
- “ Hello-World Service 2”
- 一个可执行文件创建一个ServiceProcessInstaller对象
- ServiceProcessInstaller设置
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f17862ee720efc0e86efb30a9cecea6c.png)
2.1.3.4 ServiceInstaller
- ServiceInstaller
- 由安装实用程序(例如InstallUtil.exe)调用
- 一个服务创建一个ServiceInstaller对象
- ServiceInstaller设置
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/29434a9bdcb527163c1794b9522c528f.png)
2.1.4 安装和卸载服务
- 以管理员身份打开VS的开发人员命令提示符
- 将路径导航到服务应用程序所在文件夹
- 利用installUtil.exe实用工具对服务进行安装和卸载
- 该工具与.NET Framework一起安装到文件夹%windir%\ Microsoft.NET \ Framework [64] \ 中
- 例如,64位版本的默认路径为%windir%\ Microsoft.NET \ Framework64 \ v4.0.30319 \ InstallUtil.exe
2.1.4.1 安装服务
- installutil MyNewService.exe
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/bb518fdddb7af3da88965436161383b3.png)
2.1.4.2 卸载服务
- installutil /u MyNewService.exe
2.1.5 调试服务
2.1.5.1 调试符号与pdb文件
- 调试符号
- 指编译器在将源文件编译为可执行程序的过程中,为支持调试而摘录的调试信息
- 这些信息以表格的形式记录在符号表中
- pdb文件
- program debug database
- 主要存储了调试符号
- Visual Studio编译时生成的文件
2.1.5.2 方式一:附加到服务进程
- 服务必须是debug版本
- 服务处于已安装、正在运行的状态
- 在Visual Studio“调试”或者“工具”菜单中点击“附加到进程”
- 在调试系统进程时,需要设置Microsoft符号服务器
- 工具=>选项=>调试=>符号=>Microsoft符号服务器
- 选择需要调试的服务进程后即进入调试模式
- 限制
- 调试服务的OnStart()、Main()方法比较麻烦
- 附加到服务进程时,服务已处于运行状态,Main()以及OnStart()方法已经执行完毕
2.1.5.3 方式二:修改应用程序输出类型
- 发布服务时输出类型设置为Windows应用程序
- 调试时将输出类型更改为控制台应用程序
- 需要调整OnStart()和OnStop()方法的调用方式
- 服务的OnStart()和OnStop()方法是由系统服务管理器调用的
- 更改为控制台应用程序后则由控制台应用程序调用
- 需要在Main()方法中调用调整后的方法
- Environment.UserInteractive判断是否在交互模式
public partial class Service1 : ServiceBase
{
...
internal void TestStartupAndStop(string[] args)
{
this.OnStart(args);
Console.ReadLine();
this.OnStop();
}
}
static class Program
{
static void Main(string[] args)
{
if (Environment.UserInteractive)
{
Service1 service1 = new Service1();
service1.TestStartupAndStop(args);
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
}
}
2.2 基于TopShelf创建服务
2.2.1 TopShelf
- Topshelf是用于托管使用.NET Framework编写的服务的第三方框架
- 服务的创建得到简化
- 允许开发人员创建一个简单的控制台应用程序
- 可以使用Topshelf将其安装为服务
- 方便调试
2.2.2 配置TopShelf
- 安装和引用TopShelf
- 在Main()方法中配置TopShelf
class Program
{
static void Main(string[] args)
{
var rc = HostFactory.Run(x =>
{
x.Service<TownCrier>(s =>
{
s.ConstructUsing(name => new TownCrier());
s.WhenStarted(tc => tc.Open());
s.WhenStopped(tc => tc.Close());
});
x.RunAsLocalSystem();
x.SetDescription("Sample Topshelf Host");
x.SetDisplayName("Stuff");
x.SetServiceName("Stuff");
});
var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());
Environment.ExitCode = exitCode;
}
}
2.2.3 安装和卸载TopShelf服务
- 以管理员身份打开系统命令提示窗口
- 将路径导航到服务应用程序所在文件夹
- 输入命令进行安装和卸载
2.2.3.1 安装服务
2.2.3.2 卸载服务
- MyNewService.exe uninstall
2.3 ServiceController
- 指代Windows服务,并允许连接到正在运行或已停止的服务,对其进行操作或获取有关它的信息
- 可以实现安装服务后自动启动服务
[RunInstaller(true)]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
ServiceController serviceController = new ServiceController("Service1");
serviceController.Start();
}
}
2.4 定时服务
public partial class Service1 : ServiceBase
{
...
protected override void OnStart(string[] args)
{
Timer timer = new Timer();
timer.Interval = 60000;
timer.Elapsed += new ElapsedEventHandler(this.OnTimer);
timer.Start();
}
...
public void OnTimer(object sender, ElapsedEventArgs args)
{
...
}
}