当前位置:   article > 正文

【C#】Windows服务(Service)安装及启停_windows service

windows service

目录

一、创作背景

二、问题解决

2.1 安装Windows service服务

2.2 主方法Main()主方法改写

2.3 安装service服务/卸载service服务

2.4 服务启停

2.5 服务调用运行

2.6 关于权限的提升

三、资源分享

3.1 引入组件

3.2 新手使用


一、前言

我能抽象出整个世界,但是我不能抽象你。 想让你成为私有常量,这样外部函数就无法访问你。 又想让你成为全局常量,这样在我的整个生命周期都可以调用你。 可惜世上没有这样的常量,我也无法定义你,因为你在我心中是那么的具体。

哈喽大家好,本专栏为【项目实战】专栏,有别于【底层库】专栏,我们可以发现增加 了『问题描述』、『项目展示』章节,十分符合项目开发流程,让读者更加清楚项目解决的问题、以及产品能够达到的效果。本专栏收纳项目开发过程的解决方案,是我项目开发相对成熟、可靠方法的提炼,我将这些问题的解决思路梳理,撰写本文分享给大家,大家遇到类似问题,可按本文方案处理。

本专栏会持续更新,不断完善,专栏文章关联性较弱(文章之间依赖性较弱,没有阅读顺序)。大家有任何问题,可以私信我。如果您对本专栏感兴趣,欢迎关注吧,我将带你用最简洁的代码,实现复杂的功能。
 

二、创作背景

今天,我们完成了一个应用程序的开发,我们要将其设置成开机自启程序,继上篇文章介绍有两种方式。

一种,开机启动目录下创建应有程序的快捷方式;

另一种,写Windows服务;我们现在采用“服务方式自启程序”。

因为用户不具备管理员权限,为了安全考虑,我们方便监控管理。最终确认方案二。

先说说我们面临的问题,或者说是待解决的问题:

①安装服务,通过咱新开发的C#应用软件安装,不希望借助第三方工具(installutil.exe),因为面向人群非科班出身,操作越简单越好。

②开启、停止服务,不希望出现【黑色命令框】弹窗,看着就烦,用户体验不好。

③不希望出现Windows权限不够的提示框,因为用户是普通用户,不具备“管理员操作权限”,本程序有一些敏感操作,有需要最高权限,不要出现“权限不够,哪怕是否确认授权”消息提示框。

怎么样?需求是不是很变态,需要同时满足以上三点,网上很多技术教程已经不适用。变态需求即是挑战,下面我将介绍本文解决方案,彻底解决以上问题,希望可以帮助面临同样困状的你们。

三、解决方案

3.1 安装Windows service服务

点击【是否开机自启】,安装Windows service服务,并实现服务启停。

3.2 主方法Main()主方法改写

Program.cs文件Main()有两种运行方式

①客户端方式启动,最常用的方式,相当于鼠标双击可执行程序

②服务service方式启动,采用Windows service安装,默认含有参数。

因此我们可以通过参数args区分两种方式,如下图代码所示,其中客户端启动参用“单例模式”,我这里使用了全局信号量卡控,一次只能运行一个程序客户端。

  1. static void Main(string[] args)
  2. {
  3. //ProcessExec processExec = new ProcessExec();
  4. //string aa = "";
  5. //processExec.MediaServiceStart("", out aa);
  6. if (args.Length > 0)
  7. {
  8. var serviceName = Const.ct_strServiceName;
  9. var host = HostFactory.New(x =>
  10. {
  11. x.EnableShutdown();
  12. x.Service<ServiceCtrl>();
  13. x.RunAsLocalSystem();
  14. x.SetDescription(Const.ct_strProjName);
  15. x.SetDisplayName(serviceName);
  16. x.SetServiceName(serviceName);
  17. });
  18. LogHelper.Info("***********主程序运行(服务方式)**********");
  19. host.Run();
  20. }
  21. else
  22. {
  23. bool createNew;
  24. //使用用互斥量(System.Threading.Mutex)只运行一个实列
  25. using (System.Threading.Mutex mutex = new System.Threading.Mutex(true, Application.ProductName, out createNew))
  26. {
  27. if (createNew)
  28. {
  29. Application.EnableVisualStyles();
  30. Application.SetCompatibleTextRenderingDefault(false);
  31. LogHelper.Info("***********主程序运行(客户端方式)**********");
  32. Application.Run(new FormMain());
  33. }
  34. else
  35. {
  36. System.Threading.Thread.Sleep(1000);
  37. System.Environment.Exit(1);
  38. }
  39. }
  40. }
  41. }

