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>

 

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