编写Windows服务疑问1:操作过程
Windows 服务开发平时不太受人关注,毕竟那是高大上的项目类型,平常需求也用不上,很多老掉牙的家伙也只知有WinForm,仍不知有WPF,更别说Windows 服务了,正如陶渊明所写的,“不知有汉,无论魏晋”。
通常,就算要让程序开机启动,多数也只考虑设置一个启动项,也很少去想到开发Windows服务。如果程序需要自动启动,并且希望在后台完成一些东东,其实使用Windows服务也不错的。
正因为用的人少,那么说的人更少了,使得不了解它,想学又找不到资料的人也多。没事,老周没什么资本,唯一值得骄傲就是颜值低,尤其是脸皮长得稍厚一些,所以,就让老周写几篇烂博客,看看能不能给大家解惑,传道授业愧不敢当,解几个简单的惑还是可以的。
其实,Windows服务也是一个Windows标准程序,也是编译为.exe的,运行时在任务管理器中也是有进程的,但它与一般的可执行文件也有不同,至少你不能直接双击运行服务。服务隶属于系统对象,因此在你运行服务前,需要向系统注册它。就好比你坐公交要先买票一样(包括刷卡,无人售票等),当然,个别另类人种是不买票的,后门上再从后门下。
一个服务项目可以包含多个服务,就是说你建立一个应用程序项目,你可以在里面根据你的需要,声明多个服务。实现服务的方法是从ServiceBase派生出一个自定义的类,然后重写相关的虚方法,来定义你的代码,当然,不是全部虚方法都要重写,你只写你觉得需要的方法。不要问我ServiceBase类在哪个命名空间,自己打开“对象浏览器”去找,“对象浏览器”是个好用的东西,老周每天都会看它一眼,特别好看,比你到网上下载的那些文笔拙劣的垃圾小说好看,不信你去看看。
举个地球人都知道的例子,假设你需要在服务启动时打开某个端口,去监听客户端的连接请求,那么,你可以重写OnStart方法;当服务被停止时关闭监听,重写OnStop方法;当服务被暂停时向所有客户端发送一条服务器放假的消息,请重写OnPause方法;如果你希望在关机的时候做一些处理,比如清空犯罪痕迹,可以重写OnShutdown方法,当系统即将关机,并且你的服务还在运行的时候,这个方法就会被调用。
扯的太空洞了,是吧,那就来点年货,反正也快过年了。咱们就从头到尾做一遍,看看你能不能掌握。
要上厕所的赶紧上,下面开始干活。
1、在VS中新项一个空项目,我知道VS中就有Windows服务的项目模板,但,为了让大伙伴能够更好地理解,咱们从空白开始。
2、因为项目是空的,所以里面连毛都没有,为了让项目看起来比较像样,先来设置一下项目的基本属性,估计这个很少人会注意,因为平时我们建项目时,连属性文件都由VS帮我们建好了。在项目节点上右击,选择属性。然后在应用程序页上,填好需要的东西,你可以随便填,不用客气。
注意,在输出类型那里选择“Windows应用程序”,不要选控制台应用程序,因为服务是没有界面的,就是一个进程。而且,在Win 7以上的系统中(其实是Vista时代就是了),是禁止服务和桌面交互的。我也赞成这样做,老周特别讨厌那些服务动不动就弹出个窗口,相当恶心。服务进程就是做逻辑处理的,BS有UI的服务。
3、点击“程序集信息”按钮,填上相关信息,自己喜欢填什么就填什么。
填完后确定,就会自动生成AssemblyInfo.cs文件。然后保存并关闭项目属性窗口。
4、添加下面三个引用,不用我教了。先加这么几个,等不够用时再加。
5、好,现在可以开始了,我们来做第一个服务,服务名称叫HuiJi,显示名为“灰机”.
public class HuiJiService : ServiceBase
{
public HuiJiService()
{
ServiceName = "HuiJi";
} protected override void OnStart(string[] args)
{
Trace.WriteLine($"{this.ServiceName} - 正在起飞。");
} protected override void OnStop()
{
Trace.WriteLine($"{this.ServiceName} - 正在降落。");
}
}
由于ServiceName属性是ServiceBase类的公共属性,所以,你可以像我这样,在类的构造函数中设置,也可以不设置,等到在Main入口点中实例化HuiJiService时再赋值,公共属性在类外部可以访问。
6、好,这么个简单的服务做好了,然后我们要在Main入口点中运行它。
static void Main()
{
SV.HuiJiService hj = new SV.HuiJiService();
// 调用静态方法运行指定服务
ServiceBase.Run(hj);
}
一定要记住,在Main中要调用Run静态方法来运行你想要运行的服务,这个很重要,不要忘了。如果只有一个服务,就只传一个服务实例给它,如果你要运行N个服务,就传一个服务数组进去。
那么,是不是这样就能运行了呢。非也,虽然我们写好了服务,但操作系统不知道你这是啥玩意儿,你得写安装程序类,这样在向系统注册时,安装工具才会去查找安装器,对服务进行安装。
7、服务需要两个(至少)安装器,一个是安装服务本身,另一个用来安装服务进程。要注意的是,服务和服务进程是不同的,服务就是我们刚刚写的从ServiceBase类派生的那个,而服务进程就是我们这个应用的.exe文件运行时的进程。
哪怕你只有一个服务,至少也有两个安装器——服务和服务进程。如果有三个服务要安装,就要定义四个安装器(三个服务安装,一个进程安装),每个服务安装器只能对应一个服务。
正因为要>=2安装器,所以要从Installer类派生出来一个类,在里面实例化好各种安装器,然后统一加入到Installers集合中,这样,在安装服务时,安装工具就会自动扫描这个集合里面的安装器,逐个进行安装。
public class MyInstaller : Installer
{
ServiceInstaller svinstaller = null; //服务安装器
ServiceProcessInstaller processinstaller = null; //进程安装器
public MyInstaller()
{
// 实例化安装器
svinstaller = new ServiceInstaller();
// 设置的服务名称必须和自定义服务的名字相同
svinstaller.ServiceName = "HuiJi";
// 服务描述
svinstaller.Description = "23世纪触发式新型飞机。";
// 设置显示名称
svinstaller.DisplayName = "飞机";
// 启动方式为自动
svinstaller.StartType = ServiceStartMode.Automatic;
// 实例化进程安装器
processinstaller = new ServiceProcessInstaller();
// 设置帐户类型为本地系统,通常用这个
// 如果是远程服务,应指定登录的用户名和密码
processinstaller.Account = ServiceAccount.LocalSystem; // 把安装器添加到集合中
this.Installers.Add(svinstaller);
this.Installers.Add(processinstaller);
}
}
安装服务用ServiceInstaller,而且,必须必须注意,安装器上ServiceName指定的服务名字,必须要和刚才在Main中运行的服务的名字相同,不然,无法正确安装,也无法启动服务。
刚才我们定义服务的名字叫 HuiJi,所以这里也要指明为 HuiJi。
服务进程要用 ServiceProcessInstaller 来安装,实例化后,要指定它的帐户类型,对于在本机运行的服务,用LocalSystem就可以了(本地系统帐户)。一个应用程序用一个ServiceProcessInstaller就可以了,你用多个也没用,不信你可以试试在 Installers 集合中添加N个 ServiceProcessInstaller 实例,最后你发现,只有第一个进去的才会有效,其他的靠边站。而且,一个应用程序安装多个服务进程也没什么用。
最后,不要忘了把两个安装器添加进 Installers 集合中。
8、最关键一步,在刚铡定义的自定义安装类上应用RunInstaller特性,并且把参数设为true,这样做表示这个类是用于给安装工具发现,并且扫描安装的,这一步不能少,少了就无法安装。
[RunInstaller(true)]
public class MyInstaller : Installer
{
好了,包含一个服务的应用程序做好了,下面看看如何注册和运行它了。
首先,生成应用项目,不要直接运行,因为它不是普通的.exe。
然后,要知道,安装服务用到一个叫 InstallUtil.exe 的工具,这个工具在 C: \ Windows \ Microsoft.NET \ Framework 下面,自己去找。找到了随便你怎么弄,最把这个exe所在目录加入到系统变量path中,这样,你就很轻松地用了。
到项目生成目录 \ bin \ Debug 下面,确认已成功生成了.exe 文件,然后在文件管理器左上角的菜单中 文件 ,“打开命令提示符” -- 以管理员身份打开命令提示符。
然后输入installUtil xxxx.exe ,回车。如图。
看到提示安装成功,说明服务已经装上了。现在打开系统的本地服务管理器窗口,可以看到刚才的服务了。
现在,你可以启动和停止服务了。
我们刚刚是通过Trace来输出跟踪信息的,那如何调试呢。
首先,关掉VS,再以管理员身份运行VS,然后再打开你的项目。
接着,到服务管理器中,启动服务。
回到VS,执行菜单 调试 -- 附加到进程。选择我们服务应用程序的进程。
然后附加调试器,接着再把服务停止,再启动,你就会看到 输出 窗口中的跟踪信息了。
测试完后,可以把刚刚的服务卸载掉,你可以用 sc delete 服务名 命令来删掉。不过,最好还是用刚才的 installUtil 工具来卸载。很简单,只要加一个 /u 参数就可以了。比如:installUtil /u xxxx.exe。
然后,你再到服务管理器窗口刷新一下,刚刚的服务就不见了,被卸载了。
做完这个练习后,你应该知道 Windows 服务怎么耍了,至少基本的步骤你懂了。
示例源代码 :http://files.cnblogs.com/files/tcjiaan/sampleServiceapp.zip
编写Windows服务疑问1:操作过程的更多相关文章
- 编写Windows服务疑问2:探索服务与安装器的关系
首先,来弄两个服务,一个叫“飞机”,一个叫“火车”. public class FeiJiService : ServiceBase { public FeiJiService() { Service ...
- C#编写windows服务
项目要求: 数据库用有一张表,存放待下载文件的地址,服务需要轮训表将未下载的文件下载下来. 表结构如下: 过程: VS--文件-->新建项目-->windows-->windows服 ...
- 使用C语言编写windows服务一般框架
原文:使用C语言编写windows服务一般框架 编写windows服务和编写windows应用程序一样,有一些回调函数必须填写且向windows 服务管理器(service manager)进行注册, ...
- C#编写Windows 服务
C#编写Windows 服务 Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时 ...
- python实现编写windows服务
使用python编写windows服务 最近测试服务器上经常发生磁盘空间不足,每次手动清除比较麻烦,所以写个windows服务定时清理下.中间也遇到过几个坑,一起记录下来. 1.python实现win ...
- C# 编写windows服务及服务的安装、启动、删除、定时执行任务
一.编写windows服务 1.VS2017 - 创建服务Myservice 2.创建好项目之后 --- >> 双击 Service1.cs ---- >> 出现一个设计 ...
- c# 编写windows 服务,并制作安装包
对服务的认识有很多个阶段. 第一阶段:当时还在用c++,知道在一个进程里while(True){},然后里面做很多很多事情,这就叫做服务了,界面可能当时还用Console控制台程序. 第二阶段:知道了 ...
- 手把手教用C#编写Windows服务 并控制服务 安装、启动、停止、卸载
Windows服务 Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动, ...
- 第八篇--编写Windows服务
编写service服务参考网址:https://blog.csdn.net/nodeathphoenix/article/details/24181509 vc获得显示器状态(捕获息屏.亮屏网址):h ...
随机推荐
- C# Web应用调试开启外部访问
在用C#开发Web应用时有个痛点,就是本机用VS开启Web应用调试时外部机器无法访问此Web应用.这里将会介绍如何通过设置允许局域网和外网机器访问本机的Web应用. 目录 1. 设置内网访问 2. 设 ...
- 谈谈一些有趣的CSS题目(三)-- 层叠顺序与堆栈上下文知多少
开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...
- C++内存对齐总结
大家都知道,C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址.非空类的大小与类中非静态成员变量和虚函数表的多少有关. 而值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有 ...
- 自定义Inspector检视面板
Unity中的Inspector面板可以显示的属性包括以下两类:(1)C#以及Unity提供的基础类型:(2)自定义类型,并使用[System.Serializable]关键字序列化,比如: [Sys ...
- 微信开发 :WeixinPayInfoCollection尚未注册Mch 问题解决
在使用开源项目 SENPARC.WEIXIN SDK 调用微信支付接口的时候出现了WeixinPayInfoCollection尚未注册Mch,这个问题. 最后地解决方案是: 我这个傻逼忘了在全局Gl ...
- Java中用得比较顺手的事件监听
第一次听说监听是三年前,做一个webGIS的项目,当时对Listener的印象就是个"监视器",监视着界面的一举一动,一有动静就触发对应的响应. 一.概述 通过对界面的某一或某些操 ...
- prometheus监控系统
关于Prometheus Prometheus是一套开源的监控系统,它将所有信息都存储为时间序列数据:因此实现一种Profiling监控方式,实时分析系统运行的状态.执行时间.调用次数等,以找到系统的 ...
- jira的插件开发流程实践
怎么开头呢,由于自己比较懒,博客一直不怎么弄,以后克己一点,多传点自己遇到的问题和经历上来,供自己以后记忆,也供需要的小伙伴少走点弯路吧 最近公司项目需要竞标一个运维项目,甲方给予了既定的几种比较常用 ...
- 在开源中国(oschina)git中新建标签(tags)
我今天提交代码到主干上面,本来想打个标签(tags)的. 因为我以前新建过标签(tags),但是我现在新建的时候不知道入库在哪了.怎么找也找不到了. 从网上找资料也没有,找客服没有人理我,看到一个交流 ...
- MySQL常用命令
数据库登陆命令: mysql -uroot -p 2.提示输入密码: 3.登陆成功: 4.数据库修改相关命令: 修改数据库的编码格式: 语法格式为:ALTER {DATABASE|SCHEMA} [ ...