使用C#創建安裝Windows服務程序(乾貨)

使用C#創建安裝Windows服務程序(乾貨)

最近在公司要求使用Windows服務作爲消息隊列的消費者,所以自行研究了一下C#中Windows服務如何創建以及如何使用,以及部署的方式。我是西瓜程序猿,此篇記錄一下供大家參考學習。

 

一、Windows服務概述

1.1-Windows服務是什麼?

Windows服務是在Windows操作系統上,以後臺形式運行的應用程序。它們可以在系統啓動時自動啓動,並且獨立於用戶登錄。Windows服務通常用於執行那些長時間運行、無需用戶交互或需要在後臺持續執行的任務。

 

1.2-Windows能用來做什麼?

  1. 後臺任務和自動化:可以使用Windows服務來執行重複性的計劃任務、數據同步、定期備份、報告生成等。

  2. 網絡服務:Windows服務可以作爲網絡服務器提供網絡服務,如Web服務器、FTP服務器、郵件服務器等。

  3. 定時任務:Windows服務可以創建定時任務並在指定時間間隔或特定事件發生時觸發執行操作。

  4. 數據處理:可以使用Windows服務進行數據處理、數據轉換、數據清洗等批量處理任務。

  5. 消息隊列:可以用於消息隊列的消費者,後臺任務一直和消息隊列保持長連接,需要消費時會自動接收到進行業務處理。

1.3-Windows服務有什麼優勢?

  1. 後臺執行:Windows服務在後臺運行,不會干擾用戶的工作,也無需用戶登錄即可持續執行任務。

  2. 系統級別權限:Windows服務可以在系統級別運行,具有更高的權限,可以訪問系統資源和執行敏感操作。

  3. 自動啓動:Windows服務可以在系統啓動時自動啓動,確保任務始終處於運行狀態。

  4. 可靠性和穩定性:Windows服務被設計爲長時間運行的應用程序,具有較高的可靠性和穩定性。

二、創建Windows服務

2.1-創建Windows服務項目

(1)打開【Visual Studio】開發工具,然後選擇【 Windows 服務(.NET Framework) 】,點擊下一步。
注意:Windows服務只有在.NET Framework版本中才有了,在跨平臺中使用Worker Service。


(2)修改項目名稱和項目存儲目錄,項目名稱我寫的是【MyDemoService】,然後框架我選擇的是【.NET Farmework 4.8】,這個可以根據自己的需要填寫和選擇,然後點擊【創建】。

創建好的目錄如下:【Program.cs】是主程序的入口,【Service1.cs】是服務的入口,可以創建多個,然後在Prodrams.cs中配置就好了。

(3)【Service1】服務名稱可以重命名修改,此處我重命名爲【MyDemoService】, Program.cs文件中也相對應的也要進行修改。


(4)然後我們就可以在【MyDemoService】中寫業務邏輯代碼了,有很多種方式可以定位到要寫的具體文件,先列舉兩種常用的。
方法一:在【program.cs】文件中,找到這個類,按鍵盤上的F12可以直接進入查看文件。

方法二:直接右擊,然後點擊【查看代碼】。

業務代碼寫到這裏面:

到這一步服務就創建好了,然後就寫具體的業務代碼就行了。注意:服務必須至少重寫 OnStart 和 OnStop 纔有用。

 

 

2.2-服務可以重寫的方法

/// <summary> /// 服務啓動:指示服務開始運行時應採取的操作。必須在此過程中爲服務編寫代碼才能執行有用的操作。 /// </summary> /// <param name="args"></param> protected override void OnStart(string[] args) { } /// <summary> /// 服務停止:指示在服務停止運行時應發生什麼情況。 /// </summary> protected override void OnStop() { } /// <summary> /// 暫停:指示在服務暫停時應發生什麼情況。 /// </summary> protected override void OnPause() { } /// <summary> /// 繼續:指示服務在暫停後恢復正常運行時應發生什麼情況。 /// </summary> protected override void OnContinue() { } /// <summary> /// 停止前:指示在系統關閉之前應發生什麼情況(如果此時服務正在運行)。 /// </summary> protected override void OnShutdown() { }

2.3-配置日誌(log4net)

