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配置文件来决定
此时,我们需要编写配置文件和读取配置文件的工具类,工具类如下:- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Xml;
-
- namespace RTVSWindowsService
- {
- /// <summary>
- /// 系统XML配置属性
- /// </summary>
- class SystemConfig
- {
- /// <summary>
- /// 得到工作文件夹路径
- /// </summary>
- /// <returns></returns>
- public static string GetWorkDirectory()
- {
- try
- {
- return Path.GetDirectoryName(typeof(SystemConfig).Assembly.Location);
- }
- catch
- {
- return AppDomain.CurrentDomain.BaseDirectory;
- }
- }
-
- /// <summary>
- /// 推断字符串是否为空串
- /// </summary>
- /// <param name="szString">目标字符串</param>
- /// <returns>true:为空串;false:非空串</returns>
- private static bool IsEmptyString(string szString)
- {
- if (szString == null)
- return true;
- if (szString.Trim() == string.Empty)
- return true;
- return false;
- }
- /// <summary>
- /// 创建一个制定根节点名的XML文件
- /// </summary>
- /// <param name="szFileName">XML文件</param>
- /// <param name="szRootName">根节点名</param>
- /// <returns>bool</returns>
- private static bool CreateXmlFile(string szFileName, string szRootName)
- {
- if (szFileName == null || szFileName.Trim() == "")
- return false;
- if (szRootName == null || szRootName.Trim() == "")
- return false;
-
- XmlDocument clsXmlDoc = new XmlDocument();
- clsXmlDoc.AppendChild(clsXmlDoc.CreateXmlDeclaration("1.0", "GBK", null));
- clsXmlDoc.AppendChild(clsXmlDoc.CreateNode(XmlNodeType.Element, szRootName, ""));
- try
- {
- clsXmlDoc.Save(szFileName);
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- /// <summary>
- /// 从XML文件获取相应的XML文档对象
- /// </summary>
- /// <param name="szXmlFile">XML文件</param>
- /// <returns>XML文档对象</returns>
- private static XmlDocument GetXmlDocument(string szXmlFile)
- {
- if (IsEmptyString(szXmlFile))
- return null;
- if (!File.Exists(szXmlFile))
- return null;
- XmlDocument clsXmlDoc = new XmlDocument();
- try
- {
- clsXmlDoc.Load(szXmlFile);
- }
- catch
- {
- return null;
- }
- return clsXmlDoc;
- }
-
- /// <summary>
- /// 将XML文档对象保存为XML文件
- /// </summary>
- /// <param name="clsXmlDoc">XML文档对象</param>
- /// <param name="szXmlFile">XML文件</param>
- /// <returns>bool:保存结果</returns>
- private static bool SaveXmlDocument(XmlDocument clsXmlDoc, string szXmlFile)
- {
- if (clsXmlDoc == null)
- return false;
- if (IsEmptyString(szXmlFile))
- return false;
- try
- {
- if (File.Exists(szXmlFile))
- File.Delete(szXmlFile);
- }
- catch
- {
- return false;
- }
- try
- {
- clsXmlDoc.Save(szXmlFile);
- }
- catch
- {
- return false;
- }
- return true;
- }
-
- /// <summary>
- /// 获取XPath指向的单一XML节点
- /// </summary>
- /// <param name="clsRootNode">XPath所在的根节点</param>
- /// <param name="szXPath">XPath表达式</param>
- /// <returns>XmlNode</returns>
- private static XmlNode SelectXmlNode(XmlNode clsRootNode, string szXPath)
- {
- if (clsRootNode == null || IsEmptyString(szXPath))
- return null;
- try
- {
- return clsRootNode.SelectSingleNode(szXPath);
- }
- catch
- {
- return null;
- }
- }
-
- /// <summary>
- /// 获取XPath指向的XML节点集
- /// </summary>
- /// <param name="clsRootNode">XPath所在的根节点</param>
- /// <param name="szXPath">XPath表达式</param>
- /// <returns>XmlNodeList</returns>
- private static XmlNodeList SelectXmlNodes(XmlNode clsRootNode, string szXPath)
- {
- if (clsRootNode == null || IsEmptyString(szXPath))
- return null;
- try
- {
- return clsRootNode.SelectNodes(szXPath);
- }
- catch
- {
- return null;
- }
- }
-
- /// <summary>
- /// 创建一个XmlNode并加入到文档
- /// </summary>
- /// <param name="clsParentNode">父节点</param>
- /// <param name="szNodeName">结点名称</param>
- /// <returns>XmlNode</returns>
- private static XmlNode CreateXmlNode(XmlNode clsParentNode, string szNodeName)
- {
- try
- {
- XmlDocument clsXmlDoc = null;
- if (clsParentNode.GetType() != typeof(XmlDocument))
- clsXmlDoc = clsParentNode.OwnerDocument;
- else
- clsXmlDoc = clsParentNode as XmlDocument;
- XmlNode clsXmlNode = clsXmlDoc.CreateNode(XmlNodeType.Element, szNodeName, string.Empty);
- if (clsParentNode.GetType() == typeof(XmlDocument))
- {
- clsXmlDoc.LastChild.AppendChild(clsXmlNode);
- }
- else
- {
- clsParentNode.AppendChild(clsXmlNode);
- }
- return clsXmlNode;
- }
- catch
- {
- return null;
- }
- }
-
- /// <summary>
- /// 设置指定节点中指定属性的值
- /// </summary>
- /// <param name="parentNode">XML节点</param>
- /// <param name="szAttrName">属性名</param>
- /// <param name="szAttrValue">属性值</param>
- /// <returns>bool</returns>
- private static bool SetXmlAttr(XmlNode clsXmlNode, string szAttrName, string szAttrValue)
- {
- if (clsXmlNode == null)
- return false;
- if (IsEmptyString(szAttrName))
- return false;
- if (IsEmptyString(szAttrValue))
- szAttrValue = string.Empty;
- XmlAttribute clsAttrNode = clsXmlNode.Attributes.GetNamedItem(szAttrName) as XmlAttribute;
- if (clsAttrNode == null)
- {
- XmlDocument clsXmlDoc = clsXmlNode.OwnerDocument;
- if (clsXmlDoc == null)
- return false;
- clsAttrNode = clsXmlDoc.CreateAttribute(szAttrName);
- clsXmlNode.Attributes.Append(clsAttrNode);
- }
- clsAttrNode.Value = szAttrValue;
- return true;
- }
-
- #region"配置文件的读取和写入"
- private static string CONFIG_FILE = "SystemConfig.xml";
- /// <summary>
- /// 读取指定的配置文件里指定Key的值
- /// </summary>
- /// <param name="szKeyName">读取的Key名称</param>
- /// <param name="szDefaultValue">指定的Key不存在时,返回的值</param>
- /// <returns>Key值</returns>
- public static int GetConfigData(string szKeyName, int nDefaultValue)
- {
- string szValue = GetConfigData(szKeyName, nDefaultValue.ToString());
- try
- {
- return int.Parse(szValue);
- }
- catch
- {
- return nDefaultValue;
- }
- }
-
- /// <summary>
- /// 读取指定的配置文件里指定Key的值
- /// </summary>
- /// <param name="szKeyName">读取的Key名称</param>
- /// <param name="szDefaultValue">指定的Key不存在时,返回的值</param>
- /// <returns>Key值</returns>
- public static float GetConfigData(string szKeyName, float fDefaultValue)
- {
- string szValue = GetConfigData(szKeyName, fDefaultValue.ToString());
- try
- {
- return float.Parse(szValue);
- }
- catch
- {
- return fDefaultValue;
- }
- }
-
- /// <summary>
- /// 读取指定的配置文件里指定Key的值
- /// </summary>
- /// <param name="szKeyName">读取的Key名称</param>
- /// <param name="szDefaultValue">指定的Key不存在时,返回的值</param>
- /// <returns>Key值</returns>
- public static bool GetConfigData(string szKeyName, bool bDefaultValue)
- {
- string szValue = GetConfigData(szKeyName, bDefaultValue.ToString());
- try
- {
- return bool.Parse(szValue);
- }
- catch
- {
- return bDefaultValue;
- }
- }
-
- /// <summary>
- /// 读取指定的配置文件里指定Key的值
- /// </summary>
- /// <param name="szKeyName">读取的Key名称</param>
- /// <param name="szDefaultValue">指定的Key不存在时,返回的值</param>
- /// <returns>Key值</returns>
- public static string GetConfigData(string szKeyName, string szDefaultValue)
- {
- string szConfigFile = string.Format("{0}\\{1}", GetWorkDirectory(), CONFIG_FILE);
- if (!File.Exists(szConfigFile))
- {
- return szDefaultValue;
- }
-
- XmlDocument clsXmlDoc = GetXmlDocument(szConfigFile);
- if (clsXmlDoc == null)
- return szDefaultValue;
-
- string szXPath = string.Format(".//key-value[@name='{0}']", szKeyName);
- XmlNode clsXmlNode = SelectXmlNode(clsXmlDoc, szXPath);
- if (clsXmlNode == null)
- {
- return szDefaultValue;
- }
-
- XmlNode clsValueAttr = clsXmlNode.Attributes.GetNamedItem("value");
- if (clsValueAttr == null)
- return szDefaultValue;
- return clsValueAttr.Value;
- }
-
- /// <summary>
- /// 保存指定Key的值到指定的配置文件里
- /// </summary>
- /// <param name="szKeyName">要被改动值的Key名称</param>
- /// <param name="szValue">新改动的值</param>
- public static bool WriteConfigData(string szKeyName, string szValue)
- {
- string szConfigFile = string.Format("{0}\\{1}", GetWorkDirectory(), CONFIG_FILE);
- if (!File.Exists(szConfigFile))
- {
- if (!CreateXmlFile(szConfigFile, "SystemConfig"))
- return false;
- }
- XmlDocument clsXmlDoc = GetXmlDocument(szConfigFile);
-
- string szXPath = string.Format(".//key-value[@name='{0}']", szKeyName);
- XmlNode clsXmlNode = SelectXmlNode(clsXmlDoc, szXPath);
- if (clsXmlNode == null)
- {
- clsXmlNode = CreateXmlNode(clsXmlDoc, "key-value");
- }
- if (!SetXmlAttr(clsXmlNode, "name", szKeyName))
- return false;
- if (!SetXmlAttr(clsXmlNode, "value", szValue))
- return false;
- //
- return SaveXmlDocument(clsXmlDoc, szConfigFile);
- }
- #endregion
- }
- }
SystemConfig.xml配置文件:
- <?xml version="1.0" encoding="utf-8" ?>
- <SystemConfig>
- <!--配置RTVS可执行文件的全路径-->
- <key-value name="RTVS_FULL_EXE_FILE_PATH" value="D:\DEVELOPERS\vs_workspace\SQWL\RealTimeVideo\RTVS\bin\Debug\RTVS.exe"></key-value>
- <!--服务启动停止输出日志全路径-->
- <key-value name="LOG_FULL_FILE_PATH" value="D:\DEVELOPERS\vs_workspace\SQWL\RealTimeVideo\RTVSWindowsService\bin\Debug\StartOrStopLog.log"></key-value>
- </SystemConfig>
注意:这里的xml节点读取有规则,如key-value标签。
使用工具类来实现启动和关闭服务的调用方法:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Diagnostics;
- using System.Linq;
- using System.ServiceProcess;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace RTVSWindowsService
- {
- public partial class RTVSWindowsService : ServiceBase
- {
- //全局变量
- string exePath = SystemConfig.GetConfigData("RTVS_FULL_EXE_FILE_PATH", "");
-
- string logPath = SystemConfig.GetConfigData("LOG_FULL_FILE_PATH", "");
-
- string path= AppDomain.CurrentDomain.BaseDirectory;
-
- public RTVSWindowsService()
- {
- InitializeComponent();
- }
-
- protected override void OnStart(string[] args)
- {
- LogWrite("Start........");//日志
- LogWrite("Work dictionary:"+ SystemConfig.GetWorkDirectory());
- LogWrite("SystemConfig.xml RTVS_FULL_EXE_FILE_PATH node value:" + exePath);
- LogWrite("SystemConfig.xml LOG_FULL_FILE_PATH node value:" + logPath);
- //启动目标应用程序 winform
- StartProgram(exePath);
- LogWrite("Started!");//日志
- }
-
- protected override void OnStop()
- {
- LogWrite("Stop........");//日志
- //杀死目标应用程序 winform
- StopProgram(exePath);
- LogWrite("Stopped!");//日志
- }
-
- /// <summary>
- /// 启动所有要启动的程序 ProgramPath:完整路径
- /// </summary>
- private void StartProgram(string ProgramPath)
- {
- try
- {
- string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
- if (!IsExistProcess(fileName))
- {
- ProcessStartInfo startInfo = new ProcessStartInfo(ProgramPath);
- startInfo.WindowStyle = ProcessWindowStyle.Normal;
- Process.Start(startInfo);
- LogWrite("Windows Service: " + fileName + " started.");
- }
- }
- catch (Exception err)
- {
- LogWrite(err.Message);
- }
- }
-
- /// <summary>
- /// 杀死启动的程序 ProgramPath:完整路径
- /// </summary>
- private void StopProgram(string ProgramPath)
- {
- try
- {
- string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
- if (IsExistProcess(fileName))
- {
- Process[] MyProcesses = Process.GetProcesses();
- foreach (Process MyProcess in MyProcesses)
- {
- if (MyProcess.ProcessName.CompareTo(fileName) == 0)
- {
- MyProcess.Kill();
- break;
- }
- }
- }
- }
- catch (Exception err)
- {
- LogWrite(err.Message);
- }
- }
-
- /// <summary>
- /// 检查该进程是否已启动
- /// </summary>
- /// <param name="processName"></param>
- /// <returns></returns>
- private bool IsExistProcess(string processName)
- {
- Process[] MyProcesses = Process.GetProcesses();
- foreach (Process MyProcess in MyProcesses)
- {
- if (MyProcess.ProcessName.CompareTo(processName) == 0)
- {
- return true;
- }
- }
- return false;
- }
-
- /// <summary>
- /// 写日志
- /// </summary>
- public void LogWrite(string str)
- {
- using (System.IO.StreamWriter sw = new System.IO.StreamWriter(logPath, true))
- {
- sw.WriteLine(DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") + str);
- }
- }
-
- }
-
- }
测试安装和卸载服务
最终我们的项目是这样的,如下图所示:
debug下面的可执行文件及文件结构(没有的需要放置到此目录下):
注意:运行的时候要以管理员身份运行,否则安装和卸载都会失败。
源码基本都在上面了,示例debug可执行打包文件:https://download.csdn.net/download/boonya/10498262
参考文章:
c#读取xml文件配置文件Winform及WebForm-Demo具体解释
C#创建window service服务启动另外的winform
Windows服务改进项目重构
可执行文件使用相对路径,并且实现服务可配置化处理。通用性处理,这里对项目进行了更名处理:
先看SystemConfig.xml配置:
- <?xml version="1.0" encoding="utf-8" ?>
- <SystemConfig>
- <!--服务名称-->
- <key-value name="WINDOWS_SERVICE_NAME" value="RTVSWindowsService"></key-value>
- <!--服务显示名称-->
- <key-value name="WINDOWS_SERVICE_DISPLAY_NAME" value="RTVSWindowsService"></key-value>
- <!--服务功能描述-->
- <key-value name="WINDOWS_SERVICE_DESCRIBE" value="******科技有限公司转发应用服务(客户端、设备与网关数据转发服务)"></key-value>
- <!--执行exe的名称-->
- <key-value name="EXECUTE_EXE_FULL_NAME" value="RTVS.exe"></key-value>
- </SystemConfig>
注:如果想要配置文件输出的话需要修改如下属性(bat文件同理):
修改ProjectInstaller.cs
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Configuration.Install;
- using System.Linq;
- using System.Threading.Tasks;
-
- namespace RTVSWindowsService
- {
- [RunInstaller(true)]
- public partial class ProjectInstaller : System.Configuration.Install.Installer
- {
- const string WINDOWS_SERVICE_NAME = "WINDOWS_SERVICE_NAME";
- const string WINDOWS_SERVICE_DISPLAY_NAME = "WINDOWS_SERVICE_DISPLAY_NAME";
- const string WINDOWS_SERVICE_DESCRIBE = "WINDOWS_SERVICE_DESCRIBE";
- public ProjectInstaller()
- {
- InitializeComponent();
- // 赋予本地系统权限
- this.serviceProcessIntaller1.Account =System.ServiceProcess.ServiceAccount.LocalSystem;
- // 设置服务名称
- this.serviceInstaller1.ServiceName = SystemConfig.GetConfigData(WINDOWS_SERVICE_NAME, "DeveloperWindowsService");
- // 设置服务名称
- this.serviceInstaller1.DisplayName = SystemConfig.GetConfigData(WINDOWS_SERVICE_DISPLAY_NAME, "DeveloperWindowsService");
- // 设置服务名称
- this.serviceInstaller1.Description = SystemConfig.GetConfigData(WINDOWS_SERVICE_DESCRIBE, "DeveloperWindowsService(开发者WindowsService)");
- }
- }
- }
具体服务的修改WindowsService.cs
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.ServiceProcess;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace RTVSWindowsService
- {
- public partial class WindowsService : ServiceBase
- {
- string service = SystemConfig.GetConfigData("WINDOWS_SERVICE_DISPLAY_NAME", "");
-
- string exe = SystemConfig.GetConfigData("EXECUTE_EXE_FULL_NAME", "");
-
- string path = AppDomain.CurrentDomain.BaseDirectory;
-
- public WindowsService()
- {
- InitializeComponent();
- }
-
- protected override void OnStart(string[] args)
- {
- LogWrite("Start........");//日志
- LogWrite("CurrentDirectory:" + SystemConfig.GetWorkDirectory());
- LogWrite("BaseDirectory path:" +path);
- DirectoryInfo dictionaryInfo = Directory.GetParent(path);
- LogWrite("ParentDictionay:" + dictionaryInfo.Parent.FullName);
- string exePath = Path.Combine(dictionaryInfo.Parent.FullName, @exe);
- LogWrite("EXE PATH:" + exePath);
- //启动目标应用程序 winform
- StartProgram(exePath);
- LogWrite("Started!");//日志
-
- }
-
- protected override void OnStop()
- {
- LogWrite("Stop........");//日志
- DirectoryInfo dictionaryInfo = Directory.GetParent(path);
- LogWrite("ParentDictionay:" + dictionaryInfo.Parent.FullName);
- string exePath = Path.Combine(dictionaryInfo.Parent.FullName, @exe);
- LogWrite("EXE PATH:" + exePath);
- //杀死目标应用程序 winform
- StopProgram(exePath);
- LogWrite("Stopped!");//日志
- }
-
- /// <summary>
- /// 启动所有要启动的程序 ProgramPath:完整路径
- /// </summary>
- private void StartProgram(string ProgramPath)
- {
- try
- {
- string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
- if (!IsExistProcess(fileName))
- {
- ProcessStartInfo startInfo = new ProcessStartInfo(ProgramPath);
- startInfo.WindowStyle = ProcessWindowStyle.Normal;
- Process.Start(startInfo);
- LogWrite("Windows Service: "+service +" "+ fileName + " started.");
- }
- }
- catch (Exception err)
- {
- LogWrite(err.Message);
- }
- }
-
- /// <summary>
- /// 杀死启动的程序 ProgramPath:完整路径
- /// </summary>
- private void StopProgram(string ProgramPath)
- {
- try
- {
- string fileName = System.IO.Path.GetFileNameWithoutExtension(ProgramPath);
- if (IsExistProcess(fileName))
- {
- Process[] MyProcesses = Process.GetProcesses();
- foreach (Process MyProcess in MyProcesses)
- {
- if (MyProcess.ProcessName.CompareTo(fileName) == 0)
- {
- MyProcess.Kill();
- break;
- }
- }
- }
- }
- catch (Exception err)
- {
- LogWrite(err.Message);
- }
- }
-
- /// <summary>
- /// 检查该进程是否已启动
- /// </summary>
- /// <param name="processName"></param>
- /// <returns></returns>
- private bool IsExistProcess(string processName)
- {
- Process[] MyProcesses = Process.GetProcesses();
- foreach (Process MyProcess in MyProcesses)
- {
- if (MyProcess.ProcessName.CompareTo(processName) == 0)
- {
- return true;
- }
- }
- return false;
- }
-
- /// <summary>
- /// 写日志
- /// </summary>
- public void LogWrite(string str)
- {
- using (System.IO.StreamWriter sw = new System.IO.StreamWriter(Path.Combine(path, @"StartOrStopLog.log"), true))
- {
- sw.WriteLine(DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") + str);
- }
- }
- }
-
- }
Windows服务改进项目GUI实现
GUI有个问题,运行时bat执行需要以“管理员身份运行”,可以让程序申请管理员权限来实现,下一篇文章会讲如何快速申请管理员权限。
GUI服务管理项目结构如下:
服务管理界面设计如下:
GUI管理代码如下:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Diagnostics;
- using System.Drawing;
- using System.IO;
- using System.Linq;
- using System.ServiceProcess;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using RTVSWindowsService;
-
- namespace RTVS
- {
- /// <summary>
- /// RTVS系统服务管理GUI
- /// </summary>
- public partial class FormServiceGUI : Form
- {
- ServiceController serviceController = null;
-
- string serviceName = SystemConfig.GetConfigServiceData("WINDOWS_SERVICE_DISPLAY_NAME", "RTVSWindowsService");
-
- // 每次执行的间隔毫秒时长1000ms
- System.Timers.Timer timer = new System.Timers.Timer(1000);
-
- public FormServiceGUI()
- {
- InitializeComponent();
- }
-
- /// <summary>
- /// 判断服务是否存在
- /// </summary>
- /// <returns></returns>
- public bool ExistService()
- {
- bool Exits = false;
- ServiceController[] services = ServiceController.GetServices();
-
- foreach (ServiceController service in services)
- {
- if (service.ServiceName.ToLower().Equals(serviceName.ToLower()))
- {
- serviceController = service;
- Exits = true;
- break;
- }
- }
- return Exits;
- }
-
- /// <summary>
- /// 安装服务
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnInstall_Click(object sender, EventArgs e)
- {
- if (ExistService())
- {
- MessageBox.Show("服务已经安装,请卸载后进行重装!","操作提示:");
- return;
- }
- ShowState("开始安装服务......");
- try
- {
- string CurrentDirectory = System.Environment.CurrentDirectory;
- System.Environment.CurrentDirectory = CurrentDirectory + "\\Service";
- ShowState("查找服务目录");
- Process process = new Process();
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.FileName = "INSTALL.bat";
- process.StartInfo.CreateNoWindow = true;
- process.Start();
- System.Environment.CurrentDirectory = CurrentDirectory;
- ShowState("服务安装完成!");
- }
- catch (Exception ex)
- {
- string msg = ex.Message;
- if (ex.InnerException != null)
- msg += ex.InnerException.Message;
- ShowState("安装服务出现错误:" + msg);
- }
- }
-
- /// <summary>
- /// 卸载服务
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnUninstall_Click(object sender, EventArgs e)
- {
- ShowState("开始卸载服务.......");
- try
- {
- string CurrentDirectory = System.Environment.CurrentDirectory;
- System.Environment.CurrentDirectory = CurrentDirectory + "\\Service";
- Process process = new Process();
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.FileName = "UNINSTALL.bat";
- process.StartInfo.CreateNoWindow = true;
- process.Start();
- System.Environment.CurrentDirectory = CurrentDirectory;
- ShowState("服务卸载完成!");
- }
- catch (Exception ex)
- {
- string msg = ex.Message;
- if (ex.InnerException != null)
- msg += ex.InnerException.Message;
- ShowState("卸载服务出现错误:" + msg);
- }
- }
-
- /// <summary>
- /// 启动服务
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnStart_Click(object sender, EventArgs e)
- {
- try
- {
-
- ShowState("启动服务开始");
- ServiceController serviceController = new ServiceController(serviceName);
- serviceController.Start();
- ShowState("启动服务完成。");
- }
- catch (Exception ex)
- {
- string msg = ex.Message;
- if (ex.InnerException != null)
- msg += ex.InnerException.Message;
- ShowState("启动服务出现错误:" + msg);
- }
- }
-
- /// <summary>
- /// 停止服务
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnStop_Click(object sender, EventArgs e)
- {
- try
- {
-
- ShowState("停止服务开始");
- ServiceController serviceController = new ServiceController(serviceName);
- if (serviceController.CanStop)
- serviceController.Stop();
-
- ShowState("停止服务完成。");
- }
- catch (Exception ex)
- {
-
- string msg = ex.Message;
- if (ex.InnerException != null)
- msg += ex.InnerException.Message;
- ShowState("停止服务出现错误:" + msg);
- }
- }
-
- /// <summary>
- /// 清理日志
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnClearLog_Click(object sender, EventArgs e)
- {
- this.textBoxMessage.Text = "";
- }
-
- /// <summary>
- /// 初始化批处理脚本
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void FormInstallerService_Load(object sender, EventArgs e)
- {
- string Installbat_content = @"%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe {0}
- Net Start " + serviceName +"\n"+
- "sc config " + serviceName + " start=AUTO" + "\n" +
- "sc config " + serviceName + " type= interact type=own";
- string directory = Path.Combine(System.Environment.CurrentDirectory, "Service");
- File.WriteAllText(Path.Combine(directory, "INSTALL.bat"), string.Format(Installbat_content, Path.Combine(directory, "WindowsService.exe")));
-
- string Unistallbat_content = @"%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /u {0}";
-
- File.WriteAllText(Path.Combine(directory, "UNINSTALL.bat"), string.Format(Unistallbat_content, Path.Combine(directory, "WindowsService.exe")));
-
-
-
- ShowState("系统服务管理运行中.........");
- ShowState("系统服务:"+ serviceName+(ExistService() ? "已安装" : "未安装"));
- if (serviceController!=null)
- {
- ShowState("系统服务:" + serviceName + "状态:"+serviceController.Status);
- }
- this.StartPosition = FormStartPosition.CenterScreen;
-
- //到达时间的时候执行事件
- timer.Elapsed += new System.Timers.ElapsedEventHandler(TimerTask);
- //设置是执行一次(false)还是一直执行(true)
- timer.AutoReset = true;
- //是否执行System.Timers.Timer.Elapsed事件
- timer.Enabled = true;
- }
-
- public void TimerTask(object source, System.Timers.ElapsedEventArgs e)
- {
- ShowState("系统服务:" + serviceName + (ExistService() ? "已安装,当前状态:" + serviceController.Status:"未安装"));
- }
-
- /// <summary>
- /// 操作状态
- /// </summary>
- /// <param name="message"></param>
- private void ShowState(string message)
- {
- this.textBoxMessage.AppendText(DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ") +message +"\n");
- }
- }
- }
服务与GUI的关系:
这里我们直接让服务代码输出到Service中:
得到如下图所示输出文件:
这样我们的RTVS项目就可以使用windows服务了。服务运行界面如下:
包含服务和可以参考实现的GUI,另外还提供可配置的Windows服务,下载地址:https://download.csdn.net/download/boonya/10502384