当前位置:   article > 正文

Windows服务应用程序_windows下后台服务程序启动前台可交互界面程序

windows下后台服务程序启动前台可交互界面程序

一、Windows服务应用程序简介

  • Windows服务应用程序
    • 在自己的Windows会话中长期运行的可执行应用程序
  • Windows服务的特点
    • 可以随Windows启动而启动
      • 不需要像控制台应用程序一样每次手动点击启动
    • 可以长期在后台执行
      • 不需要交互界面
      • 不需要担心将交互界面关闭后程序也随之关闭
    • 需要先安装在windows上才能启动应用程序
      • 不能直接启动

二、创建Windows服务应用程序

2.1 基于ServiceBase创建服务

2.1.1 使用Visual Studio中的Windows服务模板新建项目

2.1.1.1 文件结构

在这里插入图片描述

  • Program.cs
namespace WindowsService1
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        static void Main()
        {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
            	//Service1继承自ServiceBase
                new Service1()
            };
            ServiceBase.Run(ServicesToRun);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • Service1.cs
    • 继承自ServiceBase
    • 包含构造函数、启动、停止
    • 分部类
namespace WindowsService1
{
    public partial class Service1 : ServiceBase
    {
        public Service1()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
        	//do some business
        	...
        }

        protected override void OnStop()
        {
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • Service1.Designer.cs
    • 组件设计器自动生成的代码
    • 类的文件名为Service1.Designer.cs,但类名为Service1,且为分部类
namespace WindowsService1
{
    partial class Service1
    {
        /// <summary> 
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region 组件设计器生成的代码

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

        }

        #endregion
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
2.1.1.2 项目结构

在这里插入图片描述

  • Service1.cs
    • 包含设计和代码视图,类似WinForms
      • 双击打开设计视图
      • 在视图中双击或右键空白位置或者点击链接可跳转至代码视图
    • 属性对应于Service1.Designer.cs中的InitializeComponent()的设置
      • 可通过属性对服务进行重命名
        在这里插入图片描述
        在这里插入图片描述

2.1.2 添加自定义事件日志功能

2.1.2.1 添加EventLog组件

在这里插入图片描述

  • Visual Studio会自动在Service1.Designer.cs中添加创建EventLog对象的代码
partial class Service1
{
	private void InitializeComponent()
	{
		this.eventLog1 = new System.Diagnostics.EventLog();
		((System.ComponentModel.ISupportInitialize)(this.eventLog1)).BeginInit();
		// 
		// Service1
		// 
		this.ServiceName = "Service1";
		((System.ComponentModel.ISupportInitialize)(this.eventLog1)).EndInit();
	}
	private System.Diagnostics.EventLog eventLog1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
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";
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
2.1.2.3 写入事件日志
public partial class Service1 : ServiceBase
{
	...
	protected override void OnStart(string[] args)
	{
	    eventLog1.WriteEntry("In OnStart.");
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这里插入图片描述

2.1.3 添加安装程序

在这里插入图片描述

2.1.3.1 ProjectInstaller.cs
  • Visual Studio自动生成ProjectInstaller.cs
    • 该类继承自System.Configuration.Install.Installer
    • 自动创建ServiceProcessInstaller和ServiceInstaller的对象
      • ServiceProcessInstaller
      • ServiceInstaller

在这里插入图片描述

  • ProjectInstaller.cs
namespace WindowsService1
{
    [RunInstaller(true)]
    public partial class ProjectInstaller : System.Configuration.Install.Installer
    {
        public ProjectInstaller()
        {
            InitializeComponent();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
2.1.3.2 ProjectInstaller.Designer.cs
  • ProjectInstaller.Designer.cs
namespace WindowsService1
{
    partial class ProjectInstaller
    {
        #region 组件设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
            this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
            // 
            // serviceProcessInstaller1
            // 
            this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
            this.serviceProcessInstaller1.Password = null;
            this.serviceProcessInstaller1.Username = null;
            // 
            // serviceInstaller1
            // 
            this.serviceInstaller1.Description = "这是描述";
            this.serviceInstaller1.DisplayName = "这是displayname";
            this.serviceInstaller1.ServiceName = "Service1";
            this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
            // 
            // ProjectInstaller
            // 
            this.Installers.AddRange(new System.Configuration.Install.Installer[] {
            this.serviceProcessInstaller1,
            this.serviceInstaller1});

        }

        #endregion

        private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
        private System.ServiceProcess.ServiceInstaller serviceInstaller1;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
2.1.3.3 ServiceProcessInstaller
  • ServiceProcessInstaller
    • 由安装实用程序(例如InstallUtil.exe)调用
      • 一个服务可执行文件可能包含两个服务
        • “ Hello-World Service 1”
        • “ Hello-World Service 2”
      • 一个可执行文件创建一个ServiceProcessInstaller对象
  • ServiceProcessInstaller设置
    • Account

在这里插入图片描述

2.1.3.4 ServiceInstaller
  • ServiceInstaller
    • 由安装实用程序(例如InstallUtil.exe)调用
    • 一个服务创建一个ServiceInstaller对象
  • ServiceInstaller设置

在这里插入图片描述

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
    在这里插入图片描述
2.1.4.2 卸载服务
  • installutil /u MyNewService.exe

2.1.5 调试服务

2.1.5.1 调试符号与pdb文件
  • 调试符号
    • 指编译器在将源文件编译为可执行程序的过程中,为支持调试而摘录的调试信息
      • 主要包括源文件名、变量名、函数名、对应的行号等等
    • 这些信息以表格的形式记录在符号表中
  • pdb文件
    • program debug database
    • 主要存储了调试符号
    • Visual Studio编译时生成的文件
      • 每个程序集都有一个与之对应的pdb文件
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();  
	} 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
static class Program
{
	/// <summary>
	/// 应用程序的主入口点。
	/// </summary>
	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);
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

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;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2.2.3 安装和卸载TopShelf服务

  • 以管理员身份打开系统命令提示窗口
  • 将路径导航到服务应用程序所在文件夹
  • 输入命令进行安装和卸载
2.2.3.1 安装服务
  • MyNewService.exe install
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();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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)
	{
		...
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/69291
推荐阅读
相关标签
  

闽ICP备14008679号