爲了方便測試,先介紹一下如何使用log4net做日誌記錄,當日志啓動時和停止時我們記錄一下。

(1)我們在項目目錄下新建一個文件夾【LogConfig】,然後再創建一個文件爲【log4net.config】。


(2)【log4net.config】內容如下。

 

<?xml version="1.0" encoding="utf-8"?><configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections> <system.web> <compilation debug="true" targetFramework="4.5.2" /> <httpRuntime targetFramework="4.5.2" /> </system.web> <log4net> <!--錯誤日誌:::記錄錯誤日誌--> <!--按日期分割日誌文件 一天一個--> <!-- appender 定義日誌輸出方式 將日誌以回滾文件的形式寫到文件中。--> <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender"> <!--保存路徑:下面路徑項目啓動的時候自動在C盤中創建log、logError文件--> <file value="log/error/error_" /> <!-- 如果想在本項目中添加路徑,那就直接去掉C:\\ 只設置log\\LogError 項目啓動中默認創建文件 --> <appendToFile value="true"/> <!--按照何種方式產生多個日誌文件(日期[Date],文件大小[Size],混合[Composite])--> <rollingStyle value="Date"/> <!--這是按日期產生文件夾--> <datePattern value="yyyy-MM-dd'.log'"/> <!--是否只寫到一個文件中--> <staticLogFileName value="false"/> <!--保留的log文件數量 超過此數量後 自動刪除之前的 好像只有在 按Size分割時有效 設定值value="-1"爲不限文件數--> <param name="MaxSizeRollBackups" value="100"/> <!--每個文件的大小。只在混合方式與文件大小方式下使用。超出大小後在所有文件名後自動增加正整數重新命名,數字最大的最早寫入。可用的單位:KB|MB|GB。不要使用小數,否則會一直寫入當前日誌--> <maximumFileSize value="50MB" /> <!-- layout 控制Appender的輸出格式,也可以是xml 一個Appender只能是一個layout--> <layout type="log4net.Layout.PatternLayout"> <!--每條日誌末尾的文字說明--> <!--輸出格式 模板--> <!-- <param name="ConversionPattern" value="記錄時間:%date 線程ID:[%thread] 日誌級別:%-5level 記錄類:%logger 操作者ID:%property{Operator} 操作類型:%property{Action}%n 當前機器名:%property%n當前機器名及登錄用戶:%username %n 記錄位置:%location%n 消息描述:%property{Message}%n 異常:%exception%n 消息:%message%newline%n%n" />--> <!--樣例:2008-03-26 13:42:32,111 [10] INFO Log4NetDemo.MainClass [(null)] - info--> <!--<conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日誌級別:%-5level %n錯誤描述:%message%newline %n"/>--> <conversionPattern value="%n========== %n【日誌級別】%-5level %n【記錄時間】%date %n【執行時間】[%r]毫秒 %n【出錯文件】%F %n【出錯行號】%L %n【出錯的類】%logger 屬性[%property{NDC}] %n【錯誤描述】%message %n【錯誤詳情】%newline"/> </layout> <filter type="log4net.Filter.LevelRangeFilter,log4net"> <levelMin value="ERROR" /> <levelMax value="FATAL" /> </filter> </appender> <!--DEBUG:::記錄DEBUG日誌--> <!--按日期分割日誌文件 一天一個--> <!-- appender 定義日誌輸出方式 將日誌以回滾文件的形式寫到文件中。--> <appender name="DebugAppender" type="log4net.Appender.RollingFileAppender"> <!--保存路徑:下面路徑項目啓動的時候自動在C盤中創建log、logError文件--> <file value="log/debug/debug_" /> <!-- 如果想在本項目中添加路徑,那就直接去掉C:\\ 只設置log\\LogError 項目啓動中默認創建文件 --> <appendToFile value="true"/> <!--按照何種方式產生多個日誌文件(日期[Date],文件大小[Size],混合[Composite])--> <rollingStyle value="Date"/> <!--這是按日期產生文件夾--> <datePattern value="yyyy-MM-dd'.log'"/> <!--是否只寫到一個文件中--> <staticLogFileName value="false"/> <!--保留的log文件數量 超過此數量後 自動刪除之前的 好像只有在 按Size分割時有效 設定值value="-1"爲不限文件數--> <param name="MaxSizeRollBackups" value="100"/> <!--每個文件的大小。只在混合方式與文件大小方式下使用。超出大小後在所有文件名後自動增加正整數重新命名,數字最大的最早寫入。可用的單位:KB|MB|GB。不要使用小數,否則會一直寫入當前日誌--> <maximumFileSize value="50MB" /> <!-- layout 控制Appender的輸出格式,也可以是xml 一個Appender只能是一個layout--> <layout type="log4net.Layout.PatternLayout"> <!--每條日誌末尾的文字說明--> <!--輸出格式 模板--> <!-- <param name="ConversionPattern" value="記錄時間:%date 線程ID:[%thread] 日誌級別:%-5level 記錄類:%logger 操作者ID:%property{Operator} 操作類型:%property{Action}%n 當前機器名:%property%n當前機器名及登錄用戶:%username %n 記錄位置:%location%n 消息描述:%property{Message}%n 異常:%exception%n 消息:%message%newline%n%n" />--> <!--樣例:2008-03-26 13:42:32,111 [10] INFO Log4NetDemo.MainClass [(null)] - info--> <!--<conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日誌級別:%-5level %n錯誤描述:%message%newline %n"/>--> <conversionPattern value="%n========== %n【日誌級別】%-2level %n【記錄時間】%date %n【執行時間】[%r]毫秒 %n【debug文件】%F %n【debug行號】%L %n【debug類】%logger 屬性[%property{NDC}] %n【debug描述】%message"/> </layout> <filter type="log4net.Filter.LevelRangeFilter,log4net"> <levelMin value="DEBUG" /> <levelMax value="WARN" /> </filter> </appender> <!--INFO:::記錄INFO日誌--> <!--按日期分割日誌文件 一天一個--> <!-- appender 定義日誌輸出方式 將日誌以回滾文件的形式寫到文件中。--> <appender name="INFOAppender" type="log4net.Appender.RollingFileAppender"> <!--保存路徑:下面路徑項目啓動的時候自動在C盤中創建log、logError文件--> <file value="log/info/info_" /> <!-- 如果想在本項目中添加路徑,那就直接去掉C:\\ 只設置log\\LogError 項目啓動中默認創建文件 --> <appendToFile value="true"/> <!--按照何種方式產生多個日誌文件(日期[Date],文件大小[Size],混合[Composite])--> <rollingStyle value="Date"/> <!--這是按日期產生文件夾--> <datePattern value="yyyy-MM-dd'.log'"/> <!--是否只寫到一個文件中--> <staticLogFileName value="false"/> <!--保留的log文件數量 超過此數量後 自動刪除之前的 好像只有在 按Size分割時有效 設定值value="-1"爲不限文件數--> <param name="MaxSizeRollBackups" value="100"/> <!--每個文件的大小。只在混合方式與文件大小方式下使用。超出大小後在所有文件名後自動增加正整數重新命名,數字最大的最早寫入。可用的單位:KB|MB|GB。不要使用小數,否則會一直寫入當前日誌--> <maximumFileSize value="50MB" /> <!-- layout 控制Appender的輸出格式,也可以是xml 一個Appender只能是一個layout--> <layout type="log4net.Layout.PatternLayout"> <!--每條日誌末尾的文字說明--> <!--輸出格式 模板--> <!-- <param name="ConversionPattern" value="記錄時間:%date 線程ID:[%thread] 日誌級別:%-5level 記錄類:%logger 操作者ID:%property{Operator} 操作類型:%property{Action}%n 當前機器名:%property%n當前機器名及登錄用戶:%username %n 記錄位置:%location%n 消息描述:%property{Message}%n 異常:%exception%n 消息:%message%newline%n%n" />--> <!--樣例:2008-03-26 13:42:32,111 [10] INFO Log4NetDemo.MainClass [(null)] - info--> <!--<conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日誌級別:%-5level %n錯誤描述:%message%newline %n"/>--> <conversionPattern value="%n========== %n【日誌級別】%-2level %n【記錄時間】%date %n【執行時間】[%r]毫秒 %n【info文件】%F %n【info行號】%L %n【info類】%logger 屬性[%property{NDC}] %n【info描述】%message"/> </layout> <filter type="log4net.Filter.LevelRangeFilter,log4net"> <levelMin value="INFO" /> <levelMax value="WARN" /> </filter> </appender> <!--Set root logger level to DEBUG and its only appender to A1--> <root> <!--控制級別,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF--> <level value="ALL" /> <appender-ref ref="DebugAppender" /> <appender-ref ref="ErrorAppender" /> <appender-ref ref="INFOAppender" /> </root> </log4net></configuration>

