c#建立windows service樣本以及在asp.net中如何控制windows service

來源:互聯網
上載者:User

有關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> 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.