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 }
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 }
第三步添加控制台應用程序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 }
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 }
在項目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