C#編寫window服務,一步一步(1)


Window服務是啥,這里就不廢話了,如何用在哪里用也不廢話了,這里我這篇文章只是詳述了我在vs2012中創建window服務的經過,希望對你有所幫助。

另外:我在編寫服務過程中參考了 Professional C# 2012 and   .NET 4.5

 

第一步,創建一個解決方案名稱MonitoringFish

  不廢話,你肯定會,會的直接去下一步。如果真的不會請繼續看

  

第二步添加服務用的類庫項目Sensor

      並添加類文件QuoteException.cs和SensorFish.cs

  這兩個類的功能並不重要,主要是給服務類用的,你也可以寫自己的類文件,或者干脆不要,直接在服務類里邊寫邏輯代碼

  QuoteException.cs代碼如下:

  

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 namespace Sensor
 7 {
 8     /// <summary>
 9     /// 自定義異常
10     /// </summary>
11     [Serializable]
12     class QuoteException : Exception
13     {
14         public QuoteException() { }
15         public QuoteException(string message) : base(message) { }
16         public QuoteException(string message, Exception inner) : base(message, inner) { }
17         protected QuoteException(
18         System.Runtime.Serialization.SerializationInfo info,
19         System.Runtime.Serialization.StreamingContext context)
20             : base(info, context) { }
21     }
22 }
View Code

 

   SensorFish.cs代碼如下:

  

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.IO;
  6 using System.Net;
  7 using System.Net.Sockets;
  8 using System.Threading.Tasks;
  9 using System.Diagnostics.Contracts;
 10 using System.Diagnostics;
 11 
 12 namespace Sensor
 13 {
 14     /// <summary>
 15     /// 傳感器監測
 16     /// </summary>
 17     public class SensorFish
 18     {
 19 
 20 
 21         private TcpListener listener;
 22         private int port;//端口號
 23         private string filename;
 24         private List<string> quotes;
 25         private Random random;
 26         private Task listenerTask;
 27 
 28         /// <summary>
 29         /// 傳感器控制類
 30         /// </summary>
 31         public SensorFish()
 32             : this("quotes.txt")
 33         {
 34 
 35         }
 36 
 37         /// <summary>
 38         /// 傳感器控制類
 39         /// </summary>
 40         /// <param name="fileName"></param>
 41         public SensorFish(string fileName)
 42             : this(fileName, 7890)
 43         {
 44 
 45         }
 46 
 47         /// <summary>
 48         /// 傳感器控制類
 49         /// </summary>
 50         /// <param name="fileName"></param>
 51         /// <param name="port"></param>
 52         public SensorFish(string fileName, int port)
 53         {
 54             //Contract.Requires<ArgumentNullException>(fileName != null);
 55             //Contract.Requires<ArgumentException>(port >= IPEndPoint.MinPort && port <= IPEndPoint.MaxPort);
 56             this.filename = fileName;
 57             this.port = port;
 58         }
 59 
 60         protected void ReadQuotes()
 61         {
 62             try
 63             {
 64                 quotes = File.ReadAllLines(filename).ToList();
 65                 if (quotes.Count == 0)
 66                 {
 67                     throw new QuoteException("quotes  file  is   empty");
 68                 }
 69                 random = new Random();
 70             }
 71             catch (IOException ex)
 72             {
 73                 throw new QuoteException("I/O Error", ex);
 74             }
 75         }
 76 
 77         protected string GetRandomQuoteOfTheDay()
 78         {
 79             int index = random.Next(0, quotes.Count);
 80             return quotes[index];
 81         }
 82 
 83 
 84         /// <summary>
 85         /// 開啟服務
 86         /// </summary>
 87         public void Start()
 88         {
 89             ReadQuotes(); //讀取文件
 90             listenerTask = Task.Factory.StartNew(Listener, TaskCreationOptions.LongRunning);//異步方法調用
 91         }
 92 
 93         private void Listener()
 94         {
 95             try
 96             {
 97                 IPAddress ipAddress = IPAddress.Any;//提供一個ip地址,只是服務器應偵聽所有網絡接口上的客戶端活動。此字段為只讀
 98                 listener = new TcpListener(ipAddress, port);//指定在本地的IP地址和端口號上偵聽是否有傳入的連接嘗試
 99                 listener.Start();//開始偵聽傳入的連接請求
100                 while (true)
101                 {
102                     Socket clientSocket = listener.AcceptSocket();//接受關起的連接請求
103                     string message = GetRandomQuoteOfTheDay();
104                     var encoder = new UnicodeEncoding();
105                     byte[] buffer = encoder.GetBytes(message);
106                     clientSocket.Send(buffer, buffer.Length, 0);//將指定的字節數發送到已連接的Socket
107                     clientSocket.Close();//關閉Socket,並釋放所有的關聯的資源
108                 }
109             }
110             catch (SocketException ex)
111             {
112                 Trace.TraceError(string.Format("QuoteServer {0}", ex.Message));
113                 throw new QuoteException("socket error", ex);
114             }
115         }
116 
117 
118         /// <summary>
119         /// 停止服務
120         /// </summary>
121         public void Stop()
122         {
123             listener.Stop();//關閉偵聽
124         }
125 
126         /// <summary>
127         /// 暫定服務
128         /// </summary>
129         public void Suspend()
130         {
131             listener.Stop();
132         }
133 
134         /// <summary>
135         /// 重新開始服務
136         /// </summary>
137         public void Resume()
138         {
139             Start();
140         }
141 
142         /// <summary>
143         /// 重啟
144         /// </summary>
145         public void RefreshSensor()
146         {
147             ReadQuotes();
148         }
149     }
150 }
View Code

 

  

