在我们日常工作中,不免需要编写windows后台服务程序。在这过程中,你是否有考虑过这几个问题呢?
- 服务调试很麻烦?
- 双击不能启动服务?
- 如何安装服务?
- 如何使用管理员权限启动?
针对以上问题,今天我们就来聊聊它们!
调试服务
在调试服务前,我们肯定需要先新建一个服务的解决方案。打开 Visual Studio ,“新建项目” -> “Windows 服务” 点击确定。如下图所示:
新建windows服务工程后,我们就可以在service1.cs文件里面添加我们的逻辑代码(跟普通写程序一样,这里就不做赘述)。 我们可以尝试随便写一些逻辑代码,并准备开始调试。 调试的方式有几种:
-
通过附加进程和断点调试
因为服务一般情况下是不能通过双击启动的,必须得先把服务安装好后才能调试
这个时候,OnStart/OnStop方法在执行的时候,我们没有办法去调试我们的代码,整个调试过程会变得很困难。 这个时候,我们可能会考虑采用第二种方案(即下面的这种方案)。 -
通过Debugger.Launch()
在这种方案下,我们只需要将
Debugger.Launch()
这段代码插入到期望中断的代码位置,服务在启动的时候,便会把启动调试器的窗口弹出来,我们选择一个调试器就可以进去调试了。 如下图所示:
随后的调试方法跟普通调试一样。不过这种方式也存在比较明显的缺点:每次调试一次都会弹出 打开调试器窗口 ,费时费力,体检较差。
接下来看看第三种方法。 -
通过直接启动方式调试
在种模式下,我们首先需要了解一点,程序即时控制台,服务也不例外。在使用这种模式之前,我们首先需要将我们之前添加的服务程序设置成控制台程序。 具体步骤:选择我们需要设置的项目,右键查看属性,将输出类型改成“控制台应用程序”
到这里,还没有结束,因为我们之前添加项目是以服务的方式添加,所以我们得在 Program.cs 文件里面修改主函数的输入参数。
主函数:static void Main(string[] args)
到这里,我们的基本设置就完成了,接下来就需要它的这种模式,开启控制台启动逻辑了。 我们首先需要判断当前是否是用户交互模式,即如果是鼠标双击启动,则为用户交互模式;如果是服务启动,则不是用户交互模式。
判断当前是否是用户交互模式: Environment.UserInteractive
通过它,我们就可以针对两种不同的模式分别写我们的代码。下面是个完整的例子:
static void Main(string[] args) { if (Environment.UserInteractive) { //以控制台模式启动 //服务好控制台共用的代码,初始化服务 MainService mainService = new MainService(); Console.WriteLine("正在启动服务"); //这行代码,是服务OnStart的执行代码,自行修改。 mainService.Start(); Console.WriteLine("服务已启动,输入 exit 退出服务"); while (Console.ReadLine() != "exit") { //do something } //这行代码,是服务的OnStop的执行代码 mainService.Stop(); } else { //以服务模式启动 ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new MainService() }; ServiceBase.Run(ServicesToRun); } }
使用这种方法,我们可以通过控制台的输出来监控我们的程序运行是否正常,用来做测试也非常方便,可以随便打断点,跟普通的控制台程序的使用一摸一样。
安装服务
当我们开发完服务程序后,会面临这样一样问题:我应该怎么安装我的服务呢?如果给用户更好的体验?接下来,我们针对这一问题,简单的聊一聊! 服务安装的方式有多种,常见的方法有这么两种
-
使用cmd命令进行安装。
这种模式下,我们需要调起cmd.exe,输入命令行来完成我们的安装。 例如:sc create “{服务名}” binpath =”{服务启动的程序绝对路径}”。后面也可以跟随参数,标记是否是自动启动等。也可以通过sc delete “{服务名}” 这种方式来删除服务。 具体细节,这里就不做赘述,感兴趣可以自行查阅,我们主要讨论第二种方法。
-
使用C#代码来完成安装。
使用cmd命名的模式安装服务,难免会影响体验,也不好控制,虽然我们也可以在我们的代码里面去调用cmd来完成服务的安装,相对会显得很麻烦。所以,我们可以选择接下来讨论这种方式来安装我们的服务。 双击打开我们刚才创建的服务,以上例子是 MainService.cs,我们会看到一个设计面板。
鼠标右键,选择添加安装程序,如下图所示:
创建安装程序后,这个时候会在项目中自动生成一个 ProjectInstaller.cs 文件,这个文件里面就包含了我们的安装信息。 在这个文件里面,我们可以看到两个安装组件:serviceInstaller 和 serviceProcessInstall。
在serviceProcessInstall安装组件中,我们主要关注它的访问权限设置,其他的默认就好。我们通常将访问权限设置为LocalSystem ,权限最大(注意:设置为这个权限,安装的时候需要以管理员权限运行)。 具体区别可以参见:https://docs.microsoft.com/zh-cn/windows/desktop/Services/localsystem-account。
serviceProcessInstall设置完成后,我们接下来看下serviceInstaller,选中它,查看属性面板:
我们会发现他有更多的设置项,主要有:ServiceName (服务名) ,DisplayName (服务显示名),StartType (启动类型), DelayedAutoStart (是否延时启动),ServicesDependedOn (服务运行所依赖的其它服务列表),Description (服务描述)。
通常我们主要前面三个就可以了,根据自己需要设置好就行了。
特别注意:服务安装程序设置好了后,它并不会自己进行安装,它需要由其它的程序来调用它,才能真正完成安装。为了测试,我们可以新建一个测试的demo,用来安装这个服务(比如说在程序启动的时候,调用安装服务逻辑)。这里贴出关键代码。
/// <summary>
/// 安装服务
/// </summary>
/// <param name="serviceFilePath">服务程序的绝对路径</param>
public static void Install(string serviceFilePath)
{
using (AssemblyInstaller installer = new AssemblyInstaller())
{
installer.UseNewContext = true;
installer.Path = serviceFilePath;
IDictionary savedState = new Hashtable();
installer.Install(savedState);
installer.Commit(savedState);
}
}
/// <summary>
/// 卸载服务
/// </summary>
/// <param name="serviceFilePath">服务程序的绝对路径</param>
public static void Uninstall(string serviceFilePath)
{
using (AssemblyInstaller installer = new AssemblyInstaller())
{
installer.UseNewContext = true;
installer.Path = serviceFilePath;
installer.Uninstall(null);
}
}
只需要在我们的程序中调用以上代码,就可以完成对服务的安装。
提升权限
前面我们有提及到,要安装服务,需要有足够的权限才能成功。不用担心,我们有办法来给我们的程序做提权。 我们找到刚才新建的demo程序,在项目的属性中,找到“安全性”选项卡,点击“启用ClickOnce安全设置”
这个时候项目中会自动生成一个 app.manifest 的配置文件 ,我们打开它:
我们可以看到红线部分的配置项,只需要将 level=”asInvoker” 修改成 requireAdministrator ,再保存。
设置完成后,再回到“安全性”选项卡,将“启用ClickOnce安全设置”取消选中。最后再编译后,我们看下我们的应用程序图标会发生变化,应用程序图标上多了个“盾牌”小图标,能看到这个,那么恭喜你成功了。
最后,可以运行程序愉快的玩耍了。
本文会经常更新,请阅读原文: https://huchengv5.gitee.io//post/Windows%E5%90%8E%E5%8F%B0%E6%9C%8D%E5%8A%A1%E8%B0%83%E8%AF%95%E4%B8%8E%E5%AE%89%E8%A3%85.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名胡承(包含链接: https://huchengv5.gitee.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。