3.3 安装service服务/卸载service服务

①添加新项-->Windows服务--AutoPlayerService.cs

 ②右键空白处,【添加安装程序】,它会自动创建ProjectInstaller.cs文件,打开该文件有两个组件serviceProcessInstaller1、serviceInstaller1

网上大部分文章,教你们在属性中填写 服务信息,这种方式存在缺陷,常常服务不能够自动注入,需要借助第三方工具写入注册表,我教你们另外一种方式,如下图所示:

 ③我的方式,通过使用底层入口实现。

添加以下4行代码。

  1. [DllImport("kernel32.dll")]
  2. public static extern int WinExec(string CmdLine, int ShowCmd);
  3. [DllImport("shell32.dll")]
  4. public static extern int ShellExecute(IntPtr hWnd, string Operation, string FileName,
  5. string Parameters, string Directory, int ShowCmd);

 ④实现方法

安装服务

  1. string cmd = Application.ExecutablePath;
  2. cmd += " install";
  3. WinExec(cmd, 1);
  4. MessageBox.Show("服务安装完毕!");
  5. ShellExecute(this.Handle, "open", "Services.msc", "", "", 1);

卸载方法

  1. ServiceStop(Const.ct_strServiceName);//停止服务
  2. string cmd = Application.ExecutablePath;
  3. cmd += " uninstall";
  4. WinExec(cmd, 1);
  5. MessageBox.Show("服务卸载完毕!");

3.4 服务启停

①创建服务类ServiceCtrl.cs ,实现接口ServiceControl。

  1. public class ServiceCtrl : ServiceControl
  2. {
  3. MainTimer m_MainTimer;
  4. public ServiceCtrl()
  5. {
  6. try
  7. {
  8. m_MainTimer = new MainTimer(21600);//6小时检查一次
  9. m_MainTimer.InitTimer();
  10. }
  11. catch (Exception err)
  12. {
  13. LogHelper.Error("初始化服务发生错误:" + err.ToString());
  14. }
  15. }
  16. /// <summary>
  17. /// 服务启动时执行
  18. /// </summary>
  19. /// <param name="c"></param>
  20. /// <returns></returns>
  21. public bool Start(Topshelf.HostControl c)
  22. {
  23. try
  24. {
  25. m_MainTimer.StartTimer();
  26. string l_strMsgError = "";
  27. if (SysParameter.shutdown_tag == "T")
  28. {
  29. ProcessExec exec = new ProcessExec();
  30. exec.StartShutDown(out l_strMsgError);
  31. }
  32. return true;
  33. }
  34. catch (Exception err)
  35. {
  36. LogHelper.Error("启动服务发生错误:" + err.ToString());
  37. return false;
  38. }
  39. }
  40. /// <summary>
  41. /// 服务关闭时执行
  42. /// </summary>
  43. /// <param name="c"></param>
  44. /// <returns></returns>
  45. public bool Stop(Topshelf.HostControl c)
  46. {
  47. try
  48. {
  49. m_MainTimer.StopTimer();
  50. return true;
  51. }
  52. catch (Exception err)
  53. {
  54. LogHelper.Error("停止服务发生错误:" + err.ToString());
  55. return false;
  56. }
  57. }
  58. }

