很多时候,我们希望服务程序可以直接运行,或者可以响应一些参数,这时候,混合Windows服务和Windows窗体的程序就排上用场了。要实现同时支持Windows服务和Windows窗体,需要在启动的第一步时判断当前运行环境是否为服务模式,可以从以下几个方面进行判断:

  • 当前用户名称:Environment.UserName,如果为SYSTEM则可以是服务模式
  • 是否用户交互模式:Environment.UserInteractive,为false时也可以认为是服务模式
  • 自定义启动参数:创建服务时添加一个特定的启动参数,比如[/s],然后代码中检查启动参数args[0] == "/s"

如果上述条件都不成立,就进入窗体模式,或者是响应一些其他的命令行参数,比如安装[/i]、卸载服务[/u]等。

项目需要添加下面的组件引用:

  • System.Configuration.Install
  • System.ServiceModel
  • System.ServiceProcess

项目包含的类文件:

  • Program.cs:根据运行模式执行服务或者窗体,或者响应命令行参数;
  • MainService.cs:服务类,实现与窗体类一致的功能;
  • MainForm.cs:窗体类,实现与服务类一致的功能,还可以添加一些安装和卸载服务,启动和停止服务的管理功能;
  • MainWorker.cs:实现功能的类,服务类与窗体类都调用该类;
  • ServiceInstaller.cs:服务安装类,可以调用框架的InstallUtil.exe工具安装卸载服务。

各个类的代码如下:

  • Program.cs
  1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Runtime.InteropServices;
