c#创建windows service示例 .

原文转自:http://dreambuild.blog.sohu.com/132659489.html

有关windows service的详细介绍,不在本文叙述。如果想了解,自己到网上去搜一搜,关键词就是“windows service”,搜索结果保证不会让你失望,呵呵!本文的预期读者即为对windows service有一定了解,但又没有编写过windows service程序的人。
首先咱不急于介绍如何如何编码,第一步干吗第二步干吗。在你需要动手之前,你要想清楚,我为什么要这么做,有没有其它更方便、简单、快捷的解决办法。如果我必须这么做,有什么好处等等这些。因为我自己就遇到过很多种类似的情况,高高兴兴地把东西做出来了,结果发现要么就是太复杂,要么就是客户不爽,要么就发现原来还有更简单的办法,总之就是一句:我做的东西,就项目管理者的角度而言,是没有用的,我做这个东西的时间白费了,延缓了项目的进度。当然,如果从个人自身水平和经验来说,那又是完全另外一种评论和结果了。
现在我假设你已经想得非常清楚了,狠下心来决定采用windows service来解决你在项目中遇到的问题了。你想知道如何创建一个windows service了。那么我接着介绍。为了便于理解,以下部分将分为几个部分分别讲述。
一、windows service示例使用的业务环境
之所以先介绍windows service示例使用的业务环境,是因为如果有这些介绍,后面理解起来更加容易。我在本文中所举的windows service示例,源于我们实际工作中一个web项目的需要,该项目是一个在线考试系统,其中有这么一个取舍,在线考试的时候,因为考生数量较多,为避免交卷时将答题信息一股脑儿往数据库里插入出错,采取了这样一种办法:先让所有的考生交卷,交卷的时候并不往数据库里插入数据,而是将考生答题情况生成一个xml文件,在考生交卷后上传到服务器固定的目录下,然后由程序去解析xml文件,抽取数据,插入到数据库。为了减轻服务器的压力,这些上传到服务器上的xml文件解析工作不能在考生考试时候进行,这样做是尽量减少考生考试时出错的机率。那么,如何对这些上传到服务器上的xml文件来解析,什么时间来解析,是必须要考虑的。如果不考虑这些,可以在考生交卷将包含答题信息的xml文件上传到服务器之后,由web系统直接执行一段代码,对固定目录下提交的xml文件扫描,读取数据并插入数据库。这也不失为一个好的办法。但如果想要在服务器压力较小的情况下再来对这些xml文件进行解析,参考网上的评论,据说有三个较好的办法:一是采用数据库的作业;二是采用windows的计划任务;三是采用windows service。不管怎么样,我最后决定采用windows service了。
二、windows service示例实现的功能
本文中的windows service示例,要实现代功能简单明确:1,定时扫描服务器固定目录下的xml格式文件;2,定时扫描固定目录下的xml文件,如果时间在晚上20到23点之间,提取xml文件中的数据,往本地数据库kaoshi中的表t_datiqk中插入数据;3,如果数据提取并插入成功,固定目录下的xml文件删除。
三、用c#在vs2005中创建windows service的步骤
前面介绍了很多本文中的windows service示例的情况,估计各位看官已经非常不耐烦了。那么好,现在进入真刀真枪的代码编写和操作步骤阶段。感谢微软,感谢vs2005,让我创建windows service如此的容易、快捷。接下来介绍在vs2005中用c#创建windows servcie的步骤。
1、创建windows service工程项目
打开vs2005,点击File-New-Project(偶用的是TeamSuit版VisioStudio),选择Windows Service。如下图。

在工程名称输入框中,输入GradeService(这个是windows service的名字,你爱怎么取随你,俺管不着),然后在下面选择项目的保存路径,点击OK即可,在解决方案浏览器中可以看到,vs2005已经自动为你生成了一些必要的文件,如下图