②服务启动

  1. //启动服务
  2. public static void ServiceStart(string serviceName)
  3. {
  4. using (ServiceController control = new ServiceController(serviceName))
  5. {
  6. if (control.Status == ServiceControllerStatus.Stopped)
  7. {
  8. control.Start();
  9. }
  10. }
  11. }

③服务停止

  1. //停止服务
  2. public static void ServiceStop(string serviceName)
  3. {
  4. using (ServiceController control = new ServiceController(serviceName))
  5. {
  6. if (control.Status == ServiceControllerStatus.Running)
  7. {
  8. control.Stop();
  9. }
  10. }
  11. }

④判断服务是否在运行状态(可选)

  1. //判断服务状态
  2. public static bool IsServiceRuning(string serviceName)
  3. {
  4. ServiceController[] services = ServiceController.GetServices();
  5. foreach (ServiceController sc in services)
  6. {
  7. if (sc.ServiceName.ToLower() == serviceName.ToLower())
  8. {
  9. if (sc.Status == ServiceControllerStatus.Running)
  10. {
  11. return true;
  12. }
  13. else
  14. {
  15. return false;
  16. }
  17. }
  18. }
  19. return false;
  20. }

3.5 服务调用运行

以上基本完成了服务的开发,关于服务调用我给一个案例:我这里鼠标右键点击,自动启动服务,开始执行主方法。

  1. //启动服务(Windows服务运行)
  2. private void BTN_ServiceRun_Click(object sender, EventArgs e)
  3. {
  4. m_service.Start(null);
  5. try
  6. {
  7. BTN_ServiceRun.Enabled = false;
  8. //ToDo:采用服务式,无dos黑框
  9. //string cmd = Const.ct_strServiceName;
  10. //cmd = "net start " + cmd;
  11. //FormSet.WinExec(cmd, 1);
  12. FormSet.ServiceStart(Const.ct_strServiceName);
  13. if (DataOperation.UpdateStateType("正在运行"))
  14. {
  15. GC_Main.DataSource = DataOperation.SelectData();
  16. GC_Main.RefreshDataSource();
  17. }
  18. }
  19. finally
  20. {
  21. BTN_ServiceStop.Enabled = true;
  22. }
  23. }
  24. //停止服务(Windows服务停止)
  25. private void BTN_ServiceStop_Click(object sender, EventArgs e)
  26. {
  27. m_service.Stop(null);
  28. try
  29. {
  30. BTN_ServiceStop.Enabled = false;
  31. FormSet.ServiceStop(Const.ct_strServiceName);
  32. if (DataOperation.UpdateStateType("已停止"))
  33. {
  34. GC_Main.DataSource = DataOperation.SelectData();
  35. GC_Main.RefreshDataSource();
  36. }
  37. }
  38. finally
  39. {
  40. BTN_ServiceRun.Enabled = true;
  41. }
  42. }

3.6 关于权限的提升

有时,你或许没有管理员权限,毕竟公司电脑嘛,每次服务开启都会有弹窗提示“权限不足”,很是厌烦,而且我们还要再次人工干预,这时你需要再程序中增加用户权限配置清单。