6 using System.ServiceProcess;
7 using System.Windows.Forms;
8
9 namespace WindowsServiceFormHybridSample
10 {
11 internal static class Program
12 {
13 public const string SERVICE_NAME = "WindowsServiceFormHybridSample";
14
15 [STAThread]
16 static void Main(string[] args)
17 {
18 //本应用程序为Windows服务和Windows窗体混合的模式
19 //如果启动参数为/S,则进入Windows服务模式,否则进入Windows窗体模式
20 //如果当前账户名称为SYSTEM,则进入Windows服务模式,否则进入Windows窗体模式
21
22 //var serviceMode = args.Length > 0 && args[0].Equals("/S", StringComparison.OrdinalIgnoreCase);
23 var serviceMode = Environment.UserName.Equals("SYSTEM", StringComparison.OrdinalIgnoreCase);
24 25 try
26 {
27 if (serviceMode)
28 {
29 //开启Windows服务模式,切勿弹出消息框
30 ServiceBase.Run(new MainService());
31 return;
32 }
33
34 //开启Windows窗体模式
35 Application.EnableVisualStyles();
36 Application.SetCompatibleTextRenderingDefault(false);
37
38 if (args.Length == 0)
39 {
40 //打开窗体
41 using (var form = new MainForm())
42 {
43 Application.Run(form);
44 }
45
46 return;
47 }
48
49 //处理命令行参数
50 Program.ParseArgs(args);
51 }
52 catch (Exception ex)
53 {
54 if (serviceMode)
55 {
56 //写入错误日志
57 }
58 else
59 {
60 MessageBox.Show(ex.ToString());
61 }
62 }
63 }
64
65 private static void ParseArgs(string[] args)
66 {
67 var argInstall = 0;
68 var argUninstall = 0;
69 var argSilent = 0;
70 var argOthers = 0;
71
72 foreach (var arg in args)
73 {
74 var temp = arg.Replace('/', '-').ToUpper();
75
76 switch (temp)
77 {
78 case "-I":
79 argInstall = 1;
80 break;
81 case "-U":
82 argUninstall = 1;
83 break;
84 case "-S":
85 argSilent = 1;
86 break;
87 default:
88 argOthers = 1;
89 break;
90 }
91 }
92
93 if (argOthers == 1)
94 {
95 MessageBox.Show(Program.SERVICE_NAME + "支持的命令行参数:\r\n\r\n/i\t安装更新服务\r\n/u{2}卸载更新服务\r\n/s\t静默模式");
96 }
97 else
98 {
99 int value = argInstall + argUninstall;
100
101 switch (value)
102 {
103 case 0:
104 MessageBox.Show("需要指定[/i]或者[/u]参数。");
105 break;
106 case 2:
107 MessageBox.Show("不能同时指定[/i]和[/u]参数。");
108 break;
109 default:
110 if (argInstall == 1)
111 {
112 Program.InstallServiceA(false, argSilent == 1);
113 }
114 else
115 {
116 Program.InstallServiceB(true, argSilent == 1);
117 }
118
119 break;
120 }
121 }
122 }
123
124 /// <summary>
125 /// 调用.NET Framework框架的InstallUtil.exe工具安装卸载服务,需要项目中包含服务安装类ServiceInstaller.cs
126 /// </summary>
127 private static void InstallServiceA(bool uninstall = false, bool slient = false)
128 {
129 try
130 {
131 var fileName = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "InstallUtil.exe");
132 var args = string.Format("{0}\"{1}\"", uninstall ? "/U " : string.Empty, Application.ExecutablePath);
133
134 using (var process = Process.Start(new ProcessStartInfo(fileName, args) { WindowStyle = ProcessWindowStyle.Hidden }))
135 {
136 process.WaitForExit(10000);
137 }
138
139 if (uninstall)
140 {
141 return;
142 }
143
144 fileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "sc.exe");
145 args = string.Format("start \"{0}\"", Program.SERVICE_NAME);
146
147 using (var process = Process.Start(new ProcessStartInfo(fileName, args) { WindowStyle = ProcessWindowStyle.Hidden }))
148 {
149 process.WaitForExit(10000);
150 }
151 }
152 catch (Exception ex)
153 {
154 MessageBox.Show(ex.ToString());
155 }
156 }
157
158 /// <summary>
159 /// 调用操作系统的sc.exe工具安装卸载服务
160 /// </summary>
161 private static void InstallServiceB(bool uninstall = false, bool slient = false)
162 {
163 try
164 {
165 var fileName = Path.Combine(Environment.SystemDirectory, "sc.exe");
166 var argsList = new List<string>();
167
168 if (!uninstall)
169 {
170 argsList.Add(string.Format("create {0} binPath= \"{1}\" start= auto", Program.SERVICE_NAME, Application.ExecutablePath));
171 argsList.Add(string.Format("start {0}", Program.SERVICE_NAME));
172 }
173 else
174 {
175 argsList.Add(string.Format("stop {0}", Program.SERVICE_NAME));
176 argsList.Add(string.Format("delete {0}", Program.SERVICE_NAME));
177 }
178
179 foreach (var args in argsList)
180 {
181 using (var process = Process.Start(new ProcessStartInfo(fileName, args) { WindowStyle = ProcessWindowStyle.Hidden }))
182 {
183 process.WaitForExit(10000);
184 }
185 }
186 }
187 catch (Exception ex)
188 {
189 MessageBox.Show(ex.ToString());
190 }
191 }
192 }
193 }
  • MainService.cs
 1 using System;
2 using System.ServiceProcess;
3
4 namespace WindowsServiceFormHybridSample
5 {
6 internal class MainService : ServiceBase
7 {
8 public MainService()
9 {
10 base.ServiceName = Program.SERVICE_NAME;
11 }
12
13 protected override void OnStart(string[] args)
14 {
15 try
16 {
17 //这里最好执行异步的方法
18 //否则会导致Windows的服务启动超时
19
20 //与MainForm执行相同的方法
21 MainWorker.Start();
22 }
23 catch (Exception ex)
24 {
25 //写入错误日志
26 }
27 }
28
29 protected override void OnStop()
30 {
31 try
32 {
33 MainWorker.Stop();
34 }
35 catch (Exception ex)
36 {
37 //写入错误日志
38 }
39 }
40 }
41 }
  • MainForm.cs
 1 using System;
2 using System.Windows.Forms;
3
4 namespace WindowsServiceFormHybridSample
5 {
6 public partial class MainForm : Form
7 {
8 public MainForm()
9 {
10 this.InitializeComponent();
11 this.button1.Text = "启动服务";
12 }
13
14 private void button1_Click(object sender, EventArgs e)
15 {
16 //与MainService执行相同的方法
17 try
18 {
19 if (this.button1.Text == "启动服务")
20 {
21 MainWorker.Start();
22 this.button1.Text = "停止服务";
23 return;
24 }
25
26 MainWorker.Stop();
27 this.button1.Text = "启动服务";
28 }
29 catch (Exception ex)
30 {
31 MessageBox.Show(ex.ToString());
32 }
33 }
34 }
35 }
  • MainWorker.cs
 1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4
