转帖|其它|编辑:郝浩|2009-04-07 09:37:11.000|阅读 1359 次
概述:本课程说明了Windows Service程序的概念,并演示如何使用C#开发一个简单的Windows Service程序。
#慧都22周年庆大促·界面/图表报表/文档/IDE/IOT/测试等千款热门软控件火热促销中>>
系统配置信息对象MyConfig用于读取和修改保存在数据表SystemConfig中的系统配置信息。其包含的配置信息的代码如下
|
private bool bolLogRenamed = true; /// <summary> /// 是否记录重命名事件 /// </summary> public bool LogRenamed { get { return bolLogRenamed; } set { bolLogRenamed = value; } }
private bool bolLogChanged = true; /// <summary> /// 是否记录文件修改事件 /// </summary> public bool LogChanged { get { return bolLogChanged; } set { bolLogChanged = value; } } private bool bolLogCreated = true; /// <summary> /// 是否记录对象创建事件 /// </summary> public bool LogCreated { get { return bolLogCreated; } set { bolLogCreated = value; } } private bool bolLogDeleted = true; /// <summary> /// 是否记录对象删除事件 /// </summary> public bool LogDeleted { get { return bolLogDeleted; } set { bolLogDeleted = value; } }
private string[] myWatchedPaths = null; /// <summary> /// 监视的目录 /// </summary> public string[] WatchedPaths { get { return myWatchedPaths; } set { myWatchedPaths = value; } } |
它的Load方法用于从数据库中加载配置信息,其处理过程为
|
public void Load() { myWatchedPaths = null; System.Collections.ArrayList paths = new System.Collections.ArrayList(); using (System.Data.IDbCommand cmd = Util.DBConnection.CreateCommand()) { cmd.CommandText = "Select ConfigName , ConfigValue From SystemConfig"; System.Data.IDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { string Name = Convert.ToString(reader.GetValue(0)); if (Name == null) { continue; } Name = Name.Trim().ToLower(); string Value = Convert.ToString(reader.GetValue(1)); if (Name.StartsWith("path")) { paths.Add(Value.Trim()); } else if (Name == "logrenamed") { bolLogRenamed = Convert.ToBoolean(Value); } else if (Name == "logchanged") { bolLogChanged = Convert.ToBoolean(Value); } else if (Name == "logdeleted") { bolLogDeleted = Convert.ToBoolean(Value); } else if (Name == "logcreated") { bolLogCreated = Convert.ToBoolean(Value); } } } myWatchedPaths = (string[])paths.ToArray(typeof(string)); } |
在该方法中程序查询数据表SystemConfig中的配置项目名称和数据,若项目名称以“path”开头则为要监视的路径,而配置项logrenamed,logchanged,logdeleted,logcreated分别表示是否监视文件目录重命名,修改,删除和新建等操作。
MyConfig对象还有一个Save方法用于将系统配置信息保存到数据库中,其处理过程为
|
public void Save() { using (System.Data.IDbCommand cmd = Util.DBConnection.CreateCommand()) { cmd.CommandText = "Delete From SystemConfig"; cmd.ExecuteNonQuery(); cmd.CommandText = "Insert Into SystemConfig ( ConfigName , ConfigValue ) Values( ? , ? )" ; System.Data.IDbDataParameter pName = cmd.CreateParameter(); cmd.Parameters.Add( pName ); System.Data.IDbDataParameter pValue = cmd.CreateParameter(); cmd.Parameters.Add( pValue );
pName.Value = "LogRenamed"; pValue.Value = bolLogRenamed.ToString(); cmd.ExecuteNonQuery();
pName.Value = "LogChanged"; pValue.Value = bolLogChanged.ToString(); cmd.ExecuteNonQuery();
pName.Value = "LogDeleted"; pValue.Value = bolLogDeleted.ToString(); cmd.ExecuteNonQuery();
pName.Value = "LogCreated"; pValue.Value = bolLogCreated.ToString(); cmd.ExecuteNonQuery();
for (int iCount = 0; iCount < myWatchedPaths.Length; iCount++) { string path = myWatchedPaths[ iCount ] ; if( path == null || path.Trim().Length == 0 ) { continue ; } pName.Value = "path" + iCount ; pValue.Value = path ; cmd.ExecuteNonQuery(); } } } |
在这个方法中,首先删除数据表SystemConfig中所有的记录,然后将所有的配置信息保存到数据表SystemConfig中。
类MyFileSystemWatcherService就是文件系统监视服务,它是从ServiceBase派生的,首先说明一下执行文件系统监视的功能性的过程,其代码如下
|
/// <summary> /// 文件系统监视器列表 /// </summary> private System.Collections.ArrayList myWatchers = null;
/// <summary> /// 开始启动文件系统监视 /// </summary> /// <returns>操作是否成功</returns> internal bool StartFileSystemWatching() { myWatchers = new System.Collections.ArrayList(); MyConfig.Instance.Load(); string[] paths = MyConfig.Instance.WatchedPaths; System.Text.StringBuilder myPathList = new StringBuilder(); if (paths != null) { foreach (string path in paths) { if (System.IO.Path.IsPathRooted(path) == false) { continue; } string BasePath = null; string Filter = null;
if (System.IO.Directory.Exists(path)) { BasePath = path; Filter = "*.*"; } else { BasePath = System.IO.Path.GetDirectoryName(path); Filter = System.IO.Path.GetFileName(path); } if (BasePath == null) { continue; } BasePath = BasePath.Trim(); if (BasePath.ToUpper().StartsWith(System.Windows.Forms.Application.StartupPath)) { // 不能监视程序本身所在的目录的文件系统更改 continue; }
if (System.IO.Directory.Exists(BasePath) == false) { // 不能监视不存在的目录 continue; } if (myPathList.Length > 0) { myPathList.Append(";"); } myPathList.Append(path); System.IO.FileSystemWatcher watcher = new System.IO.FileSystemWatcher(); watcher.Path = BasePath; watcher.Filter = Filter; watcher.EnableRaisingEvents = true; watcher.IncludeSubdirectories = false; if (MyConfig.Instance.LogChanged) { watcher.Changed += delegate(object sender, System.IO.FileSystemEventArgs args) { WriteFileSystemLog(args.FullPath, args.ChangeType.ToString()); }; } if (MyConfig.Instance.LogCreated) { watcher.Created += delegate(object sender, System.IO.FileSystemEventArgs args) { WriteFileSystemLog(args.FullPath, args.ChangeType.ToString()); }; } if (MyConfig.Instance.LogDeleted) { watcher.Deleted += delegate(object sender, System.IO.FileSystemEventArgs args) { WriteFileSystemLog(args.FullPath, args.ChangeType.ToString()); }; } if (MyConfig.Instance.LogRenamed) { watcher.Renamed += delegate(object sender, System.IO.RenamedEventArgs args) { WriteFileSystemLog(args.FullPath, args.ChangeType.ToString()); }; } myWatchers.Add(watcher); }//foreach this.EventLog.WriteEntry( "开始监视文件系统 " + myPathList.ToString(), EventLogEntryType.Information); }//if return true; } |
在这个过程中,首先使用MyConfig.Load从数据库中加载系统配置,然后遍历所有需要监视的路径,对其中的每个路径解析出目录名和文件名,然后创建一个FileSystemWatcher对象,设置其Path和Filter属性,还根据MyConfig中的系统配置来绑定监视对象的Changed事件,Created事件,Deleted事件和Renamed事件,以实现对文件系统的监视。这里绑定事件的代码使用了C#2.0的匿名委托的语法功能。设置FileSystemWatcher对象后将该对象添加到文件系统监视器列表myWatchers中。
启动服务后使用EventLog.WriteEntry向Windows系统事件日志添加一些日志信息。
这里使用了一个WriteFileSystemLog方法,该方法代码为
|
private void WriteFileSystemLog(string ObjectName, string EventStyle ) { System.Data.IDbConnection conn = Util.DBConnection; if (conn == null) return; // 将监视结果添加到数据库中 using (System.Data.IDbCommand cmd = conn.CreateCommand()) { cmd.CommandText = "Insert Into FileSystemLog ( RecordID , WatchTime , ObjectName , EventStyle ) Values ( '" + System.Guid.NewGuid().ToString() + "' , '" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "' , ? , '" + EventStyle + "')" ; System.Data.IDbDataParameter p = cmd.CreateParameter(); p.Value = ObjectName; cmd.Parameters.Add(p); cmd.ExecuteNonQuery(); } } |
该方法参数是记录的文件或目录名,以及事件类型,程序首先拼凑出一个Insert的SQL语句,然后向数据表FileSystemLog添加一条数据。
类型MyFileSystemWatcherService还重载了ServiceBase的OnStart,OnStop,OnPause,OnContinue等方法来响应外界对服务过程的控制。
OnStart方法的代码如下,该方法调用StartFileSystemWatching函数就算完成了启动服务的操作。
|
protected override void OnStart(string[] args) { this.StartFileSystemWatching(); } |
OnStop方法的代码如下,该方法首先销毁掉所有正在运行的文件系统监视器,然后关闭数据库连接。
|
protected override void OnStop() { if (myWatchers != null) { foreach (System.IO.FileSystemWatcher w in myWatchers) { w.EnableRaisingEvents = false; w.Dispose(); } myWatchers = null; } Util.CloseDBConnection(); base.OnStop(); } |
OnPause方法代码如下,该方法设置所有的文件系统监视器不触发事件,这样软件不能感知文件系统的修改,因此也就暂停了对文件系统的监视。
|
protected override void OnPause() { if (myWatchers != null) { foreach (System.IO.FileSystemWatcher w in myWatchers) { w.EnableRaisingEvents = false; } } base.OnPause(); } |
OnContinue方法的代码如下,该方法重新设置所有的文件系统监视器能触发事件,因此软件又能监视文件系统的修改了。
|
protected override void OnContinue() { if (myWatchers != null) { foreach (System.IO.FileSystemWatcher w in myWatchers) { w.EnableRaisingEvents = true ; } } base.OnContinue(); } |
类型Util用于管理数据库连接,其代码为
|
private static System.Data.IDbConnection myDBConnection = null; /// <summary> /// 获得数据库连接对象 /// </summary> public static System.Data.IDbConnection DBConnection { get { if (myDBConnection == null) { myDBConnection = new System.Data.OleDb.OleDbConnection( "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=""" + System.IO.Path.Combine( System.Windows.Forms.Application.StartupPath, "FileSystemWatcher.mdb") + """"); myDBConnection.Open(); } return myDBConnection; } } /// <summary> /// 关闭数据库连接 /// </summary> public static void CloseDBConnection() { if (myDBConnection != null) { myDBConnection.Close(); myDBConnection = null; } } |
从这个代码可以看出软件使用的数据库是应用程序目录下的FileSystemWatcher.mdb数据库。为了提高效率,减少数据库的连接次数,服务在运行其间只连接一次数据库,使用完毕后不断开,只有退出软件时才断开数据库连接。
在类型Program中定义了Main函数,该函数就是本软件的启动入口方法。其代码为
|
[System.STAThread()] static void Main() { try { System.Uri uri = new Uri(typeof(string).Assembly.CodeBase); string RuntimePath = System.IO.Path.GetDirectoryName( uri.LocalPath ) ; string strInstallUtilPath = System.IO.Path.Combine(RuntimePath, "InstallUtil.exe"); foreach (string arg in System.Environment.GetCommandLineArgs()) { Console.WriteLine(arg); if (arg == "/install") { System.Diagnostics.Process.Start(strInstallUtilPath, """" + System.Windows.Forms.Application.ExecutablePath + """"); return; } else if (arg == "/uninstall") { System.Diagnostics.Process.Start(strInstallUtilPath, "/u """ + System.Windows.Forms.Application.ExecutablePath + """"); return; } else if (arg == "/client") { // 启动客户端 Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false);
using (frmClient frm = new frmClient()) { Application.Run(frm); //frm.ShowDialog(); Util.CloseDBConnection(); } return; } else if (arg == "/debug") { MyFileSystemWatcherService service = new MyFileSystemWatcherService(); service.StartFileSystemWatching(); System.Threading.Thread.Sleep(1000 * 600); return; } } } catch (Exception ext) { Console.WriteLine(ext.ToString()); return; } // 运行服务对象 ServiceBase.Run( new MyFileSystemWatcherService()); } |
Main函数决定调用本软件的那个功能模块,由于Main函数本身具有安装和卸载服务的功能,首先得找到微软.NET框架所带的InstallUtil.exe的完整的路径。微软.NET编程中,基础类型string属于mscorlib.dll,因此可以使用typeof(string).Assembly.CodeBase获得文件mscorlib.dll的绝对路径名,而InstallUtil.exe和mscorlib.dll是同一个目录的,因此也就能获得InstallUtil.exe的绝对路径名了。
我们使用System.Environment.GetCommandLineArgs()获得所有的命令行参数。遍历所有的参数,若存在“/install”则表示要安装服务,于是调用InstallUtil.exe来将软件本身注册为服务,若遇到“/uninstall”则调用InstallUtil.exe卸载服务,若遇到“/client”则调用客户端模块,若遇到“/debug”则创建服务对象,调用它的StartFileSystemWatching模拟启动服务,然后主线程阻塞掉,但此时文件系统监视的功能性模块还在运行,可以设置断点进行调试。
若没有遇到任何可识别的命令行参数,则调用ServiceBase.Run函数来执行服务。
由于向Windows系统注册自己为服务时没有指明任何命令行参数,因此服务管理器启动进程时不会添加任何命令行参数,因此本程序也就是以服务模式运行。若在Windows资源管理器中双击执行程序时也是以服务模式运行,此时没有相关的运行环境,程序启动后会报错。此时必须添加程序代码可识别的命令行参数。
程序编写完毕,编译通过,生成一个MyWindowsService.exe文件,我们就可以开始运行这个软件了。
首先我们得向系统注册服务,我们可以使用命令行“程序路径/MyWindowsService.exe /install”来注册服务,也可以直接运行“微软.NET框架路径/installutil.exe 程序路径/MyWindowsService.exe”;相反的,我们可以使用命令行“程序路径/MyWindowsService.exe /uninstall”或者“微软.NET框架路径/installutil.exe /u 程序路径/MyWindowsService.exe”来卸载服务。
安装服务后,我们可以使用命令行“程序路径/MyWindowsService.exe /client”来运行该服务的客户端软件了。
在本课程中,我们使用C#编写了一个简单的用于监视文件系统的Windows服务,包括服务器软件和客户端软件,若使用传统的C++开发服务这种底层程序需要熟悉大量的API函数,而微软.NET框架很好的封装了这些技术细节,简化了编程过程,使得我们可以把主要警力放在提供服务内容的功能性模块的开发上来,从这里可以看出基于微软.NET框架是可以低成本的开发出一些功能强大的软件。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@hmdbvip.cn
文章转载自:博客园