创建app.manifest应用程序清单文件。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  3. <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  4. <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
  5. <security>
  6. <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
  7. <!-- UAC 清单选项
  8. 如果想要更改 Windows 用户帐户控制级别,请使用
  9. 以下节点之一替换 requestedExecutionLevel 节点。n
  10. <requestedExecutionLevel level="asInvoker" uiAccess="false" />
  11. <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
  12. <requestedExecutionLevel level="highestAvailable" uiAccess="false" />
  13. 指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
  14. 如果你的应用程序需要此虚拟化来实现向后兼容性,则删除此
  15. 元素。
  16. -->
  17. <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
  18. </requestedPrivileges>
  19. </security>
  20. </trustInfo>
  21. <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  22. <application>
  23. <!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
  24. Windows 版本的列表。取消评论适当的元素,
  25. Windows 将自动选择最兼容的环境。 -->
  26. <!-- Windows Vista -->
  27. <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
  28. <!-- Windows 7 -->
  29. <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
  30. <!-- Windows 8 -->
  31. <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
  32. <!-- Windows 8.1 -->
  33. <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
  34. <!-- Windows 10 -->
  35. <!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
  36. </application>
  37. </compatibility>
  38. <!-- 指示该应用程序可以感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
  39. 自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
  40. 选择加入。选择加入此设置的 Windows 窗体应用程序(目标设定为 .NET Framework 4.6 )还应
  41. 在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。-->
  42. <!--
  43. <application xmlns="urn:schemas-microsoft-com:asm.v3">
  44. <windowsSettings>
  45. <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
  46. </windowsSettings>
  47. </application>
  48. -->
  49. <!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
  50. <!--
  51. <dependency>
  52. <dependentAssembly>
  53. <assemblyIdentity
  54. type="win32"
  55. name="Microsoft.Windows.Common-Controls"
  56. version="6.0.0.0"
  57. processorArchitecture="*"
  58. publicKeyToken="6595b64144ccf1df"
  59. language="*"
  60. />
  61. </dependentAssembly>
  62. </dependency>
  63. -->
  64. </assembly>

<requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />这句代码帮你以管理员身份运行,这样就不会弹窗了。

四、资源分享

4.1 引入组件

你需要引入最强大的组件库 Geyc.Utils.dll,我已经本文介绍的方法封装了,本组件包含大量实战项目的操作类、底层库、UI控件等,定期更新补充。

倘若你在项目引用过程中,发现组件中的错误,或是库不支持,或是组件适配环境性问题,请联系我修改封装底层库文件。

链接:https://pan.baidu.com/s/1sEO9aH2_re7Xwa-WDL_V7w?pwd=l6d0 
提取码:l6d0

4.2 新手使用

我已将方法封装,你也只需要几行代码就可以实现本文阐述的功能。

  1. //判断服务是否运行
  2. IsServiceRuning(string serviceName)
  3. //安装服务
  4. InstallService(string serviceFilePath)
  5. //卸载服务
  6. UninstallService(string serviceFilePath)
  7. //启动服务
  8. ServiceStart(string serviceName)
  9. //停止服务
  10. ServiceStop(string serviceName)

4.3 底层库源码

