只有累積,沒有奇蹟

2019年4月29日 星期一

[.NETCore] Windows Service - 服務並未以適時的方式回應啟動或控制請求。

前言
在上一篇有提到如何使用指令 註冊 Window Service 服務,提到了如何用指令操作 Windows Service 看啟用的狀態,但有時在啟動時會發生錯誤造成啟動失敗的狀況發生,舉例來說在啟動服務時跳出  'Windows 無法啟動,本機電腦的 TestService 服務,錯誤 1503 : 服務並未已適時的方式回應啟動獲控制請求。 這篇要說明的是在註冊服務當下發生異常的處理方式若有問題歡迎提出一起討論或是給予指導。

解決方案
首先第一步要知道啟動失敗的原因,當 Windows Service 在啟動服務失敗時,會透過 GetLastError函數回傳錯誤訊息,並將其錯誤原因與錯誤代碼做 mapping,因此在錯誤訊息中可以看到錯誤代碼 1053,每個錯誤代碼背後都代表特定錯誤原因,可以透過清單查詢代碼對應的錯誤原因 傳送門
在 1053 對應的錯誤訊息為 : The service did not respond to the start or control request in a timely fashion.,疑似執行的程式內容並未執行正確,錯誤訊息內容太籠統無法得到處理方向,因此進行下一步處理。

事件檢視器
事件檢視器 Event Viewer 是提供使用者查看電腦中發生大大小小事情的工具,Server 的啟動關閉紀錄、IIS 站台的狀況、或是當應用程式忽然異常時都會將其 Log 資訊寫到事件檢視器中,因此當 Windows Service 啟動失敗時,也可以使用事件檢視器來查看可能異常的原因,開啟方式為在左下角開始,輸入 Event 並按下 Enter
可以透過時間搜尋發生問題的當下系統中的 log 資訊,可以發現啟動時 Windows Service 紀錄的錯誤為 服務並未以適當的方式回應啟動或控制請求,根本原因是等候 TestService 服務連線時發生逾時 (30000 毫秒) 造成
回到代碼發現舊有的代碼 Console 應用程式在啟動時並未加上 Log 紀錄,因此在 Console 進入點 main 方法加上 StreamWriter 加入 log 尋找可能錯誤原因,另外在與同事討論後提到 Serilog 在作為 Windows Service 時要特別注意 Log 路徑問題,建議在使用 Serilog 在啟動時加上指定路徑,因此在 Initial Log 時加入下列代碼
private static void InitialLogging()
{
    var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
    var pathToContentRoot = Path.GetDirectoryName(pathToExe);
    Directory.SetCurrentDirectory(pathToContentRoot);

    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Debug()
        .WriteTo.Console()
        .WriteTo.File("logs/App_.txt", rollingInterval: RollingInterval.Day)
        .CreateLogger();
}
接著在開啟 cmd 輸入指令重新啟動 Windows Service,指令為  start ServiceName  要啟動時狀態為 START_PENDING (2)
D:\>sc start testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 26300
        FLAGS              : 
當啟動完畢之後,狀態就會更新為 RUN (4) 
SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0
服務啟動正常除蟲完畢,打完收工 !!

參考
powershell create windows service

2019年4月27日 星期六

[Windows] 註冊 Windows Service 服務

前言
最近專案有個需求要將排程透過 Windows Service 服務來執行,在 Windows OS 要註冊 Service 可以用  cmd  與  powershell  兩種方式來建立以及刪除 Service,兩種方式之前都有使用過但要再使用時都會上網查因此決定紀錄一下未來方便查詢,此篇就針對這兩種方式進行基本介紹與說明若有問題歡迎提出一起討論或是給予指導。

使用命令提示字元 cmd
在執行時請先注意開啟 cmd 需要使用 Admin 權限執行,否則會有執行異常或是告知沒權限的錯誤訊息,以下為開啟時應用程式時用 administrator 開啟的畫面,用管理者身分執行 cmd

註冊服務
在 cmd 中可以透過 sc.exe 來建立 windows service,sc 全名為 service control,語法指令格式如下說明
描述:
        在登錄和服務資料庫中建立服務項目。
使用方法:
        sc <server> create [service name] [binPath= ] <option1> <option2>...

選項:
注意: 選項名稱包括等號。
      在等號和值之間必須空一格。
 type= <own|share|interact|kernel|filesys|rec|userown|usershare>
       (預設值 = own)
 start= <boot|system|auto|demand|disabled|delayed-auto>
       (預設值 = demand)
 error= <normal|severe|critical|ignore>
       (預設值 = normal)
 binPath= <.exe 檔案的二進位檔案路徑名稱>
 group= <載入順序群組>
 tag= <yes|no>
 depend= <相依性(以 / (反斜線) 隔開)>
 obj= <帳戶名稱|物件名稱>
       (預設值 = LocalSystem)
 DisplayName= <顯示名稱>
 password= <密碼>