第三步添加控制台應用程序SensorServiceTest

  這里要說下為什么添加這個控制台程序了。

  因為在開發過程中要對Sensor項目進行調試,為了方便用 SensorServiceTest承載這個類庫,作為服務使用。在第四步的程序中將會調用這個服務,以便驗證Sensor中的各個類功能是否正常。

  

  直接主函數中加入代碼,如下:

  

 1     /// <summary>
 2     /// 服務測試程序
 3     /// </summary>
 4     class Program
 5     {
 6         static void Main(string[] args)
 7         {
 8 
 9             var qs = new SensorFish("Quotes.txt", 4567);
10             qs.Start();
11             Console.WriteLine("Hit return to  exit");
12             Console.ReadLine();
13             qs.Stop();
14         }
15      }

 

第四步添加wpf應用程序項目ServiceTestClicent

  用於配合第三步創建服務測試Sensor項目,請注意配置項目屬性頁的【設置】選項卡的鍵值如下圖所示

  

 

  這個項目中我創建了一個MainWindow.xaml文件和QuoteInformation.cs類用於客戶端程序的調用,當然在創建wpf項目時候自動生成了app.config(非必須)和App.xaml(必須)

  xaml文件代碼如下:

  

 1 <Window x:Class="ServiceTestClicent.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="MainWindow"  Height="200" Width="300">
 5     <Grid>
 6         <Grid.RowDefinitions>
 7             <RowDefinition Height="*" MinHeight="30"></RowDefinition>
 8             <RowDefinition Height="3*"></RowDefinition>
 9         </Grid.RowDefinitions>