(3)並且右擊【【log4net.config】】文件,點擊【屬性】,然後將[複製到輸出目錄]設置爲【始終複製】。


(4)然後安裝log4net。在項目目錄中右擊【引用】,然後點擊【管理NuGet程序包】

(5)然後點擊瀏覽,搜索【log4net】,右側點擊安裝。

(6)重要:然後配置【AssemblyInfo.cs 】文件,如果不配置,是輸出不了日誌的。

添加到底部即可:(如果你的【log4net.config】文件路徑和我的不一樣,記得修改成跟自己配置路徑一樣的)。

代碼:

 

[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "config", ConfigFile = "LogConfig/log4net.config", Watch = true)]

(7)然後就可以使用log4net了,首先在Windows服務中獲得log4net的實例。


代碼:

 

 private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

三、Windows服務的運行和部署

3.1-服務基本配置

(1)點擊我們的服務【MyDemoService.cs】,然後右擊點擊【添加安裝程序】。


(2)然後可以看到下面多出來了一個文件,就是安裝程序。


(3)然後可以修改基本信息,服務組件中的【服務名稱】【服務描述】等等。我們右擊【serviceInstall1】點擊屬性,然後進行修改。


(4)然後點擊【serviceProcessInstall1】右擊屬性,進行修改。

 

 