舉例來說,要註冊 Windows Service 名為 TestService,其檔案位置在 D:\Job\TestService\Test.Jobs.exe 位置中,可以透過  create ServiceName  指令建立
D:\>sc create TestService binPath="D:\Job\TestService\Test.Jobs.exe"
[SC] CreateService 成功
建立成功可以看到新增 TestService 成功的訊息,接著我們使用   query ServiceName  查詢目前 service 狀態,剛建立好的 service 狀態會是 STOPPED (1)
D:\>sc query testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 1  STOPPED
        WIN32_EXIT_CODE    : 1077  (0x435)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

啟動服務
可以透過  start ServiceName  指令來啟動服務,要啟動時狀態為 START_PENDING (2)
D:\>sc start testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 26300
        FLAGS              : 
當正常執行,狀態就會更新為 RUN (4) 
SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 4  RUNNING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

停止服務
要停止服務的可以透過  stop ServiceName 指令,執行完指令會進行停止的動作,因此查看當下狀態會是 STOP_PENDING,當停止後再次查詢可以看到狀態改為 STOPPED (1)
D:\>sc stop testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 3  STOP_PENDING
                                (STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

D:\>sc query testservice

SERVICE_NAME: testservice
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 1  STOPPED
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0

刪除服務
當要刪除服務的話執行  delete ServiceName  指令,其回傳訊息與 create 類似直接回傳刪除服務成功
D:\>sc delete testservice
[SC] DeleteService 成功

使用 Powershell 
同樣的在執行前需要使用管理者身分執行 powershell,否則會有執行異常或是告知沒權限的錯誤訊息,
以下為開啟時應用程式時用 administrator 開啟的畫面

註冊服務
在 cmd 中可以透過 sc.exe 來建立 windows service,sc 全名為 service control,語法指令格式如下說明
PS C:\> New-Service -Name "TestService" -BinaryPathName "D:\Jobs\TestService\Jobs.exe" -DisplayName "Test Service" -StartupType Manual -Description "This is a test service."

Status   Name               DisplayName
------   ----               -----------
Stopped  TestService        Test Service
參數介紹


  • Name : 指定服務的名稱 (唯一)
  • BinaryPathName : 服務的執行檔案路徑位置
  • DisplayName : 顯示的名稱
  • StartupType : 啟動類型
  • Description : 描述
  • 建立成功之後,在 GUI 呈現畫面如下
    要註冊 Windows Service 名為 TestService,其檔案位置在 D:\Job\TestService\Test.Jobs.exe 位置中,可以透過  create ServiceName  指令建立
    D:\>sc create TestService binPath="D:\Job\TestService\Test.Jobs.exe"
    [SC] CreateService 成功
    建立成功,接著我們查詢目前 service 狀態,在 powershell 指令比 cmd 稍微多一滴滴要使用 win32service在透過 filter 來查詢剛建立好的 testservice,與 cmd 建立的相同剛建好的狀態都是 Stopped 
    PS C:\> Get-WmiObject win32_service -Filter "name='testservice'"
    
    ExitCode  : 1077
    Name      : TestService
    ProcessId : 0
    StartMode : Manual
    State     : Stopped
    Status    : OK

    啟動服務
    可以透過  start ServiceName -Name  指令來啟動服務,與 cmd 不同的是會等到 running 在顯示目前狀態
    PS C:\> Start-Service -Name "testservice"
    PS C:\> Get-WmiObject win32_service -Filter "name='testservice'"
    
    ExitCode  : 0
    Name      : TestService
    ProcessId : 18188
    StartMode : Manual
    State     : Running
    Status    : OK              : 

    停止服務
    使用  stop ServiceName  指令停止當下 windows service,執行完指令會進行停止的動作,再次查詢服務狀態就會是已停止 STOPPED
    PS C:\> Stop-Service -Name "testservice"
    PS C:\> Get-WmiObject win32_service -Filter "name='testservice'"
    
    ExitCode  : 0
    Name      : TestService
    ProcessId : 0
    StartMode : Manual
    State     : Stopped
    Status    : OK

    刪除服務
    在 powersehll 中刪除時要特別注意,官方 MSDN 中提供的 Remove service 是 powershell 6 才提供的新功能,如果您的 powershell 版本低於 6 時候執行 remove serv 則會跳出錯誤訊息 : 無法辨識 'Remove-Service' 詞彙是否為 Cmdlet、函數,如果不確定電腦中使用的 powershell 版本為何,可以輸入以下指令
    PS C:\> $PSVersionTable.PSVersion
    
    Major  Minor  Build  Revision
    -----  -----  -----  --------
    5      1      17763  316
    因此,Remove service 會根據版本不同執行不同的指令,如果 powershell 是 6.0 或以上請輸入指令
    Remove-Service someservice 
    powershell 是 6.0 以下
    Stop-Service 'testservice'; Get-CimInstance -ClassName Win32_Service -Filter "Name='testservice'" | Remove-CimInstance
    

    後記
    透過以上簡單的介紹說明了在 cmd 與 powershell 中如何新增、刪除、查詢及修改 windows service 的各種方式與指令,如果有需要更細節的內容說是說明,可以透過 MSDN 官方網站查詢相關指令與更多的應用細節,這裡就針對簡單操作做說明不在琢磨,希望自己的金魚腦寫完這篇之後可以記錄得更清楚些 ! 

    參考
    powershell create windows service

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com