。将vs2005切换到属性浏览页面,Service1.cs会有以下属性:
Autolog                                    是否自动写入系统的日志文件
CanHandlePowerEvent       服务时候接受电源事件
CanPauseAndContinue      服务是否接受暂停或继续运行的请求
CanShutdown                       服务是否在运行它的计算机关闭时收到通知,以便能够调用 OnShutDown 过程
CanStop                                 服务是否接受停止运行的请求
ServiceName                        服务名
系统默认产生的文件名让我很不爽,我决定要改掉它。于是我右键单击“Service1.cs”,改为“GradeService”,如下图。

vs2005弹出一个窗口,如图,点击“是”。


2、完善windows service功能,增加业务代码
因为要记录日志,所以从工具栏的“Components”拖一个EventLog过来。另外,因为要定时执行,所以还需要一个计时器。从网上可以知道,有三种计时器。一种是System.Timers.Timer;一种是System.Threading.Timer;一种是System.Windows.Forms.Timer。大家都说用System.Timers.Timer好,没办法,那就用System.Timers.Timer吧。不过这个在设计页面上是看不到的,只有在GradeService.Designer.cs里面才可以看到它的声明。双击GradeService.cs[Design]页面,进入到代码页面。里面有两个空的函数,一个是OnStart,一个是OnStop。顾名思义,OnStart函数是服务启动时执行,OnStop函数在服务停止时执行。
下面是GradeService.cs的具体代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.IO;
using System.Web;
using System.Data.SqlClient;
using System.Configuration;