10 
11         <Button     Margin="3" VerticalAlignment="Stretch" Grid.Row="0" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"
12                 IsEnabled="{Binding EnableRequset}" Click="OnGetQuote" >Get Quote</Button>
13         <TextBlock  Margin="6" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Quote}" />
14     </Grid>
15 </Window>

 

 

 

   

 1 using System;
 2 using System.Net.Sockets;
 3 using System.Text;
 4 using System.Windows;
 5 using System.Windows.Input;
 6 
 7 namespace ServiceTestClicent
 8 {
 9     /// <summary>
10     /// MainWindow.xaml 的交互邏輯
11     /// </summary>
12     public partial class MainWindow : Window
13     {
14         private QuoteInformation quoteInfo = new QuoteInformation();
15         public MainWindow()
16         {
17             InitializeComponent();
18             this.DataContext = quoteInfo;
19         }
20 
21         private async void OnGetQuote(object sender, RoutedEventArgs e)
22         {
23             const int bufferSize = 1024;
24             Cursor currentCursor = this.Cursor; //代表用於鼠標指針的圖像
25             quoteInfo.EnableRequest = false;
26 
27             string serverName = Properties.Settings.Default.ServerName; //url
28             int port = Properties.Settings.Default.PortNumber;//端口
29             var client = new TcpClient();//
30             NetworkStream stream = null;
31             try
32             {
33                 await client.ConnectAsync(serverName, port);
34                 stream = client.GetStream();
35                 byte[] buffer = new Byte[bufferSize];
36                 int received = await stream.ReadAsync(buffer, 0, bufferSize);
37                 if (received <= 0)
38                 {
39                     return;
40                 }
41 
42                 quoteInfo.Quote = Encoding.Unicode.GetString(buffer).Trim('\0');
43 
44             }
45             catch (SocketException ex)
46             {
47                 MessageBox.Show(ex.Message, "Error Quote of  the  day", MessageBoxButton.OK, MessageBoxImage.Error);
48             }
49             finally
50             {
51                 if (stream != null)
52                 {
53                     stream.Close();
54                 }
55 
56                 if (client.Connected)
57                 {
58                     client.Close();
59                 }
60                 this.Cursor = currentCursor;
61                 quoteInfo.EnableRequest = true;
62             }
63 
64 
65         }
66     }
67 
68    
69 
70 }
View Code

 

  QuoteInformation.cs類代碼如下

  

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Linq;
 5 using System.Runtime.CompilerServices;
 6 using System.Text;
 7 
 8 namespace ServiceTestClicent
 9 {
10     class QuoteInformation : INotifyPropertyChanged
11     {
12         public QuoteInformation()
13         {
14             EnableRequest = true;
15         }
16         private string quote;
17         public string Quote
18         {
19             get
20             {
21                 return quote;
22             }
23             internal set
24             {
25                 SetProperty(ref quote, value);
26             }
27         }
28 
29         private bool enableRequest;
30         public bool EnableRequest
31         {
32             get
33             {
34                 return enableRequest;
35             }
36             internal set
37             {
38                 SetProperty(ref enableRequest, value);
39             }
40         }
41 
42         private void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
43         {
44             if (!EqualityComparer<T>.Default.Equals(field, value))
45             {
46                 field = value;
47                 var handler = PropertyChanged;
48                 if (handler != null)
49                 {
50                     handler(this, new PropertyChangedEventArgs(propertyName));
51                 }
52             }
53         }
54 
55         public event PropertyChangedEventHandler PropertyChanged;
56     }
57 }
View Code

 

 

  在項目SensorServiceTestClient和ServiceTestClicent上右鍵-生成后打開在各自項目中的Debug文件夾下找到exe可執行文件,並先啟動SensorServiceTestClient.exe然后啟動ServiceTestClicent.exe

  啟動ServiceTestClicent.exe如下圖所示,表示各個程序功能正常

  

  經過測試功能正常,就可以真正的編寫windows服務了

第五步添加服務項目SensorFishService

  添加一個服務類FisheryMonitoring.cs

  

  在空白處點擊一下,以便選中該選項卡,然后打開屬性窗口看到下圖所示

  

  

  (Name)對應的是服務類的名稱

  AutoLog指定把啟動和停止服務的事件自動寫到事件日志中

  CanPauseAndContinue、CanShutdown和CanStop指定服務可以處理暫停、繼續、關閉和停止服務的請求

  ServiceName是寫到注冊表中的服務的名稱,使用這個名稱可以控制服務

  CanHandleSessionChangeEvent確定服務是否能處理終端服務器會話中的改變事件

  CanHandlePowerEvent選項對運行在筆記本電腦或移動設備上的服務有效。如果啟用這個選項,服務就可以響應低電源事件,並響應的改變服務的行為。電源事件包括電量低、電源狀態改變(因為A/C電源之間的切換)開關和改為斷電

 

  設置好各個屬性后,在服務類的選項卡上右鍵,選擇【添加安裝程序】

  

  

  

  選中 ServiceProcessInstaller1打開屬性選項卡

  

  設置一下 Account,如果將他的值設置為User那么在安裝服務的時候就要指定一個具體的賬戶,只有這個賬戶可以使用這個服務,其他的不詳,請查閱其他資料

   選中serviceInstaller1並打開屬性選項卡

  

  設置一下各個屬性,各位看官請自行對照屬性的作用,下圖是系統服務中的截圖

  

  

  值得注意的是 ServiceName必須和上文中提到的ServiceName相同,別問我為什么

  至此重點的部分介紹完畢

      當然,大頭的部分還在后邊,請各位看官注意

第六步在SensorFishService項目中添加類Program.cs

  這個必須的,因為承載了程序入口,所以名字不能變,代碼如下

 1 static class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5 
 6             ServiceBase[] ServicesToRun;
 7             ServicesToRun = new ServiceBase[]{
 8                     new  FisheryMonitoring()
 9                 };
10 
11             //服務響應
12             ServiceBase.Run(ServicesToRun);  
13 // ServiceBase.Run(new  FisheryMonitoring()); 
14         }
15     }

 

至此整個windows服務編寫已經完成,下一篇筆者將介紹安裝和卸載服務的過程

注:請各位看客自行設置項目之間的引用和項目上的程序集的引用,很簡單的

附源碼:http://files.cnblogs.com/netqq/Fishery.zip

 

window服務的安裝請參考文章 http://www.cnblogs.com/netqq/p/4218147.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM