用Visual C#创建Windows服务程序
Windows服务以前被称作NT服务,是一些运行在Windows NT、Windows 2000和Windows XP等操作系统下用户环境以外的程序。在以前,编写Windows 服务程序需要程序员很强的C或C++功底。然而现在在Visual Studio.Net下,你可以运用C++或Visual C#或Visual Basic.Net很轻松的创建一个Windows 服务程序。同样,你还可以运用其他任何与CLR相容的语言来创建Windows 服务程序。本文就向大家介绍如何运用Visual C#来一步一步创建一个文件监视的Windows 服务程序,然后介绍如何安装、测试和调试该Windows 服务程序。
在介绍如何创建Windows 服务程序以前,我先向大家介绍一些有关Windows服务的背景知识。一个Windows 服务程序是在Windows操作系统下能完成特定功能的可执行的应用程序。Windows 服务程序虽然是可执行的,但是它不像一般的可执行文件通过双击就能开始运行了,它必须有特定的启动方式。这些启动方式包括了自动启动和手动启动两种。对于自动启动的Windows 服务程序,它们在Windows启动或是重启之后用户登录之前就开始执行了。只要你将相应的Windows 服务程序注册到服务控制管理器(Service Control Manager)中,并将其启动类别设为自动启动就行了。而对于手动启动的Windows 服务程序,你可以通过命令行工具的NET START 命令来启动它,或是通过控制面板中管理工具下的服务一项来启动相应的Windows 服务程序(见图1)。同样,一个Windows 服务程序也不能像一般的应用程序那样被终止。因为Windows 服务程序一般是没有用户界面的,所以你也要通过命令行工具或是下面图中的工具来停止它,或是在系统关闭时使得Windows 服务程序自动停止。因为Windows 服务程序没有用户界面,所以基于用户界面的API函数对其是没有多大的意义。为了能使一个Windows 服务程序能够正常并有效的在系统环境下工作,程序员必须实现一系列的方法来完成其服务功能。Windows 服务程序的应用范围很广,典型的Windows 服务程序包含了硬件控制、应用程序监视、系统级应用、诊断、报告、Web和文件系统服务等功能。
图1

二.创建Windows服务程序:
在介绍如何创建Windows 服务程序以前,我先向大家介绍一下.Net框架下与Windows服务相关的命名空间和其中的类库。.Net框架大大地简化了Windows 服务程序的创建和控制过程,这要归功于其命名空间中的功能强大的类库。和Windows 服务程序相关的命名空间涉及到以下两个:System.ServiceProcess和System.Diagnostics。
要创建一个最基本的Windows 服务程序,我们只需要运用.Net框架下的System.ServiceProcess命名空间以及其中的四个类:ServiceBase、ServiceInstaller、ServiceProcessInstaller以及ServiceController,其体系结构可见图2。
图2

其中ServiceBase类定义了一些可被其子类重载的函数,通过这些重载的函数,服务控制管理器就可以控制该Windows 服务程序了。这些函数包括:OnStart()、OnStop()、OnPause()以及OnContinue()等四个。而且ServiceBase类的子类还可以重载OnCustomCommand()函数来完成一些特定的操作。通过重载以上的一些函数,我们就完成了一个Windows 服务程序的基本框架,这些函数的重载方法如下:
protected override void OnStart(string[] args) { } protected override void OnStop() { } protected override void OnPause() { } protected override void OnContinue() { } |
ServiceBase类还为我们提供了一些属性,而这些属性是任何Widnows 服务程序所必须的。其中的ServiceName属性指定了Windows服务的名称,通过该名称系统就可以调用Windows服务了,同时其它应用程序也可以通过该名称来调用它的服务。而CanPauseAndContinue和CanStop属性顾名思义就是允许暂停并恢复和允许停止的意思。
要使得一个Windows 服务程序能够正常运行,我们需要像创建一般应用程序那样为它创建一个程序的入口点。在Windows 服务程序中,我们也是在Main()函数中完成这个操作的。首先我们在Main()函数中创建一个Windows服务的实例,该实例应该是ServiceBase类的某个子类的对象,然后我们调用由基类ServiceBase类定义的一个Run()方法。然而Run()方法并不就开始了Windows 服务程序,我们必须通过前面提到的服务控制管理器调用特定的控制功能来完成Windows 服务程序的启动,也就是要等到该对象的OnStart()方法被调用时服务才真正开始运行。如果你想在一个Windows 服务程序中同时启动多个服务,那么只要在Main()函数中定义多个ServiceBae类的子类的实例对象就可以了,方法就是创建一个ServiceBase类的数组对象,使得其中的每个对象对应于某个我们已预先定义好的服务。
{ System.ServiceProcess.ServiceBase[] MyServices; MyServices = new System.ServiceProcess.ServiceBase[] { new Service1(), new Service2() }; System.ServiceProcess.ServiceBase.Run(MyServices); } |
static void Main()
三.添加文件监视服务:
了解了Windows服务的基本体系结构和创建方法后,我们就可以试着往服务中添加一些实际的功能了。下面我将向大家介绍一个能监视本地文件系统的文件监视服务-FileMonitorService。该服务能根据预先设定的本地目录路径监视其中的文件包括子文件夹中的任何变化:文件创建、文件删除、文件改名、文件修改。同时,该服务还为每种变化创建了一个相对应的计数器,计数器的作用就是反映该种变化的频度。
首先,我们打开Visual Studio.Net,新建一个Visual C#的Windows服务的项目,如图3所示:
图3

在重载Windows服务的OnStart()函数之前,我们先给其类添加一些计数器对象,这些计数器分别对应了文件的创建、删除、改名以及修改等变化。一旦指定目录中的文件发生以上的某种变化,与其相对应的计数器就会自动加1。所有的这些计数器都是定义为PerformanceCounter类型的变量的,该类是包含在System.Diagnostics命名空间中的。
private System.Diagnostics.PerformanceCounter fileCreateCounter; private System.Diagnostics.PerformanceCounter fileDeleteCounter; private System.Diagnostics.PerformanceCounter fileRenameCounter; private System.Diagnostics.PerformanceCounter fileChangeCounter; |
之后我们便在类的InitializeComponent()方法中创建以上定义的各个计数器对象并确定其相关属性。同时我们将该Windows服务的名称设置为“FileMonitorService”,设定其即是允许暂停并恢复的又是允许停止的。
private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.fileChangeCounter = new System.Diagnostics.PerformanceCounter(); this.fileDeleteCounter = new System.Diagnostics.PerformanceCounter(); this.fileRenameCounter = new System.Diagnostics.PerformanceCounter(); this.fileCreateCounter = new System.Diagnostics.PerformanceCounter(); fileChangeCounter.CategoryName = "File Monitor Service"; fileDeleteCounter.CategoryName = "File Monitor Service"; fileRenameCounter.CategoryName = "File Monitor Service"; fileCreateCounter.CategoryName = "File Monitor Service"; fileChangeCounter.CounterName = "Files Changed"; fileDeleteCounter.CounterName = "Files Deleted"; fileRenameCounter.CounterName = "Files Renamed"; fileCreateCounter.CounterName = "Files Created"; this.ServiceName = "FileMonitorService"; this.CanPauseAndContinue = true; this.CanStop = true; servicePaused = false; } |
接着就是重载OnStart()函数和OnStop()函数,OnStart()函数完成了一些必要的初始化工作。在.Net框架下,文件的监视功能可以由FileSystemWatcher类来完成,该类是包含在System.IO命名空间下的。该Windows服务所要完成的功能包括了监视文件的创建、删除、改名和修改等变化,而FileSystemWatcher类包含所有了对应于这些变化的处理函数。
protected override void OnStart(string[] args) { FileSystemWatcher curWatcher = new FileSystemWatcher(); curWatcher.BeginInit(); curWatcher.IncludeSubdirectories = true; curWatcher.Path = System.Configuration.ConfigurationSettings.AppSettings ["FileMonitorDirectory"]; curWatcher.Changed += new FileSystemEventHandler(OnFileChanged); curWatcher.Created += new FileSystemEventHandler(OnFileCreated); curWatcher.Deleted += new FileSystemEventHandler(OnFileDeleted); curWatcher.Renamed += new RenamedEventHandler(OnFileRenamed); curWatcher.EnableRaisingEvents = true; curWatcher.EndInit(); } |
注意其中被监视的目录是存放在一个应用程序配置文件中的,该文件是一个XML类型的文件。这种做法的好处就是我们不必重新编译并发布该Windows服务而只要直接修改其配置文件就可以达到更改所要监视的目录的功能了。
当该Windows服务启动后,一旦被监视的目录中的文件发生某种变化,与其相对应的计数器的值便会相应的增加,方法很简单,只要调用计数器对象的IncrementBy()即可。
private void OnFileChanged(Object source, FileSystemEventArgs e) { if( servicePaused == false ) { fileChangeCounter.IncrementBy(1); } } private void OnFileRenamed(Object source, RenamedEventArgs e) { if( servicePaused == false ) { fileRenameCounter.IncrementBy(1); } } private void OnFileCreated(Object source, FileSystemEventArgs e) { if( servicePaused == false ) { fileCreateCounter.IncrementBy(1); } } private void OnFileDeleted(Object source, FileSystemEventArgs e) { if( servicePaused == false ) { fileDeleteCounter.IncrementBy(1); } } |
OnStop()函数即是停止Windows服务的,在该Windows服务中,服务一旦停止,所有的计数器的值都应归零,但是计数器并不提供一个Reset()方法,所以我们只好将计数器中的值减去当前值来达到这个目的。
protected override void OnStop() { if( fileChangeCounter.RawValue != 0 ) { fileChangeCounter.IncrementBy(-fileChangeCounter.RawValue); } if( fileDeleteCounter.RawValue != 0 ) { fileDeleteCounter.IncrementBy(-fileDeleteCounter.RawValue); } if( fileRenameCounter.RawValue != 0 ) { fileRenameCounter.IncrementBy(-fileRenameCounter.RawValue); } if( fileCreateCounter.RawValue != 0 ) { fileCreateCounter.IncrementBy(-fileCreateCounter.RawValue); } } |
同时,因为我们的Windows服务是允许暂停并恢复的,所以我们还得重载OnPause()函数和OnContinue()函数,方法很简单,只要设定前面定义的布尔值servicePaused即可。
protected override void OnPause() { servicePaused = true; } protected override void OnContinue() { servicePaused = false; } |
这样,该Windows服务的主体部分已经完成了,不过它并不有用,我们还必须为其添加安装文件。安装文件为Windows服务的正确安装做好了工作,它包括了一个Windows服务的安装类,该类是重System.Configuration.Install.Installer继承过来的。安装类中包括了Windows服务运行所需的帐号信息,用户名、密码信息以及Windows服务的名称,启动方式等信息。
[RunInstaller(true)] public class Installer1 : System.Configuration.Install.Installer { /// <summary> /// 必需的设计器变量。 /// </summary> private System.ComponentModel.Container components = null; private System.ServiceProcess.ServiceProcessInstaller spInstaller; private System.ServiceProcess.ServiceInstaller sInstaller; public Installer1() { // 该调用是设计器所必需的。 InitializeComponent(); // TODO: 在 InitComponent 调用后添加任何初始化 } #region Component Designer generated code /// <summary> /// 设计器支持所需的方法 - 不要使用代码编辑器修改 /// 此方法的内容。 /// </summary> private void InitializeComponent() { components = new System.ComponentModel.Container(); // 创建ServiceProcessInstaller对象和ServiceInstaller对象 this.spInstaller = new System.ServiceProcess.ServiceProcessInstaller(); this.sInstaller = new System.ServiceProcess.ServiceInstaller(); // 设定ServiceProcessInstaller对象的帐号、用户名和密码等信息 this.spInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem; this.spInstaller.Username = null; this.spInstaller.Password = null; // 设定服务名称 this.sInstaller.ServiceName = "FileMonitorService"; // 设定服务的启动方式 this.sInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic; this.Installers.AddRange( new System.Configuration.Install.Installer[] {this.spInstaller, this.sInstaller }); } #endregion } |
同样,因为该Windows服务中运用到了计数器对象,我们也要为其添加相应的安装文件,安装文件的内容和作用与前面的类似。限于篇幅,这里就不给出相应的代码了,有兴趣的读者可以参考文后附带的源代码文件。
到此为止,整个Windows服务已经 构建完毕,不过Windows 服务程序和一般的应用程序不同,它不能直接调试运行。如果你直接在IDE下试图调试运行之,就会报出如图4所示提示。
图4

根据其中提示,我们知道安装Windows服务需要用到一个名为InstallUtil.exe的命令行工具。而运用该工具安装Windows服务的方法是非常简单的,安装该Windows服务的命令如下:
installutil FileMonitorService.exe |
而要卸载该Windows服务,你只要输入如下的命令即可:
installutil /u FileMonitorService.exe |
Windows服务安装成功后,它便会出现在服务控制管理器中,如图5所示。
图5

这样,该文件监视的Windows服务就完成了,一旦我们对被监视的目录中的文件进行操作,相应的计数器就会运作,起到监视文件变化的作用。不过这个功能对于一般的用户而言没多大意义,然而你可以在此基础上添加新的功能,比如 构建一个后台的文件处理系统,一旦被监视的目录中的文件发生某种变化,Windows服务便对其进行特定的操作,而最终用户就不必去关心后台处理程序是如何实现的了。
四.总结:
本文向大家介绍了Windows服务的一些基本概念和 构建一般的Windows服务所需的方法,同时还向大家展示了一个具有文件监视功能的Windows 服务程序。通过本文,读者应该能体会到 构建Windows服务并不是想象中的那么复杂,这主要还得归功于.Net框架为我们所作的大量努力。同时,希望大家能在本文给出的实例的基础上 构建更加完善和更加强大的Windows 服务程序。最后希望本文对大家能有不少帮助。
相关文章:

poj 3321 Apple Tree
树状数组 题意:一个树,以树枝连接两个点的形式给出,固定以1为整棵树的根。苹果长在树的节点上,节点上只可能0或1个苹果,一开始每个节点都有1个苹果 有两种操作,C表示更改某个节点的苹果数,0变1,…
人工智能在网络贷款中鲜为人知的事
作者 | Laksh Mohan翻译| 火火酱~,责编 | 晋兆雨出品 | AI科技大本营头图 | 付费下载于视觉中国现在,科技已经成为推动企业发展壮大的基本要素之一。人工智能(AI)就是一个证明此类技术在商业领域走红的好例子,比如网络…

《HTML5与CSS3实战指南》——2.5 构建The HTML5 Herald
本节书摘来自异步社区《HTML5与CSS3实战指南》一书中的第2章,第2.5节,作者: 【美】Estelle Weyl , Louis Lazaris , Alexis Goldstein 更多章节内容可以访问云栖社区“异步社区”公众号查看。 2.5 构建The HTML5 Herald 我们已经介绍了页面结构的基础以及…

用.NET创建Windows服务
用.NET创建Windows服务 译者说明:我是通过翻译来学习C#的,文中涉及到的有Visual Studio.NET有关操作,我都根据中文版的VS.NET显示信息来处理的,可以让大家不致有误解。作者:Mark Strawmyer 我们将研究如何…

BGP local-preference MED属性实验
实验拓扑 实验配置 建立两个AS 65001、65000 AS65000内跑OSPF,并在R1上发布三个网段100.1.1.1 100.1.2.1 100.1.3.1 在R3 R5上聚合后发布给R4。 每台路由器都有一个对应的loopback地址。 实验过程 <R1>dis bgp ro Total Number of Routes: 10 BGP Local route…
加速产业生态算力升级,华为鲲鹏展翅福州
11月20日,为了让更多开发者了解鲲鹏计算生态体系,并且助力行业人才培养,由福建鲲鹏生态创新中心、福州市大数据基地开发有限责任公司联合举办的鲲鹏开发者训练营圆满完成。此次活动现场吸引到了大量的开发者参与,产、学、研各界人…

《CCNP TSHOOT 300-135认证考试指南》——2.2节故障检测与排除及网络维护工具箱
本节书摘来自异步社区《CCNP TSHOOT 300-135认证考试指南》一书中的第2章,第2.2节故障检测与排除及网络维护工具箱,作者 【加】Raymond Lacoste , 【美】Kevin Wallace,更多章节内容可以访问云栖社区“异步社区”公众号查看 2.2 故障检测与排…

在linux系统下实现音视频即时通讯的部分代码
由于使用习惯,Linux在中国受欢迎程度远不如windows,相应的软件也比较少,尤其是音视频类的软件,但是,这并不代表就完全没有。下面介绍一款强大的音视频即时通讯平台给大家,它就是——Anychat for Linux SDK。AnyChat是一…
文本分类六十年
作者 | Lucy出品 | AI科技大本营文本分类是自然语言处理中最基本而且非常有必要的任务,大部分自然语言处理任务都可以看作是个分类任务。近年来,深度学习所取得的前所未有的成功,使得该领域的研究在过去十年中保持激增。这些文献中已经提出了…

web service 和 remoting 有什么区别
其实现的原理并没有本质的区别,在应用开发层面上有以下区别:1、Remoting可以灵活的定义其所基于的协议,如果定义为HTTP,则与Web Service就没有什么区别了,一般都喜欢定义为TCP,这样比Web Service稍为高效一…

《实施Cisco统一通信管理器(CIPT1)》一2.4 使用分布式呼叫处理的多站点WAN部署模型...
本节书摘来异步社区《实施Cisco统一通信管理器(CIPT1)》一书中的第2章,第2.4节,作者: 【美】Dennis Hartmann译者: 刘丹宁 , 陈国辉 , 卢铭 责编: 傅道坤, 更多章节内容可以访问云栖社区“异步社…

【转】 LDA必读的资料
时间总是不够用,这里就不自己写了,摘自一篇转发的博客,感觉挺有用! 一个大牛写的介绍,貌似需FQ http://tedunderwood.wordpress.com/2012/04/07/topic-modeling-made-just-simple-enough/David M.Blei主页:…

sizeof 操作符详解
1. 定义: sizeof是何方神圣? sizeof 乃 C/C 中的一个操作符(operator)是也。简单说其作用就是返回一个对象或者类型所占的内存字节数。 MSDN上的解释为: The sizeof keyword gives the amount of storage, in bytes, a…
石锤!谷歌排名第一的编程语言,死磕这点,程序员都收益
日本最大的证券公司之一野村证券首席数字官马修汉普森,在Quant Conference上发表讲话:“用Excel的人越来越少,大家都在码Python代码。”甚至直接说:“Python已经取代了Excel。”事实上,为了追求更高的效率和质量&#…

《关系营销2.0——社交网络时代的营销之道》一T表示Technology(技术)
本节书摘来异步社区《关系营销2.0——社交网络时代的营销之道》一书中的第1章,作者: 【美】Mari Smith 译者: 张猛 , 于宏 , 赵俐 责编: 陈冀康, 更多章节内容可以访问云栖社区“异步社区”公众号查看。 T表示Technologyÿ…

jquery拖拽实现UI设计组件
想做一个UI设计的组件,左侧是控件列表,右边是编辑区域,左侧的控件可以重复拖拽到右侧然后进行编辑。 效果草图: 部分js代码: function domop(){//set drag and drop $( "#compls .component" ).each(functi…
六年磨一剑,全时发布音视频会议平台TANG,多款新品亮相
作者 | 高卫华出品 | AI科技大本营时隔六年,全时于11月26日在北京举办了“时间的力量2020新产品发布会“。发布会现场,全时创始人&CEO陈学军回顾了全时近年来的发展历程,并正式推出了全时云会议2020版,全时小智和全时云直播三…

考察新人的两道c语言题目
1> 如何判断一个板子的cpu 是big-endian 还是 Little-endian的?用c实现非常简单,10行左右,就可以判断了, 关键考察新人是否了解了什么是endian ,big-endian与little-endian的区别在哪里, 如果…

《Adobe After Effects CC经典教程》——导读
前 言 After Effects CC提供了一套完整的2D和3D工具,动态影像专业人员、视频特效艺术家、网页设计人员以及电影和视频专业人员都可以用这些工具创建合成图像、动画和特效。After Effects被广泛应用于电影、视频、DVD以及Web的后期数字制作之中。After Effects可以以…

scanf()函数的用法和实践
scanf()函数的用法和实践摘要: 本文阐述了基于ANSI,Win 95,Win NT上的 C/C语言中scanf()函数的用法,以及在实际使用中常见错误及对策。 关键词: scanf()一、 序言 在CSDN论坛的C/C版块,我时常见…
邢波出任全球第一所AI大学校长,履历横跨三门学科
整理 | 高卫华出品 | AI科技大本营近日,世界上第一家研究型人工智能大学——Mohamed bin Zayed University of Artificial Intelligence,简称MBZUAI大学(MBZUAI),任命著名华人AI学术教授邢波为校长。据悉,首…

Ubuntu 10.10 安装 libx11-dev
今天(2013-04-11)尝试安装 ImageMagick,结果发现 config.log 文件中包含了如下错误信息: fatal error: X11/Xlib.h: No such file or directory 也就是说缺少了 libx11-dev 包,心想这有什么难的,直接通过 a…

《计算机组成原理》----2.6 浮点数
本节书摘来自华章出版社《计算机组成原理》一书中的第2章,第2.6节, 作 者 Computer Organization and Architecture: Themes and Variations[英]艾伦克莱门茨(Alan Clements) 著,沈 立 王苏峰…

javascript/dom:原生的JS写选项卡方法
来源:http://www.jb51.net/article/30108.htm <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html xmlns"http://www.w3.org/1999/xhtml"><head><meta http-…
CSDN 星城大巡礼,长沙“科技之星”年度企业评选正式开启
2020年,长沙市委主要领导发出“软件产业再出发”的号召,颁布了软件三年行动计划。今年5月,CSDN 作为专业的 IT 社区,与长沙高新区签约,将全国总部落户长沙,这一战略决策,让CSDN与长沙的联结进一…

Linux下用C获取当前系统时间
#include <time.h> time_t time(time_t calptr); 返回的是日历时间,即国际标准时间公元1970年1月1日00 : 00 : 00以来经过的秒数。然后再调用 char *ctime(const time_t calptr) ; 转化为字符串表示 #include <stdio.h> #inc…

Java程序猿的JavaScript学习笔记(12——jQuery-扩展选择器)
计划按例如以下顺序完毕这篇笔记:Java程序猿的JavaScript学习笔记(1——理念) Java程序猿的JavaScript学习笔记(2——属性复制和继承) Java程序猿的JavaScript学习笔记(3——this/call/apply) J…
关于动态规划,你想知道的都在这里了!
作者 | Your DevOps Guy翻译| 火火酱~,责编 | 晋兆雨出品 | AI科技大本营头图 | 付费下载于视觉中国什么是动态规划?它又有什么重要的呢?在本文中,我将介绍由Richard Bellman在20世纪50年代提出的动态规划(dynamic pro…

Tcpdump命令的使用与示例——linux下的网络分析
顾名思义,TcpDump可以将网络中传送的数据包的“头”完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来帮助你去掉无用的信息。tcpdump就是一种免费的网络分析工具,尤其其提供了源代码&a…

document.getElementById与getElementByName的区别
document.getElementById( "id_Number ") 得到的是单个元素 document.getElementsByName( "name ") 得到的是数组 转载于:https://www.cnblogs.com/qiuh/archive/2013/04/16/3023596.html