关键词搜索

源码搜索 ×
×

C# Windows Service 项目支持安装和卸载服务含GUI

发布2018-06-25浏览902次

详情内容

Windows Service 项目支持安装和卸载服务,本篇内容主要尝试实现服务的安装和卸载。实现过程中已支持使用此服务进行任何exe的启动,只要配置好exe和log的全路径记录。

创建windows service项目

修改项目名称,然后确定。

添加安装控件并修改用户权限

在新建项目的文件中打开“ProjectInstaller.cs”,右键添加“添加安装程序”,然后得到如下所示界面。

修改处理安装控件的用户级别,改成“LogSystem”方便访问:

修改服务安装控件的名称,让它看起来是有意义的windows系统服务:

编写安装和卸载bat脚本

安装:

cd %~dp0

SET regpath=%~dp0

%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe  %regpath%\RTVSWindowsService.exe

Net Start RTVSWindowsService

::echo "开机自动运行"

sc config RTVSWindowsService start= AUTO

::echo "允许服务与桌面交互"

sc config RTVSWindowsService type= interact type= own

pause

卸载:

cd %~dp0

SET regpath=%~dp0

%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /u %regpath%\RTVSWindowsService.exe

pause

启动哪个exe使用XML配置文件来决定
此时,我们需要编写配置文件和读取配置文件的工具类,工具类如下:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Xml;
  8. namespace RTVSWindowsService
  9. {
  10. /// <summary>
  11. /// 系统XML配置属性
  12. /// </summary>
  13. class SystemConfig
  14. {
  15. /// <summary>
  16. /// 得到工作文件夹路径
  17. /// </summary>
  18. /// <returns></returns>
  19. public static string GetWorkDirectory()
  20. {
  21. try
  22. {
  23. return Path.GetDirectoryName(typeof(SystemConfig).Assembly.Location);
  24. }
  25. catch
  26. {
  27. return AppDomain.CurrentDomain.BaseDirectory;
  28. }
  29. }
  30. /// <summary>
  31. /// 推断字符串是否为空串
  32. /// </summary>
  33. /// <param name="szString">目标字符串</param>
  34. /// <returns>true:为空串;false:非空串</returns>
  35. private static bool IsEmptyString(string szString)
  36. {
  37. if (szString == null)
  38. return true;
  39. if (szString.Trim() == string.Empty)
  40. return true;
  41. return false;
  42. }
  43. /// <summary>
  44. /// 创建一个制定根节点名的XML文件
  45. /// </summary>
  46. /// <param name="szFileName">XML文件</param>
  47. /// <param name="szRootName">根节点名</param>
  48. /// <returns>bool</returns>
  49. private static bool CreateXmlFile(string szFileName, string szRootName)
  50. {
  51. if (szFileName == null || szFileName.Trim() == "")
  52. return false;
  53. if (szRootName == null || szRootName.Trim() == "")
  54. return false;
  55. XmlDocument clsXmlDoc = new XmlDocument();
  56. clsXmlDoc.AppendChild(clsXmlDoc.CreateXmlDeclaration("1.0", "GBK", null));
  57. clsXmlDoc.AppendChild(clsXmlDoc.CreateNode(XmlNodeType.Element, szRootName, ""));
  58. try
  59. {
  60. clsXmlDoc.Save(szFileName);
  61. return true;
  62. }
  63. catch
  64. {
  65. return false;
  66. }
  67. }
  68. /// <summary>
  69. /// 从XML文件获取相应的XML文档对象
  70. /// </summary>
  71. /// <param name="szXmlFile">XML文件</param>
  72. /// <returns>XML文档对象</returns>
  73. private static XmlDocument GetXmlDocument(string szXmlFile)
  74. {
  75. if (IsEmptyString(szXmlFile))
  76. return null;
  77. if (!File.Exists(szXmlFile))
  78. return null;
  79. XmlDocument clsXmlDoc = new XmlDocument();
  80. try
  81. {
  82. clsXmlDoc.Load(szXmlFile);
  83. }
  84. catch
  85. {
  86. return null;
  87. }
  88. return clsXmlDoc;
  89. }
  90. /// <summary>
  91. /// 将XML文档对象保存为XML文件
  92. /// </summary>
  93. /// <param name="clsXmlDoc">XML文档对象</param>
  94. /// <param name="szXmlFile">XML文件</param>
  95. /// <returns>bool:保存结果</returns>
  96. private static bool SaveXmlDocument(XmlDocument clsXmlDoc, string szXmlFile)
  97. {
  98. if (clsXmlDoc == null)
  99. return false;
  100. if (IsEmptyString(szXmlFile))
  101. return false;
  102. try
  103. {
  104. if (File.Exists(szXmlFile))
  105. File.Delete(szXmlFile);
  106. }
  107. catch
  108. {
  109. return false;
  110. }
  111. try
  112. {
  113. clsXmlDoc.Save(szXmlFile);
  114. }
  115. catch
  116. {
  117. return false;
  118. }
  119. return true;
  120. }
  121. /// <summary>
  122. /// 获取XPath指向的单一XML节点
  123. /// </summary>
  124. /// <param name="clsRootNode">XPath所在的根节点</param>
  125. /// <param name="szXPath">XPath表达式</param>
  126. /// <returns>XmlNode</returns>
  127. private static XmlNode SelectXmlNode(XmlNode clsRootNode, string szXPath)
  128. {
  129. if (clsRootNode == null || IsEmptyString(szXPath))
  130. return null;
  131. try
  132. {
  133. return clsRootNode.SelectSingleNode(szXPath);
  134. }
  135. catch
  136. {
  137. return null;
  138. }
  139. }
  140. /// <summary>
  141. /// 获取XPath指向的XML节点集
  142. /// </summary>
  143. /// <param name="clsRootNode">XPath所在的根节点</param>
  144. /// <param name="szXPath">XPath表达式</param>
  145. /// <returns>XmlNodeList</returns>
  146. private static XmlNodeList SelectXmlNodes(XmlNode clsRootNode, string szXPath)
  147. {
  148. if (clsRootNode == null || IsEmptyString(szXPath))
  149. return null;
  150. try
  151. {
  152. return clsRootNode.SelectNodes(szXPath);
  153. }
  154. catch
  155. {
  156. return null;
  157. }
  158. }
  159. /// <summary>
  160. /// 创建一个XmlNode并加入到文档
  161. /// </summary>
  162. /// <param name="clsParentNode">父节点</param>
  163. /// <param name="szNodeName">结点名称</param>
  164. /// <returns>XmlNode</returns>
  165. private static XmlNode CreateXmlNode(XmlNode clsParentNode, string szNodeName)
  166. {
  167. try
  168. {
  169. XmlDocument clsXmlDoc = null;
  170. if (clsParentNode.GetType() != typeof(XmlDocument))
  171. clsXmlDoc = clsParentNode.OwnerDocument;
  172. else
  173. clsXmlDoc = clsParentNode as XmlDocument;
  174. XmlNode clsXmlNode = clsXmlDoc.CreateNode(XmlNodeType.Element, szNodeName, string.Empty);
  175. if (clsParentNode.GetType() == typeof(XmlDocument))
  176. {
  177. clsXmlDoc.LastChild.AppendChild(clsXmlNode);
  178. }
  179. else
  180. {
  181. clsParentNode.AppendChild(clsXmlNode);
  182. }
  183. return clsXmlNode;
  184. }
  185. catch
  186. {
  187. return null;
  188. }
  189. }
  190. /// <summary>
  191. /// 设置指定节点中指定属性的值
  192. /// </summary>
  193. /// <param name="parentNode">XML节点</param>
  194. /// <param name="szAttrName">属性名</param>
  195. /// <param name="szAttrValue">属性值</param>
  196. /// <returns>bool</returns>
  197. private static bool SetXmlAttr(XmlNode clsXmlNode, string szAttrName, string szAttrValue)
  198. {
  199. if (clsXmlNode == null)
  200. return false;
  201. if (IsEmptyString(szAttrName))
  202. return false;
  203. if (IsEmptyString(szAttrValue))
  204. szAttrValue = string.Empty;
  205. XmlAttribute clsAttrNode = clsXmlNode.Attributes.GetNamedItem(szAttrName) as XmlAttribute;
  206. if (clsAttrNode == null)
  207. {
  208. XmlDocument clsXmlDoc = clsXmlNode.OwnerDocument;
  209. if (clsXmlDoc == null)
  210. return false;
  211. clsAttrNode = clsXmlDoc.CreateAttribute(szAttrName);
  212. clsXmlNode.Attributes.Append(clsAttrNode);
  213. }
  214. clsAttrNode.Value = szAttrValue;
  215. return true;
  216. }
  217. #region"配置文件的读取和写入"
  218. private static string CONFIG_FILE = "SystemConfig.xml";
  219. /// <summary>
  220. /// 读取指定的配置文件里指定Key的值
  221. /// </summary>
  222. /// <param name="szKeyName">读取的Key名称</param>
  223. /// <param name="szDefaultValue">指定的Key不存在时,返回的值</param>
  224. /// <returns>Key值</returns>
  225. public static int GetConfigData(string szKeyName, int nDefaultValue)
  226. {
  227. string szValue = GetConfigData(szKeyName, nDefaultValue.ToString());
  228. try
  229. {
  230. return int.Parse(szValue);
  231. }
  232. catch
  233. {
  234. return nDefaultValue;
  235. }
  236. }
  237. /// <summary>
  238. /// 读取指定的配置文件里指定Key的值
  239. /// </summary>
  240. /// <param name="szKeyName">读取的Key名称</param>
  241. /// <param name="szDefaultValue">指定的Key不存在时,返回的值</param>
  242. /// <returns>Key值</returns>
  243. public static float GetConfigData(string szKeyName, float fDefaultValue)
  244. {
  245. string szValue = GetConfigData(szKeyName, fDefaultValue.ToString());
  246. try
  247. {
  248. return float.Parse(szValue);
  249. }
  250. catch
  251. {
  252. return fDefaultValue;
  253. }
  254. }
  255. /// <summary>
  256. /// 读取指定的配置文件里指定Key的值
  257. /// </summary>
  258. /// <param name="szKeyName">读取的Key名称</param>
  259. /// <param name="szDefaultValue">指定的Key不存在时,返回的值</param>
  260. /// <returns>Key值</returns>
  261. public static bool GetConfigData(string szKeyName, bool bDefaultValue)
  262. {
  263. string szValue = GetConfigData(szKeyName, bDefaultValue.ToString());
  264. try
  265. {
  266. return bool.Parse(szValue);
  267. }
  268. catch
  269. {
  270. return bDefaultValue;
  271. }
  272. }
  273. /// <summary>
  274. /// 读取指定的配置文件里指定Key的值
  275. /// </summary>
  276. /// <param name="szKeyName">读取的Key名称</param>
  277. /// <param name="szDefaultValue">指定的Key不存在时,返回的值</param>
  278. /// <returns>Key值</returns>
  279. public static string GetConfigData(string szKeyName, string szDefaultValue)
  280. {
  281. string szConfigFile = string.Format("{0}\\{1}", GetWorkDirectory(), CONFIG_FILE);
  282. if (!File.Exists(szConfigFile))
  283. {
  284. return szDefaultValue;
  285. }
  286. XmlDocument clsXmlDoc = GetXmlDocument(szConfigFile);
  287. if (clsXmlDoc == null)
  288. return szDefaultValue;
  289. string szXPath = string.Format(".//key-value[@name='{0}']", szKeyName);
  290. XmlNode clsXmlNode = SelectXmlNode(clsXmlDoc, szXPath);
  291. if (clsXmlNode == null)
  292. {
  293. return szDefaultValue;
  294. }
  295. XmlNode clsValueAttr = clsXmlNode.Attributes.GetNamedItem("value");
  296. if (clsValueAttr == null)
  297. return szDefaultValue;
  298. return clsValueAttr.Value;
  299. }
  300. /// <summary>
  301. /// 保存指定Key的值到指定的配置文件里
  302. /// </summary>
  303. /// <param name="szKeyName">要被改动值的Key名称</param>
  304. /// <param name="szValue">新改动的值</param>
  305. public static bool WriteConfigData(string szKeyName, string szValue)
  306. {
  307. string szConfigFile = string.Format("{0}\\{1}", GetWorkDirectory(), CONFIG_FILE);
  308. if (!File.Exists(szConfigFile))
  309. {
  310. if (!CreateXmlFile(szConfigFile, "SystemConfig"))
  311. return false;
  312. }
  313. XmlDocument clsXmlDoc = GetXmlDocument(szConfigFile);
  314. string szXPath = string.Format(".//key-value[@name='{0}']", szKeyName);
  315. XmlNode clsXmlNode = SelectXmlNode(clsXmlDoc, szXPath);
  316. if (clsXmlNode == null)
  317. {
  318. clsXmlNode = CreateXmlNode(clsXmlDoc, "key-value");
  319. }
  320. if (!SetXmlAttr(clsXmlNode, "name", szKeyName))
  321. return false;
  322. if (!SetXmlAttr(clsXmlNode, "value", szValue))
  323. return false;
  324. //
  325. return SaveXmlDocument(clsXmlDoc, szConfigFile);
  326. }
  327. #endregion
  328. }
  329. }