namespace GradeService
{
    public partial class GradeService : ServiceBase
    {
        public GradeService()
        {
            InitializeComponent();
            // 如果系统时间查看器中没有“Xml文件解析”这样的类别,就添加一个
            if (!System.Diagnostics.EventLog.Exists("Xml文件解析"))
            {
                System.Diagnostics.EventLog.CreateEventSource("Xml文件解析服务", "Xml文件解析");
            }
            // 设置日志的名字
            this.eventLog.Log = "Xml文件解析";
            // 设置日志的来源
            this.eventLog.Source = "Xml文件解析服务";
        }
        /// <summary>
        /// 服务启动
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            // TODO: Add code here to start your service.
            if (this.timer == null)
            {
                eventLog.WriteEntry("xml文件解析服务启动");
                timer = new System.Timers.Timer();
                // 每隔5分钟执行
                this.timer.Interval = 5 * 60 * 1000;
                // 设置timer可以激发Elapsed事件
                this.timer.Enabled = true;
                // 开始
                this.timer.Start();
                this.timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
            }
        }
        /// <summary>
        /// 时间间隔到达后执行的代码
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            // 停止计时
            this.timer.Stop();
            // 取系统当前时间
            DateTime now = DateTime.Now;
            // 判断当前时间是否在20到23点之间,如果是则执行业务代码,否则不执行
            if (now.Hour <= 23 && now.Hour >= 20)
            {
                // 获取App.config文件中xml文件保存路径
                string _basePath = System.Configuration.ConfigurationSettings.AppSettings["FilePath"];
                // 开始扫描文件
                this.ScanFile(_basePath);
            }
            // 开始计时
            this.timer.Start();
        }
        /// <summary>
        /// 服务停止
        /// </summary>
        protected override void OnStop()
        {
            // TODO: Add code here to perform any tear-down necessary to stop your service.
            if (this.timer != null)
            {
                this.timer.Enabled = false;
                this.timer.Stop();
                this.timer.Dispose();
                eventLog.WriteEntry("xml文件解析服务停止");
            }
        }        /// <summary>
        /// 解析文件并将数据插入数据库
        /// </summary>
        /// <param name="fileName">文件全路径</param>
        /// <param name="name">文件名</param>
        private void InsertData(string fileName, string name)
        {
            // 读取xml文件内容
            DataSet ds = new DataSet();
            ds.ReadXml(fileName);
            // 循环数据集并插入内容,采用事务
            string msg = "解析文件" + name + "成功!";
            // 获取连接数据库字符串
            string sqlConnString = System.Configuration.ConfigurationSettings.AppSettings

["SqlConnString"];
            if (sqlConnString == null)
            {
                eventLog.WriteEntry("在App.config文件中没有找到连接数据库的字符串,解析文件中止!");
                return;
            }
            // 建立数据库连接
            SqlConnection conn = new SqlConnection(sqlConnString);
            try
            {
                conn.Open();
            }
            catch(Exception e)
            {
                // 如果连接失败,记录原因
                eventLog.WriteEntry(e.Message);
                return;
            }
            // 开始事务
            SqlTransaction tran = conn.BeginTransaction();
            SqlCommand comm = new SqlCommand();

            comm.Transaction = tran;
            comm.Connection = conn;
            try
            {
                // 从xml文件名中获取准考证和试卷编号
                string temp = name.Substring(0, name.Length - 4);
                string[] tempXmlName = temp.Split('-');
                // 准考证号码
                string c_zhunkaozhm = tempXmlName[0].ToString();
                // 试卷编号
                string c_shijuanbh = tempXmlName[3].ToString();
                // Sql语句
                string strSqlInsert = "INSERT INTO t_datiqk

(c_zhunkaozhm,c_shijuanbh,c_timubh,c_timulx,c_fenzhi,c_geshilx,c_wentims,c_daan,c_huidada,c_datisj,c_zhe

ngque) VALUES

(@c_zhunkaozhm,@c_shijuanbh,@c_timubh,@c_timulx,@c_fenzhi,@c_geshilx,@c_wentims,@c_daan,@c_huidada,@c_da

tisj,@c_zhengque)";
                // Sql参数
                SqlParameter param;
                // 循环xml文件中抽取出来的数据
                for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
                {
                    comm.Parameters.Clear();
                    comm.CommandText = strSqlInsert;// Sql语句

                    param = new SqlParameter("@c_zhunkaozhm", SqlDbType.VarChar);// 准考证号
                    param.Value = c_zhunkaozhm;
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_shijuanbh", SqlDbType.VarChar);// 试卷编号
                    param.Value = c_shijuanbh;
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_timubh", SqlDbType.VarChar);// 题目编号
                    param.Value = ds.Tables[0].Rows[i]["c_bianhao"].ToString();
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_timulx", SqlDbType.Int);// 题目类型
                    param.Value = Convert.ToInt32(ds.Tables[0].Rows[i]["c_timulx"]);
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_fenzhi", SqlDbType.Float);// 分值
                    param.Value = Convert.ToSingle(ds.Tables[0].Rows[i]["c_fenzhi"]);
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_geshilx", SqlDbType.Int);// 格式类型
                    param.Value = Convert.ToInt32(ds.Tables[0].Rows[i]["c_geshilx"]);
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_wentims", SqlDbType.VarChar);// 问题描述
                    param.Value = ds.Tables[0].Rows[i]["c_wentims"].ToString();
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_daan", SqlDbType.Int);// 正确答案
                    param.Value = Convert.ToInt32(ds.Tables[0].Rows[i]["c_zhengqueda"]);
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_huidada", SqlDbType.Int);// 考生回答答案
                    param.Value = Convert.ToInt32(ds.Tables[0].Rows[i]["c_daan"]);
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_datisj", SqlDbType.DateTime);// 答题时间
                    param.Value = Convert.ToDateTime(ds.Tables[0].Rows[i]["c_datisj"]);
                    comm.Parameters.Add(param);

                    param = new SqlParameter("@c_zhengque", SqlDbType.Int);// 是否正确
                    param.Value = Convert.ToInt32(ds.Tables[0].Rows[i]["c_zhengque"]);
                    comm.Parameters.Add(param);

                    comm.ExecuteNonQuery();// 执行更新
                }
                tran.Commit();
                // 扫描成功后删除xml文件
                if (File.Exists(fileName))
                {
                    File.Delete(fileName);
                }
            }
            catch (Exception ex)
            {
                // 会滚sql操作
                tran.Rollback();
                // 获取异常信息
                msg = ex.Message;
            }
            finally
            {
                conn.Close();
            }
            // 将操作信息或异常信息写入日志(日志可以在系统的事件查看器中看到)
            eventLog.WriteEntry(msg);
        }
        /// <summary>
        /// 循环扫描具体路径下的文件
        /// </summary>
        /// <param name="filePath"></param>
        private void ScanFile(string filePath)
        {
            // 创建DirectoryInfo实例
            DirectoryInfo dirInfo = new DirectoryInfo(filePath);
            // 得到源目录的文件列表
            FileInfo[] files = dirInfo.GetFiles();
            // 循环解析文件
            for (int i = 0; i < files.Length; i++)
            {
                // 判断文件的后缀是否为xml
                string postFix = files[i].Extension;
                // 获取全路径
                string fileNme = files[i].FullName;
                // 获取文件名
                string name = files[i].Name;
                if (postFix == ".xml")
                {
                    this.InsertData(fileNme, name);
                }
            }
        }
    }
}
3、将安装程序添加到服务应用程序
想要把windows service安装起来,不是双击GradeService.exe就可以的,它和普通的可执行文件安装办法不一样。