5 namespace WindowsServiceFormHybridSample
6 {
7 internal class MainWorker
8 {
9 private static MainWorker _instance;
10 private static CancellationTokenSource _cancellationTokenSource;
11
12 private bool _isBusy;
13
14 public bool IsBusy { get => this._isBusy; }
15
16 static MainWorker()
17 {
18 MainWorker._instance = new MainWorker();
19 }
20
21 private async void DoWork(CancellationToken cancellationToken)
22 {
23 this._isBusy = true;
24
25 while (!cancellationToken.IsCancellationRequested)
26 {
27 await Task.Delay(1000);
28
29 //其他耗时任务
30 }
31
32 this._isBusy = false;
33 }
34
35 public static void Start()
36 {
37 if (MainWorker._instance.IsBusy)
38 {
39 throw new Exception("服务正在运行中。");
40 }
41
42 MainWorker._cancellationTokenSource = new CancellationTokenSource();
43 MainWorker._instance.DoWork(_cancellationTokenSource.Token);
44 }
45
46 public static void Stop()
47 {
48 if (MainWorker._cancellationTokenSource != null)
49 {
50 MainWorker._cancellationTokenSource.Cancel();
51 }
52 }
53 }
54 }
  • ServiceInstaller.cs
 1 using System.ComponentModel;
2 using System.Configuration.Install;
3 using System.ServiceProcess;
4
5 namespace WindowsServiceFormHybridSample
6 {
7 [RunInstaller(true)]
8 public class ServiceInstaller : Installer
9 {
10 public ServiceInstaller()
11 {
12 var ServiceProcessInstaller = new ServiceProcessInstaller()
13 {
14 Account = ServiceAccount.LocalSystem
15 };
16
17 var ServiceInstaller = new System.ServiceProcess.ServiceInstaller
18 {
19 ServiceName = Program.SERVICE_NAME,
20 StartType = ServiceStartMode.Automatic
21 };
22
23 base.Installers.AddRange(new Installer[] { ServiceProcessInstaller, ServiceInstaller });
24 }
25 }
26 }

