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 + "支持的命令行参数:rnrn/it安装更新服务rn/u{2}卸载更新服务rn/st静默模式");  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 }

发表评论

评论已关闭。

相关文章