3.2-服務運行與發佈

當我們直接按F5或者其他方式直接運行項目時,會提示:"無法從命令行或調試程序啓動服務。必須首先安裝 Windows服務(使用installutil.exe),然後用ServerExplorer、Windows服務管理工具或 NET START命令啓動它。"。不是這樣運行的,跟着下面步驟來操作運行與發佈Windows服務吧。

 

 

前提注意:如果你設置的目標平臺是x64,打開的目錄會不一樣,不然導致服務運行不起來。可以右擊項目名,點擊【屬性】——>【生成】——>【目標平臺】查看。


如果不是x64版本,複製這個地址:

 

C:\Windows\\Framework\v4.0.30319

如果是x64版本,複製這個地址:

C:\Windows\\Framework64\v4.0.30319

不然會報類似這種錯誤:在初始化安裝時發生異常: System.BadImageFormatException: 未能加載文件或程序集...

(1)然後我們把上面的地址(根據自己的環境選擇)添加到環境變量中。點擊【控制面板】——>【系統和安全】


(2)然後點擊【系統】

(3)點擊【高級系統設置】

(4)點擊【環境變量】

(5)在【系統變量】中找到Path,然後點擊【編輯】。

(6)然後點擊【新建】,然後把我們拷貝的目錄複製到這裏。然後點擊確認即可。

(7)測試是否配置成功,輸入這個命令查看一下【InstallUtil】,如果是下面這樣的內容說明成功了。

(8)然後編輯解決方案和項目。

(9)以管理員身份運行cmd命令,然後安裝服務。

 

InstallUtil 項目啓動執行文件全路徑
西瓜程序猿的例子:
InstallUtil D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

 


(10)出現這個說明安裝成功了。

(11)打開服務管理器,找到要啓動的服務,然後右擊啓動服務。

啓動後可以看到日誌也有了:

(12)如果要卸載服務,可以運行這個命令:

 

InstallUtil /u 項目啓動執行文件全路徑
西瓜程序猿的例子:
InstallUtil /u D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

 

 

 

3.3-常見命令

1、安裝服務:InstallUtil 項目啓動執行文件全路徑
3、啓動服務:net start 服務名
4、停止服務:net stop 服務名
4、卸載服務:InstallUtil /u 項目啓動執行文件全路徑

 

3.4-查看計算機事件

如果在啓動Windows服務時報錯,我們可以通過查看計算機事件查詢到具體的報錯信息。

(1)按鍵盤上的【Win+R】,然後輸入【eventvwr.msc】