C#开发一个混合Windows服务和Windows窗体的程序的更多相关文章

  1. VS2013创建Windows服务 || VS2015+Windows服务简易教程

    转自:https://www.cnblogs.com/no27/p/4849123.htmlhttps://blog.csdn.net/ly416/article/details/78860522 V ...

  2. [转帖]以Windows服务方式运行.NET Core程序

    以Windows服务方式运行.NET Core程序 原作者blog:https://www.cnblogs.com/guogangj/p/10093102.html 里面使用了NSSM 工具 但是自己 ...

  3. 连表查询都用Left Join吧 以Windows服务方式运行.NET Core程序 HTTP和HTTPS的区别 ASP.NET SignalR介绍 asp.net—WebApi跨域 asp.net—自定义轻量级ORM C#之23中设计模式

    连表查询都用Left Join吧   最近看同事的代码,SQL连表查询的时候很多时候用的是Inner Join,而我觉得对我们的业务而言,99.9%都应该使用Left Join(还有0.1%我不知道在 ...

  4. 玩转Windows服务系列——Windows服务小技巧

    伴随着研究Windows服务,逐渐掌握了一些小技巧,现在与大家分享一下. 将Windows服务转变为控制台程序 由于默认的Windows服务程序,编译后为Win32的窗口程序.我们在程序启动或运行过程 ...

  5. 玩转Windows服务系列——Windows服务启动超时时间

    最近有客户反映,机房出现断电情况,服务器的系统重新启动后,数据库服务自启动失败.第一次遇到这种情况,为了查看是不是断电情况导致数据库文件损坏,从客户的服务器拿到数据库的日志,进行分析. 数据库工作机制 ...

  6. Java魔法堂:以Windows服务的形式运行Java程序

    一.前言 由于防止维护人员误操作关闭Java控制台程序,因此决定将其改造为以Windows服务的形式运行.弄了一个上午总算搞定了,下面记录下来,以供日后查阅. 二.Java Service Wrapp ...

  7. 玩转Windows服务系列——Windows服务小技巧

    原文:玩转Windows服务系列——Windows服务小技巧 伴随着研究Windows服务,逐渐掌握了一些小技巧,现在与大家分享一下. 将Windows服务转变为控制台程序 由于默认的Windows服 ...

  8. Windows服务(system权限)程序显示界面与用户交互,Session0通知Session1里弹出对话框(真的很牛) good

    源码资源下载:http://download.csdn.net/detail/stony1980/4512984   1.VC2008中编写“Windows服务”(Windows Service)程序 ...

  9. 以Windows服务方式运行.NET Core程序

    在之前一篇博客<以Windows服务方式运行ASP.NET Core程序>中我讲述了如何把ASP.NET Core程序作为Windows服务运行的方法,而今,我们又遇到了新的问题,那就是: ...

  10. 动手开发一个名为“微天气”的微信小程序(上)

    引言:在智能手机软件的装机量中,天气预报类的APP排在比較靠前的位置.说明用户对天气的关注度非常高.由于人们不管是工作还是度假旅游等各种活动都须要依据自然天气来安排.跟着本文开发一个"微天气 ...

随机推荐

  1. three.js介绍和学习资料说明

    1.three.js能做什么 Three.js是基于原生WebGL封装运行的三维引擎,在所有WebGL引擎中,Three.js是国内文资料最多.使用最广泛的三维引擎.既然Threejs是一款WebGL ...

  2. java学习之旅(day.21)

    HTML 初识HTML HTML: Hyper Text Markup Language(超文本标记语言) 超文本包括文字.图片.音频.视频.动画等 W3C标准 W3C :World Wide Web ...

  3. 定了!AIRIOT新品发布会,6月6日北京见。

    随着物联网.大数据.AI技术的成熟和演进,智能物联网技术正在加速.深入渗透至各行业应用. AIRIOT物联网平台作为赋能数字经济发展和产业转型的数字基座,由航天科技控股集团股份有限公司(股票代码:00 ...

  4. 鸿蒙HarmonyOS实战-Stage模型(信息传递载体Want)

    前言 应用中的信息传递是为了实现各种功能和交互.信息传递可以帮助用户和应用之间进行有效的沟通和交流.通过信息传递,应用可以向用户传递重要的消息.通知和提示,以提供及时的反馈和指导.同时,用户也可以通过 ...

  5. Android 13 - Media框架(25)- OMXNodeInstance(二)

    关注公众号免费阅读全文,进入音视频开发技术分享群! 上一节我们了解了 OMXNodeInstance 的创建过程,以及 IOmx 服务和 OMXNodeInstance.OMX组件之间的联系.接下来我 ...

  6. C#WPF的多屏显示问题

    如果想让窗口在第二个屏幕中显示 public MainWindow() { InitializeComponent(); Screen[] _screens = Screen.AllScreens; ...

  7. .NET Core 项目Linux环境下生成二维码

    问题: 公司系统开发中,需要对企微授权链接进行二维码生成,然后向客户提供:当然,首当其冲想到的是使用ZXing.NET库进行实现,毕竟生成简单二维码也就那几句代码:然而,在本地环境中,一切都很正常,但 ...

  8. 代码界的超级英雄:GitHub的奇幻冒险之旅

    GitHub简介 GitHub是一个用于代码托管.版本控制和协作开发的平台.它于2008年2月8日由Chris Wanstrath.PJ Hyett和Tom Preston-Werner创立,目前由微 ...

  9. kubernetes的三种探针startupprobe,ReadinessProbe,LivenessProbe记录

    kubernetes的三种探针 startupprobe: k8s1.16版本后新加的探测方式,用于判断容器内应用程序是否已经启动,如果配置了startuprobe,就会先禁用其他的探测,直到它成功为 ...

  10. zabbix笔记_004 监控Windows主机

    zabbix监控Windows主机 zabbix for windows 安装包下载地址: https://www.zabbix.com/cn/download_agents#tab:34 下载zab ...