组件库 Geyc.Utils.dll中,你使用到的方法如下:

  1. [DllImport("kernel32.dll")]
  2. public static extern int WinExec(string CmdLine, int ShowCmd);
  3. [DllImport("shell32.dll")]
  4. public static extern int ShellExecute(IntPtr hWnd, string Operation, string FileName,
  5. string Parameters, string Directory, int ShowCmd);
  6. #region 开机自启项
  7. string serviceFilePath = Application.ExecutablePath;
  8. private void checkBox_AutoRun_CheckedChanged(object sender, EventArgs e)
  9. {
  10. if (!m_bStart) return;
  11. if (checkBox_AutoRun.Checked) //设置开机自启动
  12. {
  13. string cmd = Application.ExecutablePath;
  14. cmd += " install";
  15. WinExec(cmd, 1);
  16. ShellExecute(this.Handle, "open", "Services.msc", "", "", 1);
  17. FrmTips.ShowTipsSuccess(this, "开机自启已启动!");
  18. AppConfig.SetValue("auto_run", "T");
  19. }
  20. else //取消开机自启动
  21. {
  22. if (this.IsServiceExisted(Const.ct_strServiceName))
  23. {
  24. ServiceStop(Const.ct_strServiceName);//停止服务
  25. string cmd = Application.ExecutablePath;
  26. cmd += " uninstall";
  27. WinExec(cmd, 1);
  28. FrmTips.ShowTipsSuccess(this, "开机自启已终止!");
  29. }
  30. AppConfig.SetValue("auto_run", "F");
  31. }
  32. }
  33. //判断服务是否存在
  34. private bool IsServiceExisted(string serviceName)
  35. {
  36. ServiceController[] services = ServiceController.GetServices();
  37. foreach (ServiceController sc in services)
  38. {
  39. if (sc.ServiceName.ToLower() == serviceName.ToLower())
  40. {
  41. return true;
  42. }
  43. }
  44. return false;
  45. }
  46. #region 安装服务/卸载服务
  47. //安装服务
  48. private void InstallService(string serviceFilePath)
  49. {
  50. using (AssemblyInstaller installer = new AssemblyInstaller())
  51. {
  52. installer.UseNewContext = true;
  53. installer.Path = serviceFilePath;
  54. IDictionary savedState = new Hashtable();//空参数
  55. installer.Install(savedState);
  56. installer.Commit(savedState);
  57. }
  58. }
  59. //卸载服务
  60. private void UninstallService(string serviceFilePath)
  61. {
  62. using (AssemblyInstaller installer = new AssemblyInstaller())
  63. {
  64. installer.UseNewContext = true;
  65. installer.Path = serviceFilePath;
  66. installer.Uninstall(null);
  67. }
  68. }
  69. #endregion
  70. //判断服务状态
  71. public static bool IsServiceRuning(string serviceName)
  72. {
  73. ServiceController[] services = ServiceController.GetServices();
  74. foreach (ServiceController sc in services)
  75. {
  76. if (sc.ServiceName.ToLower() == serviceName.ToLower())
  77. {
  78. if (sc.Status == ServiceControllerStatus.Running)
  79. {
  80. return true;
  81. }
  82. else
  83. {
  84. return false;
  85. }
  86. }
  87. }
  88. return false;
  89. }
  90. //启动服务
  91. public static void ServiceStart(string serviceName)
  92. {
  93. using (ServiceController control = new ServiceController(serviceName))
  94. {
  95. if (control.Status == ServiceControllerStatus.Stopped)
  96. {
  97. control.Start();
  98. }
  99. }
  100. }
  101. //停止服务
  102. public static void ServiceStop(string serviceName)
  103. {
  104. using (ServiceController control = new ServiceController(serviceName))
  105. {
  106. if (control.Status == ServiceControllerStatus.Running)
  107. {
  108. control.Stop();
  109. }
  110. }
  111. }
  112. #endregion

 五、系列文章

C#项目--业务单据号生成器(定义规则、自动编号、流水号)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129129787

C#项目--开始日期结束日期范围计算(上周、本周、明年、前年等)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/129040663

C#项目--数据实体类使用
本文链接:https://blog.csdn.net/youcheng_ge/article/details/128816638

C#项目--单据审批流方案
本文链接:https://blog.csdn.net/youcheng_ge/article/details/128972545

C#项目--二维码标签制作及打印(完整版)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126884228

C#项目--条码管理操作手册
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126589496

C#项目--WebAPI发布和IIS部署,及异常处理
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126539836

C#项目--提高编程效率,代码自动生成
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126890673

C#项目--提高编程效率,Excel数据导入工具
本文链接:https://blog.csdn.net/youcheng_ge/article/details/126427323

C#项目--Windows服务(Service)安装及启停方案
本文链接:https://blog.csdn.net/youcheng_ge/article/details/124053794

C#项目--穿透Session隔离,服务调用外部程序(无窗体界面解决)
本文链接:https://blog.csdn.net/youcheng_ge/article/details/124053033

C#项目--任务计划实现,使用Quartz类
本文链接:https://blog.csdn.net/youcheng_ge/article/details/123667723

C#项目--《周计划管理关于产前准备模块》解决方案20200203
本文链接:https://blog.csdn.net/youcheng_ge/article/details/122919543

C#项目--项目中,源码解析的正则表达式
本文链接:https://blog.csdn.net/youcheng_ge/article/details/118337074

C#项目--如何获取文件版本和MD5值
本文链接:https://blog.csdn.net/youcheng_ge/article/details/112513871

C#项目--如何测试网络是否连通
本文链接:https://blog.csdn.net/youcheng_ge/article/details/110137288

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/171595
推荐阅读
相关标签
  

闽ICP备14008679号