(2) 然後點擊確定,就可以打開事件查看器的窗口。在窗口左側找到【Windows日誌】——>【系統】,就可以看到電腦的開機關機及日誌記錄了。

 

 

3.5-在服務器上安裝相同Windows服務的多個實例

這個方案比較全可以參考:

我是西瓜程序猿,用的是這種方法:
(1)通過此命令可以部署多個,[servicename]唯一就行了,自己配置。

注意:[servicename]填寫的是服務名稱,[path to your exe] 是執行文件的路徑,必須是完整路徑,不要忘記binpath= 後面的空格。這種方法確實允許多次安裝服務。但是服務安裝程序提供的所有信息。F.e.描述、登錄類型等被忽略。

 

3.6-常見的錯誤

1、無法打開計算機“.”上的服務控制管理器

在“安裝”階段發生異常。
System.InvalidOperationException: 無法打開計算機“.”上的服務控制管理器。此操作可能需要其他特權。
引發了內部異常 System.ComponentModel.Win32Exception,錯誤消息如下: 拒絕訪問。。
正在開始安裝的“回退”階段。
查看日誌文件的內容以獲得 D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe 程序集的進度。
該文件位於 D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.InstallLog。
正在回滾程序集“D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe”。
受影響的參數是:
logtoconsole =
logfile = D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.InstallLog
assemblypath = D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe
正在將事件日誌還原到源 TestDemoServices 的前一狀態。
“回退”階段已成功完成。
已完成事務處理安裝。
安裝失敗,已執行回退。

解決:權限不夠,打開cmd時記得【以管理員身份】打開。

 

2、未能加載文件或程序集xxx的基個依賴項

在初始化安裝時發生異常: System.BadImageFormatException: 未能加載文件或程序集“file://E:\DebuginServers.sB惑的基個依賴項。試圖加載格式不正確的程序。

解決:請看本文第【3.2】點

 

3、在執行【.bat】文件時,報路徑不存在相關的錯

** 執行命令**:InstallUtil /u D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe
錯誤信息:C:\Windows\system32>InstallUtil /u D:\欏圭洰婕旂ず涓存椂淇濆瓨\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe Microsoft (R) .NET Framework 安裝實用工具版本 4.8.9037.0 版權所有 (C) Microsoft Corporation。保留所有權利。在初始化安裝時發生異常: System.IO.FileNotFoundException: 未能加載文件或程序集“file:///D:\欏圭洰婕旂ず涓存椂淇濆瓨\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe”或它的某一個依賴項。系統找不到指定的文件。。

分析:這個錯是亂碼導致的,執行的命令是這個【InstallUtil /u D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe】,但是在控制檯裏面顯示的是這個【file:///D:\欏圭洰婕旂ず涓存椂淇濆瓨\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe】。出現了亂碼,這可能是由於控制檯編碼設置不正確導致的 。
解決:使用正確的編碼,嘗試將控制檯編碼設置爲與文件路徑所使用的編碼一致。例如,如果文件路徑是UTF-8編碼,您可以在控制檯中執行以下命令進行設置:

截圖:

 



四、高效工具:編寫bat啓動卸載服務

4.1-安裝服務

我們可以創建一個文件【安裝服務.bat】輸入以下內容:

REM Installnet start 服務名pause

注意:執行這個命令需要【以管理員身份】運行,不然會報這個錯誤:

System.InvalidOperationException: 無法打開計算機“.”上的服務控制管理器。此操作可能需要其他特權。
引發了內部異常 System.ComponentModel.Win32Exception,錯誤消息如下: 拒絕訪問。。

正確執行方式:

 

 

4.2-啓動服務

我們可以創建一個文件【卸載服務.bat】輸入以下內容:

注意:執行這個命令需要【以管理員身份】運行,不然會報這個錯誤:

發生系統錯誤。
拒絕訪問。

 

4.3-停止服務

我們可以創建一個文件【卸載服務.bat】輸入以下內容:

注意:執行這個命令需要【以管理員身份】運行,不然會報這個錯誤:

發生系統錯誤。
拒絕訪問。

 

4.4-卸載服務

我們可以創建一個文件【卸載服務.bat】輸入以下內容:

 

作者:[西瓜程序猿]
原文鏈接:

 

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