SystemConfig.xml配置文件:
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <SystemConfig>
  3. <!--配置RTVS可执行文件的全路径-->
  4. <key-value name="RTVS_FULL_EXE_FILE_PATH" value="D:\DEVELOPERS\vs_workspace\SQWL\RealTimeVideo\RTVS\bin\Debug\RTVS.exe"></key-value>
  5. <!--服务启动停止输出日志全路径-->
  6. <key-value name="LOG_FULL_FILE_PATH" value="D:\DEVELOPERS\vs_workspace\SQWL\RealTimeVideo\RTVSWindowsService\bin\Debug\StartOrStopLog.log"></key-value>
  7. </SystemConfig>

注意:这里的xml节点读取有规则,如key-value标签。

使用工具类来实现启动和关闭服务的调用方法:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using System.ServiceProcess;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. namespace RTVSWindowsService
  11. {
  12. public partial class RTVSWindowsService : ServiceBase
  13. {
  14. //全局变量
  15. string exePath = SystemConfig.GetConfigData("RTVS_FULL_EXE_FILE_PATH", "");
  16. string logPath = SystemConfig.GetConfigData("LOG_FULL_FILE_PATH", "");
  17. string path= AppDomain.CurrentDomain.BaseDirectory;
  18. public RTVSWindowsService()
  19. {
  20. InitializeComponent();
  21. }
  22. protected override void OnStart(string[] args)
  23. {
  24. LogWrite("Start........");//日志
  25. LogWrite("Work dictionary:"+ SystemConfig.GetWorkDirectory());
  26. LogWrite("SystemConfig.xml RTVS_FULL_EXE_FILE_PATH node value:" + exePath);
  27. LogWrite("SystemConfig.xml LOG_FULL_FILE_PATH node value:" + logPath);
  28. //启动目标应用程序 winform
  29. StartProgram(exePath);
  30. LogWrite("Started!");//日志
  31. }
  32. protected override void OnStop()
  33. {
  34. LogWrite("Stop........");//日志
  35. //杀死目标应用程序 winform
  36. StopProgram(exePath);
  37. LogWrite("Stopped!");//日志
  38. }
  39. /// <summary>
  40. /// 启动所有要启动的程序 ProgramPath:完整路径
  41. /// </summary>
  42. private void StartProgram(string ProgramPath)
  43. {
  44. try
  45. {
  46. string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
  47. if (!IsExistProcess(fileName))
  48. {
  49. ProcessStartInfo startInfo = new ProcessStartInfo(ProgramPath);
  50. startInfo.WindowStyle = ProcessWindowStyle.Normal;
  51. Process.Start(startInfo);
  52. LogWrite("Windows Service: " + fileName + " started.");
  53. }
  54. }
  55. catch (Exception err)
  56. {
  57. LogWrite(err.Message);
  58. }
  59. }
  60. /// <summary>
  61. /// 杀死启动的程序 ProgramPath:完整路径
  62. /// </summary>
  63. private void StopProgram(string ProgramPath)
  64. {
  65. try
  66. {
  67. string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
  68. if (IsExistProcess(fileName))
  69. {
  70. Process[] MyProcesses = Process.GetProcesses();
  71. foreach (Process MyProcess in MyProcesses)
  72. {
  73. if (MyProcess.ProcessName.CompareTo(fileName) == 0)
  74. {
  75. MyProcess.Kill();
  76. break;
  77. }
  78. }
  79. }
  80. }
  81. catch (Exception err)
  82. {
  83. LogWrite(err.Message);
  84. }
  85. }
  86. /// <summary>
  87. /// 检查该进程是否已启动
  88. /// </summary>
  89. /// <param name="processName"></param>
  90. /// <returns></returns>
  91. private bool IsExistProcess(string processName)
  92. {
  93. Process[] MyProcesses = Process.GetProcesses();
  94. foreach (Process MyProcess in MyProcesses)
  95. {
  96. if (MyProcess.ProcessName.CompareTo(processName) == 0)
  97. {
  98. return true;
  99. }
  100. }
  101. return false;
  102. }
  103. /// <summary>
  104. /// 写日志
  105. /// </summary>
  106. public void LogWrite(string str)
  107. {
  108. using (System.IO.StreamWriter sw = new System.IO.StreamWriter(logPath, true))
  109. {
  110. sw.WriteLine(DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") + str);
  111. }
  112. }
  113. }
  114. }

测试安装和卸载服务

最终我们的项目是这样的,如下图所示:

debug下面的可执行文件及文件结构(没有的需要放置到此目录下):


注意:运行的时候要以管理员身份运行,否则安装和卸载都会失败。

源码基本都在上面了,示例debug可执行打包文件:https://download.csdn.net/download/boonya/10498262

参考文章:

c#读取xml文件配置文件Winform及WebForm-Demo具体解释

C#创建window service服务启动另外的winform

Windows服务改进项目重构

可执行文件使用相对路径,并且实现服务可配置化处理。通用性处理,这里对项目进行了更名处理:

先看SystemConfig.xml配置:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <SystemConfig>
  3. <!--服务名称-->
  4. <key-value name="WINDOWS_SERVICE_NAME" value="RTVSWindowsService"></key-value>
  5. <!--服务显示名称-->
  6. <key-value name="WINDOWS_SERVICE_DISPLAY_NAME" value="RTVSWindowsService"></key-value>
  7. <!--服务功能描述-->
  8. <key-value name="WINDOWS_SERVICE_DESCRIBE" value="******科技有限公司转发应用服务(客户端、设备与网关数据转发服务)"></key-value>
  9. <!--执行exe的名称-->
  10. <key-value name="EXECUTE_EXE_FULL_NAME" value="RTVS.exe"></key-value>
  11. </SystemConfig>

注:如果想要配置文件输出的话需要修改如下属性(bat文件同理):


修改ProjectInstaller.cs

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Configuration.Install;
  6. using System.Linq;
  7. using System.Threading.Tasks;
  8. namespace RTVSWindowsService
  9. {
  10. [RunInstaller(true)]
  11. public partial class ProjectInstaller : System.Configuration.Install.Installer
  12. {
  13. const string WINDOWS_SERVICE_NAME = "WINDOWS_SERVICE_NAME";
  14. const string WINDOWS_SERVICE_DISPLAY_NAME = "WINDOWS_SERVICE_DISPLAY_NAME";
  15. const string WINDOWS_SERVICE_DESCRIBE = "WINDOWS_SERVICE_DESCRIBE";
  16. public ProjectInstaller()
  17. {
  18. InitializeComponent();
  19. // 赋予本地系统权限
  20. this.serviceProcessIntaller1.Account =System.ServiceProcess.ServiceAccount.LocalSystem;
  21. // 设置服务名称
  22. this.serviceInstaller1.ServiceName = SystemConfig.GetConfigData(WINDOWS_SERVICE_NAME, "DeveloperWindowsService");
  23. // 设置服务名称
  24. this.serviceInstaller1.DisplayName = SystemConfig.GetConfigData(WINDOWS_SERVICE_DISPLAY_NAME, "DeveloperWindowsService");
  25. // 设置服务名称
  26. this.serviceInstaller1.Description = SystemConfig.GetConfigData(WINDOWS_SERVICE_DESCRIBE, "DeveloperWindowsService(开发者WindowsService)");
  27. }
  28. }
  29. }

具体服务的修改WindowsService.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Linq;
  8. using System.ServiceProcess;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. namespace RTVSWindowsService
  12. {
  13. public partial class WindowsService : ServiceBase
  14. {
  15. string service = SystemConfig.GetConfigData("WINDOWS_SERVICE_DISPLAY_NAME", "");
  16. string exe = SystemConfig.GetConfigData("EXECUTE_EXE_FULL_NAME", "");
  17. string path = AppDomain.CurrentDomain.BaseDirectory;
  18. public WindowsService()
  19. {
  20. InitializeComponent();
  21. }
  22. protected override void OnStart(string[] args)
  23. {
  24. LogWrite("Start........");//日志
  25. LogWrite("CurrentDirectory:" + SystemConfig.GetWorkDirectory());
  26. LogWrite("BaseDirectory path:" +path);
  27. DirectoryInfo dictionaryInfo = Directory.GetParent(path);
  28. LogWrite("ParentDictionay:" + dictionaryInfo.Parent.FullName);
  29. string exePath = Path.Combine(dictionaryInfo.Parent.FullName, @exe);
  30. LogWrite("EXE PATH:" + exePath);
  31. //启动目标应用程序 winform
  32. StartProgram(exePath);
  33. LogWrite("Started!");//日志
  34. }
  35. protected override void OnStop()
  36. {
  37. LogWrite("Stop........");//日志
  38. DirectoryInfo dictionaryInfo = Directory.GetParent(path);
  39. LogWrite("ParentDictionay:" + dictionaryInfo.Parent.FullName);
  40. string exePath = Path.Combine(dictionaryInfo.Parent.FullName, @exe);
  41. LogWrite("EXE PATH:" + exePath);
  42. //杀死目标应用程序 winform
  43. StopProgram(exePath);
  44. LogWrite("Stopped!");//日志
  45. }
  46. /// <summary>
  47. /// 启动所有要启动的程序 ProgramPath:完整路径
  48. /// </summary>
  49. private void StartProgram(string ProgramPath)
  50. {
  51. try
  52. {
  53. string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
  54. if (!IsExistProcess(fileName))
  55. {
  56. ProcessStartInfo startInfo = new ProcessStartInfo(ProgramPath);
  57. startInfo.WindowStyle = ProcessWindowStyle.Normal;
  58. Process.Start(startInfo);
  59. LogWrite("Windows Service: "+service +" "+ fileName + " started.");
  60. }
  61. }
  62. catch (Exception err)
  63. {
  64. LogWrite(err.Message);
  65. }
  66. }
  67. /// <summary>
  68. /// 杀死启动的程序 ProgramPath:完整路径
  69. /// </summary>
  70. private void StopProgram(string ProgramPath)
  71. {
  72. try
  73. {
  74. string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
  75. if (IsExistProcess(fileName))
  76. {
  77. Process[] MyProcesses = Process.GetProcesses();
  78. foreach (Process MyProcess in MyProcesses)
  79. {
  80. if (MyProcess.ProcessName.CompareTo(fileName) == 0)
  81. {
  82. MyProcess.Kill();
  83. break;
  84. }
  85. }
  86. }
  87. }
  88. catch (Exception err)
  89. {
  90. LogWrite(err.Message);
  91. }
  92. }
  93. /// <summary>
  94. /// 检查该进程是否已启动
  95. /// </summary>
  96. /// <param name="processName"></param>
  97. /// <returns></returns>
  98. private bool IsExistProcess(string processName)
  99. {
  100. Process[] MyProcesses = Process.GetProcesses();
  101. foreach (Process MyProcess in MyProcesses)
  102. {
  103. if (MyProcess.ProcessName.CompareTo(processName) == 0)
  104. {
  105. return true;
  106. }
  107. }
  108. return false;
  109. }
  110. /// <summary>
  111. /// 写日志
  112. /// </summary>
  113. public void LogWrite(string str)
  114. {
  115. using (System.IO.StreamWriter sw = new System.IO.StreamWriter(Path.Combine(path, @"StartOrStopLog.log"), true))
  116. {
  117. sw.WriteLine(DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") + str);
  118. }
  119. }
  120. }
  121. }
Windows服务改进项目GUI实现

GUI有个问题,运行时bat执行需要以“管理员身份运行”,可以让程序申请管理员权限来实现,下一篇文章会讲如何快速申请管理员权限。


GUI服务管理项目结构如下:


服务管理界面设计如下:

GUI管理代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.Drawing;
  7. using System.IO;
  8. using System.Linq;
  9. using System.ServiceProcess;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using System.Windows.Forms;
  13. using RTVSWindowsService;
  14. namespace RTVS
  15. {
  16. /// <summary>
  17. /// RTVS系统服务管理GUI
  18. /// </summary>
  19. public partial class FormServiceGUI : Form
  20. {
  21. ServiceController serviceController = null;
  22. string serviceName = SystemConfig.GetConfigServiceData("WINDOWS_SERVICE_DISPLAY_NAME", "RTVSWindowsService");
  23. // 每次执行的间隔毫秒时长1000ms
  24. System.Timers.Timer timer = new System.Timers.Timer(1000);
  25. public FormServiceGUI()
  26. {
  27. InitializeComponent();
  28. }
  29. /// <summary>
  30. /// 判断服务是否存在
  31. /// </summary>
  32. /// <returns></returns>
  33. public bool ExistService()
  34. {
  35. bool Exits = false;
  36. ServiceController[] services = ServiceController.GetServices();
  37. foreach (ServiceController service in services)
  38. {
  39. if (service.ServiceName.ToLower().Equals(serviceName.ToLower()))
  40. {
  41. serviceController = service;
  42. Exits = true;
  43. break;
  44. }
  45. }
  46. return Exits;
  47. }
  48. /// <summary>
  49. /// 安装服务
  50. /// </summary>
  51. /// <param name="sender"></param>
  52. /// <param name="e"></param>
  53. private void btnInstall_Click(object sender, EventArgs e)
  54. {
  55. if (ExistService())
  56. {
  57. MessageBox.Show("服务已经安装,请卸载后进行重装!","操作提示:");
  58. return;
  59. }
  60. ShowState("开始安装服务......");
  61. try
  62. {
  63. string CurrentDirectory = System.Environment.CurrentDirectory;
  64. System.Environment.CurrentDirectory = CurrentDirectory + "\\Service";
  65. ShowState("查找服务目录");
  66. Process process = new Process();
  67. process.StartInfo.UseShellExecute = false;
  68. process.StartInfo.FileName = "INSTALL.bat";
  69. process.StartInfo.CreateNoWindow = true;
  70. process.Start();
  71. System.Environment.CurrentDirectory = CurrentDirectory;
  72. ShowState("服务安装完成!");
  73. }
  74. catch (Exception ex)
  75. {
  76. string msg = ex.Message;
  77. if (ex.InnerException != null)
  78. msg += ex.InnerException.Message;
  79. ShowState("安装服务出现错误:" + msg);
  80. }
  81. }
  82. /// <summary>
  83. /// 卸载服务
  84. /// </summary>
  85. /// <param name="sender"></param>
  86. /// <param name="e"></param>
  87. private void btnUninstall_Click(object sender, EventArgs e)
  88. {
  89. ShowState("开始卸载服务.......");
  90. try
  91. {
  92. string CurrentDirectory = System.Environment.CurrentDirectory;
  93. System.Environment.CurrentDirectory = CurrentDirectory + "\\Service";
  94. Process process = new Process();
  95. process.StartInfo.UseShellExecute = false;
  96. process.StartInfo.FileName = "UNINSTALL.bat";
  97. process.StartInfo.CreateNoWindow = true;
  98. process.Start();
  99. System.Environment.CurrentDirectory = CurrentDirectory;
  100. ShowState("服务卸载完成!");
  101. }
  102. catch (Exception ex)
  103. {
  104. string msg = ex.Message;
  105. if (ex.InnerException != null)
  106. msg += ex.InnerException.Message;
  107. ShowState("卸载服务出现错误:" + msg);
  108. }
  109. }
  110. /// <summary>
  111. /// 启动服务
  112. /// </summary>
  113. /// <param name="sender"></param>
  114. /// <param name="e"></param>
  115. private void btnStart_Click(object sender, EventArgs e)
  116. {
  117. try
  118. {
  119. ShowState("启动服务开始");
  120. ServiceController serviceController = new ServiceController(serviceName);
  121. serviceController.Start();
  122. ShowState("启动服务完成。");
  123. }
  124. catch (Exception ex)
  125. {
  126. string msg = ex.Message;
  127. if (ex.InnerException != null)
  128. msg += ex.InnerException.Message;
  129. ShowState("启动服务出现错误:" + msg);
  130. }
  131. }
  132. /// <summary>
  133. /// 停止服务
  134. /// </summary>
  135. /// <param name="sender"></param>
  136. /// <param name="e"></param>
  137. private void btnStop_Click(object sender, EventArgs e)
  138. {
  139. try
  140. {
  141. ShowState("停止服务开始");
  142. ServiceController serviceController = new ServiceController(serviceName);
  143. if (serviceController.CanStop)
  144. serviceController.Stop();
  145. ShowState("停止服务完成。");
  146. }
  147. catch (Exception ex)
  148. {
  149. string msg = ex.Message;
  150. if (ex.InnerException != null)
  151. msg += ex.InnerException.Message;
  152. ShowState("停止服务出现错误:" + msg);
  153. }
  154. }
  155. /// <summary>
  156. /// 清理日志
  157. /// </summary>
  158. /// <param name="sender"></param>
  159. /// <param name="e"></param>
  160. private void btnClearLog_Click(object sender, EventArgs e)
  161. {
  162. this.textBoxMessage.Text = "";
  163. }
  164. /// <summary>
  165. /// 初始化批处理脚本
  166. /// </summary>
  167. /// <param name="sender"></param>
  168. /// <param name="e"></param>
  169. private void FormInstallerService_Load(object sender, EventArgs e)
  170. {
  171. string Installbat_content = @"%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe {0}
  172. Net Start " + serviceName +"\n"+
  173. "sc config " + serviceName + " start=AUTO" + "\n" +
  174. "sc config " + serviceName + " type= interact type=own";
  175. string directory = Path.Combine(System.Environment.CurrentDirectory, "Service");
  176. File.WriteAllText(Path.Combine(directory, "INSTALL.bat"), string.Format(Installbat_content, Path.Combine(directory, "WindowsService.exe")));
  177. string Unistallbat_content = @"%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /u {0}";
  178. File.WriteAllText(Path.Combine(directory, "UNINSTALL.bat"), string.Format(Unistallbat_content, Path.Combine(directory, "WindowsService.exe")));
  179. ShowState("系统服务管理运行中.........");
  180. ShowState("系统服务:"+ serviceName+(ExistService() ? "已安装" : "未安装"));
  181. if (serviceController!=null)
  182. {
  183. ShowState("系统服务:" + serviceName + "状态:"+serviceController.Status);
  184. }
  185. this.StartPosition = FormStartPosition.CenterScreen;
  186. //到达时间的时候执行事件
  187. timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerTask);
  188. //设置是执行一次(false)还是一直执行(true)
  189. timer.AutoReset = true;
  190. //是否执行System.Timers.Timer.Elapsed事件
  191. timer.Enabled = true;
  192. }
  193. public void TimerTask(object source, System.Timers.ElapsedEventArgs e)
  194. {
  195. ShowState("系统服务:" + serviceName + (ExistService() ? "已安装,当前状态:" + serviceController.Status:"未安装"));
  196. }
  197. /// <summary>
  198. /// 操作状态
  199. /// </summary>
  200. /// <param name="message"></param>
  201. private void ShowState(string message)
  202. {
  203. this.textBoxMessage.AppendText(DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") +message +"\n");
  204. }
  205. }
  206. }

服务与GUI的关系:

这里我们直接让服务代码输出到Service中:

得到如下图所示输出文件:

这样我们的RTVS项目就可以使用windows服务了。服务运行界面如下:


包含服务和可以参考实现的GUI,另外还提供可配置的Windows服务,下载地址:https://download.csdn.net/download/boonya/10502384

相关技术文章

点击QQ咨询
开通会员
返回顶部
×
微信扫码支付
微信扫码支付
确定支付下载
请使用微信描二维码支付
×

提示信息

×

选择支付方式

  • 微信支付
  • 支付宝付款
确定支付下载