首先,我们要给windows service添加Installer。右键点击设计视图,选择Add Installer,VS将会为我们添加ProjectInstaller.cs,并在ProjectInstaller中添加组件serviceInstaller1和serviceProcessInstaller1,现在我们来修改他们的属性来控制Service的安装和启动选项。在ProjectInstaller得设计视图中选中serviceProcessInstaller1,将它得Account属性选为LocalSystem,这样以这个帐号服务启动。如果你希望系统启动时自动启动服务得话,将serviceInstaller1的StartType的属性选为Automatic,如果手动启动的话,选为manaul。
其次,要安装service,我们要用到IntallUtil.exe这个程序,这个程序位于C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727.点击开始菜单,选择“运行”,在运行对话框中输入cmd,进入到命令行窗口,输入cd :/WINDOWS/Microsoft.NET/Framework/v2.0.50727,进入到这个目录,然后输入installutil F:/project/考试系统/项目代码/Service/GradeService/GradeService/bin/Debug/GradeService.exe, installutil后边的内容就是我们的工程生成的可执行程序的路径,可以根据你自己的实际情况进行修改。

如果你给ServiceInstaller1的StartType设为Automatic的话,安装完服务,服务已经运行起来了,如果StartType是Manual的话,你需要手动启动。现在我们进入“服务”,要打开“服务”,请单击“开始”,指向“设置”,然后单击“控制面板”。依次单击“性能和维护”、“管理工具”,然后双击“服务”。在里边你应该能够看到我们制作的Service GradeService。在这里边,我们可以启动,关闭服务,还可以设置服务的启动类型。然后,我们看看服务有没有正确的写入日志,我们需要进入到事件查看器,要打开“事件查看器”,请单击“开始”,指向“设置”,然后单击“控制面板”。单击“性能和维护”,单击“管理工具”,然后双击“事件查看器”。如下图所示,我们的消息已经成功的写到了系统日志里了,下图。

 

四、在使用windows service过程中发现的小问题
在使用windows service中,因为有一些变量,不想写死,想保存在配置文件中,于是给示例service加了一个配置文件,叫App.config。其中定义了一些key,比如<add key="SqlConnString" value="Data Source=(local);Database=kaoshi;User ID=sa;Password=780910;"/>。但是发现,如果服务已经安装好后,去修改App.config中的key值,对服务并没有影响,windows servcie仍然按照App.config文件修改前的key值运行。不知道其他兄弟姐妹有没有遇到这种情况。

五、在asp.net中如何控制自己创建的windows service(网上流传,尚未验证是否属实)
windows service是可以在asp.net中进行控制的,你可以在asp.net构建的web项目中对服务器的windows service进行控制。但是要有两个前提。
1、在web项目解决方案资源管理器中添加引用System.ServiceProcess.dll;
然后在.cs中
using System.ServiceProcess;
然后在事件中写代码:
   ServiceController sc=new ServiceController("GradeServiceContrller");// 建立服务控制类对象
   if(sc.Status==ServiceControllerStatus.Stopped)
   {
    sc.Start();// 开始服务
   }
2、在web.config中模拟一个管理员用户。
如admin(属于administrator组.)
如下所示:
<configuration>
   
  <system.web> 
  <identity impersonate="true" userName="admin" password="admin" />
 </system.web>

</configuration>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章