C#开发一个混合Windows服务和Windows窗体的程序

很多时候,我们希望服务程序可以直接运行,或者可以响应一些参数,这时候,混合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 }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/739033.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

喜闻乐见的颓柿子

从洛谷搬来的高三时做的题,主要目的是测试既然一开始的时候把 dp 方程写了,第三问不如就尝试一下。 接下来我们考虑一下对 $ a_n=a_{n-1}+(n-1)a_{n-2} $ 找出通项。 首先有 $ a_1=1,,a_2=2, $ 化一下式子: \[a_n=a_{n-1}+(n-1)a_{n-2} \]即 \[\frac{a_n}{(n-1)!}=\frac{a_…

safe_shellcode

[HNCTF 2022 Week1]safe_shellcode 思路 下载附件,名称为shellcoder,很明显的shellcode提示。 判断题目解法可能是shellcode利用 常规流程查看保护发现存在NX保护,但是让我们以shellcode的思路去解题,则可能存在修改权限的函数mprotect ida打开分析分析代码,发现存在一个m…

环境变量的一些作用

环境变量的一些作用 接上篇:如果希望批量执行一组cmd指令,而不是像之前那样一次执行一个cmd指令,那么可以通过批处理来实现。将多条cmd指令存放在一个以.bat为扩展名的文件里,然后执行这个文件,这个文件就叫批处理文件。 在d:\aaa文件夹里新建demo.bat编辑,记得把文件扩展名…

Jitsi搭建流程

系统要求:Debian 11 (DD 脚本 非必需 DD,用原来的系统也 OK)。安装好宝塔(需要用到https证书)、Nninx前置: sudo -i # 切换到 root 用户apt update -y # 升级 packagesapt install wget curl sudo vim git -y # Debian 系统比较干净,安装常用的软件 1:安装 Docker 环…

代码随想录算法训练营第五十三天 | 739.每日温度 496.下一个更大的元素I 503.下一个更大的元素II

739.每日温度 题目链接 文章讲解 视频讲解 单调栈适合的场景:求当前元素左面或右面第一个比它大或小的元素单调栈里存什么元素 只要存下标就可以了,比较元素时可以通过下标取元素 单调栈是单调增还是单调减(从栈顶到栈底) 使用单调增的单调栈解题步骤:遍历数组,当栈空时直…

积分中值定理的证明1

积分中值定理的证明如下:

24年读书清单

最近整理的读书清单,今年的读书就从里面挑选了;

【Azure Blob】关闭Blob 匿名访问,iOS Objective-C SDK连接Storage Account报错

问题描述 iOS Objective-C 应用,连接Azure Storage Account, 根据官网Example代码,在没有关闭Storage Account的匿名访问时,程序正常运行。但是,只要关闭了匿名访问,上传blob到Container中,就会报错:Public access is not permitted on this storage account 问题解答…

模拟集成电路设计系列博客——9.4 ESD保护单元

模拟集成电路设计 9.4 ESD保护单元 ESD,即electrostatic discharge(静电放电)是集成电路设计中的一个经典问题。通常在电路的生产和使用过程中,很容易由于各种原因积累大量的静电电荷,一旦产生静电放电,极高的静电电压(可能\(>1000V\))会对电路产生不可逆的破坏。因…

函数进阶应用1

Excel函数进阶VLOOKUP函数 返回多列结果 通过混合引用,match函数查找以及Vlookup函数,使用填充柄拖拽行列填充 操作演示:说明首先我们可以写一个普通的VLOOKUP函数通过match函数找到要填充的符合条件的内容所在单元格的位置在拖拽时,分析哪些需要绝对引用,哪些需要混合引用…

Linux学习前置,红帽Linux系统安装

前置: 下载:VMware Workstation Pro 17,以及所需要使用的Linux版本;安装VMware Workstation; 安装: 进入VMware后选择新建虚拟机: 选择Linux和red hat 9版本; 选择使用网络地址转换 接下来选择推荐的设置就好; 选择新建磁盘 虚拟机创建好后在设备一栏里单击CD/DVD(SAT…

C++:win11下的VScode构建百度Comate

一. VScode的安装官网下载地址:https://code.visualstudio.com/ (选择好对应的系统版本) 汉化:安装完软件后,打开软件界面的"扩展商店",搜索Chinese,下载相应的插件,如下图:二. 配置C++开发环境VSCode只是一个高级的编辑器,可以用来写C++代码,不能直接编译…

学习笔记(0):重拾Halcon

目录前言教学视频 前言 了解我的人可能知道,我其实很想回去全职做外贸,但是大环境不好,淘宝做了3个月,1688做了1个月。我只能说销量很惨淡。现在打算还是老老实实上班去了。 教学视频 我之前找一个B站UP主,买了一下他的教学视频。600块钱,总共有40集,大概10个小时。大概…

Golang channel底层是如何实现的?(深度好文)

Go语言为了方便使用者,提供了简单、安全的协程数据同步和通信机制,channel。那我们知道channel底层是如何实现的吗?今天k哥就来聊聊channel的底层实现原理。同时,为了验证我们是否掌握了channel的实现原理,本文也收集了channel的高频面试题,理解了原理,面试题自然不在话…

初识XML

一.XML概述XML,Extensible Markup Language,扩展性标识语言具体作用为:(1)可作为一种简单的数据库,存储并检索数据;(2)传输约定格式的文件;(3)做软件的配置文件。【配置文件:保存软件设置的文件】HTML和XML的区别:HTML标签不能自定义,XML标签只能自定义。 HTML语法要…

本地Windows10怎样配置免安装版本MySQL?

下载MySQL免安装压缩包下载地址:https://downloads.mysql.com/archives/community/ 解压安装1、接下来我们解压文件夹,这时我们解压的文件夹是没有my.ini文件和data目录,这时我们需要自己创建my.ini文件,data文件后期回自动生成 2、新建配置my.ini文件,并添加配置信息,如下…

8、IDEA集成Git

8.1、配置Git忽略文件 8.1.1、忽略文件的原因 在使用 IDE 工具时,会自动生成一些和项目源码无关的文件,所以可以让 Git 忽略这些文件。 此外,把这些无关文件忽略掉,还能够屏蔽不同 IDE 工具之间的差异。 8.1.2、创建忽略规则文件如上图所示,在用户家目录下创建了一个忽略规…

时间序列分析专题——指数平滑模型

指数平滑法模型,分为季节性模型和非季节性模型。季节性模型只有在为活动数据集定义了周期时才可用。 本章只理论性地介绍这六种指数平滑法模型,让学习者在论文的应用中有话可写。 在具体实现中,SPSS会自动识别并给定一种最好的模型。 目录一、简单指数平滑法1.模型介绍2.关于…

Blazor 逐键搜索并动态反馈到url

Blazor 逐键搜索并动态反馈到url 源码 前言: 今天打开了 spotify 网页版找歌, 突然发现这个功能很抓眼球,于是试试blazor能不能模仿一下.1. 节省时间,直接用模板开搞 新建项目,使用bb模板, 命名为 b22dynamicURL2. 运行一次,看看效果 显示如下模板站点,就说明你前面的操作都对了…

集团数字化建设总体规划

1、数字生态体系建设规划 体系规划整体思路 从战略出发,描绘企业愿景蓝图,结合领先实践,设计方案与实施路线 通过体系规划和建设,助力业务发展,支撑战略落地 数字化助力上下贯通的高效管理与横向协同的业务经营 建设后援集中平台,实现高效高质集中作业、交叉销售,产生规…