操做系統在啓動的時候,會啓動一些不須要用戶交互的進程。這些進程被稱爲服務。當操做系統啓動後它就自動被運行。數據庫
服務程序、服務控制程序(SCP,service control program)和服務控制管理器(SCM,service control manager)組成了Windows服務。咱們能夠經過SCP操縱SCM啓動、暫停、中止服務程序。其中服務程序和SCP由咱們本身編寫。c#
servics.exe是操做系統內置的一個部件。創建數據庫、啓動服務(自啓動),分配服務進程。windows
服務控制程序,例如windows自帶的服務工具:app
固然也能夠本身寫一個服務管理工具。dom
咱們須要執行的任務工具
首先看看服務入口:this
static void Main() { ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new MainService() }; ServiceBase.Run(ServicesToRun); }
從入口看,這和控制檯程序同樣,由於絕大部分的服務都不須要交互,因此沒有用戶界面。 那麼ServiceBase.Run到底作了什麼事情呢?日常狀況下,咱們並不關心,只是windows服務聽起來有點神祕。因而就搜索關於windows service原理的文章,理解一下。以下圖:spa
大體原理:服務主線程調用StartServiceCtrlDispatcher,最終執行了ServiceMain回調,調用了咱們本身寫的服務代碼。SCP經過CtrlHandle回調了咱們對服務的一些操做,好比暫停,啓動等等。它們都經過SetServiceStatus方法與SCM通訊,把服務的狀態等信息及時地告訴SCM。我結合代碼主要介紹下,咱們的服務代碼是如何被調用的。操作系統
Main方法中的ServiceBase是一個什麼樣的類呢?線程
從繼承關係上看,它是能夠跨應用程序域調用(MarshalByRefObject——Enables access to objects across application domain boundaries in applications that support remoting)以及須要釋放資源(IDisposable)。這說明,能夠遠程調用服務以及服務佔用了非託管資源。
咱們看Run方法:
1 public static void Run(ServiceBase[] services) 2 { 3 if ((services == null) || (services.Length == 0)) 4 { 5 throw new ArgumentException(Res.GetString("NoServices")); 6 } 7 if (Environment.OSVersion.Platform != PlatformID.Win32NT) 8 { 9 string message = Res.GetString("CantRunOnWin9x"); 10 string title = Res.GetString("CantRunOnWin9xTitle"); 11 LateBoundMessageBoxShow(message, title); 12 } 13 else 14 { 15 IntPtr entry = Marshal.AllocHGlobal((IntPtr) ((services.Length + 1) * Marshal.SizeOf(typeof(System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY)))); 16 System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY[] service_table_entryArray = new System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY[services.Length]; 17 bool multipleServices = services.Length > 1; 18 IntPtr zero = IntPtr.Zero; 19 for (int i = 0; i < services.Length; i++) 20 { 21 services[i].Initialize(multipleServices); 22 service_table_entryArray[i] = services[i].GetEntry(); 23 zero = (IntPtr) (((long) entry) + (Marshal.SizeOf(typeof(System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY)) * i)); 24 Marshal.StructureToPtr(service_table_entryArray[i], zero, true); 25 } 26 System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY structure = new System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY { 27 callback = null, 28 name = IntPtr.Zero 29 }; 30 zero = (IntPtr) (((long) entry) + (Marshal.SizeOf(typeof(System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY)) * services.Length)); 31 Marshal.StructureToPtr(structure, zero, true); 32 bool flag2 = System.ServiceProcess.NativeMethods.StartServiceCtrlDispatcher(entry); 33 foreach (ServiceBase base2 in services) 34 { 35 if (base2.startFailedException != null) 36 { 37 base2.startFailedException.Throw(); 38 } 39 } 40 string str = ""; 41 if (!flag2) 42 { 43 str = new Win32Exception().Message; 44 string str4 = Res.GetString("CantStartFromCommandLine"); 45 if (Environment.UserInteractive) 46 { 47 string str5 = Res.GetString("CantStartFromCommandLineTitle"); 48 LateBoundMessageBoxShow(str4, str5); 49 } 50 else 51 { 52 Console.WriteLine(str4); 53 } 54 } 55 foreach (ServiceBase base3 in services) 56 { 57 base3.Dispose(); 58 if (!flag2 && (base3.EventLog.Source.Length != 0)) 59 { 60 object[] args = new object[] { str }; 61 base3.WriteEventLogEntry(Res.GetString("StartFailed", args), EventLogEntryType.Error); 62 } 63 } 64 } 65 }
第32行 System.ServiceProcess.NativeMethods.StartServiceCtrlDispatcher(entry),這是一個平臺調用,它很是關鍵,看看原型:
它接收一個參數 entry,這是一個指針或者句柄類型( A platform-specific type that is used to represent a pointer or a handle)。那麼它應該指向服務的入口地址。咱們看看entry是什麼結構?
第15行 IntPtr entry = Marshal.AllocHGlobal((IntPtr) ((services.Length + 1) * Marshal.SizeOf(typeof(System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY)))); 這句一看就是分配內存的。若是咱們都學過c語言的話,也不會陌生。雖然c#自動分配內存,咱們不用管,其實這件事情仍是存在的。SERVICE_TABLE_ENTRY,這個結構以下:
callback是委託類型。這個類何時實例化的?第22行 service_table_entryArray[i] = services[i].GetEntry(); GetEntry方法以下:
1 private System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY GetEntry() 2 { 3 System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY service_table_entry = new System.ServiceProcess.NativeMethods.SERVICE_TABLE_ENTRY(); 4 this.nameFrozen = true; 5 service_table_entry.callback = this.mainCallback; 6 service_table_entry.name = this.handleName; 7 return service_table_entry; 8 }
第5行,callback的實例是this.mainCallback,定義 private System.ServiceProcess.NativeMethods.ServiceMainCallback mainCallback; 它的類型: public delegate void ServiceMainCallback(int argCount, IntPtr argPointer);在何時實例化呢?
private void Initialize(bool multipleServices) { if (!this.initialized) { ...this.status.currentState = 2; this.status.controlsAccepted = 0; this.status.win32ExitCode = 0; this.status.serviceSpecificExitCode = 0; this.status.checkPoint = 0; this.status.waitHint = 0; this.mainCallback = new System.ServiceProcess.NativeMethods.ServiceMainCallback(this.ServiceMainCallback); ... } }
在服務初始化的方法中實例化的。在Run方法的第21行中調用了初始化方法 :services[i].Initialize(multipleServices); 因此如今重心轉移到 this.ServiceMainCallback:
public unsafe void ServiceMainCallback(int argCount, IntPtr argPointer) { fixed (System.ServiceProcess.NativeMethods.SERVICE_STATUS* service_statusRef = &this.status) { string[] state = null; ...this.startCompletedSignal = new ManualResetEvent(false); this.startFailedException = null; ThreadPool.QueueUserWorkItem(new WaitCallback(this.ServiceQueuedMainCallback), state); this.startCompletedSignal.WaitOne(); if ((this.startFailedException != null) && (this.status.win32ExitCode == 0)) { this.status.win32ExitCode = 0x428; } if (!System.ServiceProcess.NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef)) { object[] objArray2 = new object[] { new Win32Exception().Message }; this.WriteEventLogEntry(Res.GetString("StartFailed", objArray2), EventLogEntryType.Error); this.status.currentState = 1; System.ServiceProcess.NativeMethods.SetServiceStatus(this.statusHandle, service_statusRef); } } }
在這段代碼中,它開啓了一個新的線程 ThreadPool.QueueUserWorkItem,回調了ServiceQueuedMainCallback,除此以外,定義了ManualResetEvent,用於主線程和子線程之間的同步。this.startCompletedSignal.WaitOne() 開啓子線程後,先阻塞主線程運行,等待子線程的結果。ServiceQueuedMainCallback幹了些什麼事情?
1 private void ServiceQueuedMainCallback(object state) 2 { 3 string[] args = (string[]) state; 4 try 5 { 6 this.OnStart(args); 7 this.WriteEventLogEntry(Res.GetString("StartSuccessful")); 8 this.status.checkPoint = 0; 9 this.status.waitHint = 0; 10 this.status.currentState = 4; 11 } 12 catch (Exception exception) 13 { 14 object[] objArray1 = new object[] { exception.ToString() }; 15 this.WriteEventLogEntry(Res.GetString("StartFailed", objArray1), EventLogEntryType.Error); 16 this.status.currentState = 1; 17 if (!System.LocalAppContextSwitches.DontThrowExceptionsOnStart) 18 { 19 this.startFailedException = ExceptionDispatchInfo.Capture(exception); 20 } 21 } 22 this.startCompletedSignal.Set(); 23 }
第6行,this.OnStart,這是一個服務開始運行的地方。
protected virtual void OnStart(string[] args) { }
相應還有OnStop方法,微軟暴露出這些虛方法,咱們在子類中,恰好重寫,這樣咱們寫的服務代碼就被執行了。
感興趣的同窗,能夠寫個服務,安裝個反編譯工具,按F12,就能夠跟進到代碼裏面去看了。