VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • c#发现之旅—c#开发windows service程序下

制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
 

  类似的对于“停止服务”,其点击事件处理为

private void btnStopService_Click(object sender, EventArgs e)
{
    if (bolServiceInstalled == false)
        return;
    using (System.ServiceProcess.ServiceController control = new System.ServiceProcess.ServiceController("MyFileSystemWatcher"))
    {
        if (control.Status == System.ServiceProcess.ServiceControllerStatus.Running)
        {
            control.Stop();
        }
    }
}

  在这个处理过程中,若判断出服务状态为运行中,则调用控制器的Stop方法来停止服务。在这里Stop方法内部只是通知操作系统停止指定的服务,它发送通知后立即返回,不会等待服务停止后返回。

  我们还在窗体上放置一个定时器控件,定时间隔为2秒,用于根据服务的状态刷新工具条按钮状态,其定时事件处理为

private void myTimer_Tick(object sender, EventArgs e)
{
    if (bolServiceInstalled == false)
        return;
    using (System.ServiceProcess.ServiceController control = new System.ServiceProcess.ServiceController("MyFileSystemWatcher"))
    {
        btnStartService.Enabled = (control.Status == System.ServiceProcess.ServiceControllerStatus.Stopped);
        btnStopService.Enabled = (control.Status == System.ServiceProcess.ServiceControllerStatus.Running);
    }
}

 

  在这里我们创建了一个绑定到文件系统监控服务的ServiceController对象,然后根据它的Status状态来设置“启动服务”和“停止服务”按钮的可用状态。

  系统配置对话框 dlgConfig

  在客户端主窗体中点击工具条的“系统配置”按钮就会弹出系统设置对话框,该对话框的用户界面为

C#发现之旅:C#开发Windows Service程序(下)

  该对话框比较简单,就是用于显示和修改系统配置信息对象MyConfig中的内容。由于文件系统监视服务只有在启动的时候读取系统配置信息,因此对系统配置的任何修改都需要重新启动服务才能生效。

  系统配置信息对象 MyConfig

  系统配置信息对象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

  类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框架是可以低成本的开发出一些功能强大的软件。

 

 

  在这里我们创建了一个绑定到文件系统监控服务的ServiceController对象,然后根据它的Status状态来设置“启动服务”和“停止服务”按钮的可用状态。

  系统配置对话框 dlgConfig

  在客户端主窗体中点击工具条的“系统配置”按钮就会弹出系统设置对话框,该对话框的用户界面为

C#发现之旅:C#开发Windows Service程序(下)

  该对话框比较简单,就是用于显示和修改系统配置信息对象MyConfig中的内容。由于文件系统监视服务只有在启动的时候读取系统配置信息,因此对系统配置的任何修改都需要重新启动服务才能生效。

  系统配置信息对象 MyConfig

  系统配置信息对象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; }
}



相关教程