使用 Rust 實現 Windows 服務

如果你需要在 Windows 下運行你的 Rust 應用,並希望你的應用以服務形式運行,那麼這篇文章可能幫助到你。

windows-service-rs 是一個爲管理和實現 Windows 服務提供便利的庫。

本節描述了實現作爲 Windows 服務運行的程序的步驟,要了解此類程序的完整源代碼,請查看 examples 文件夾。

每個 Windows 服務都必須實現一個服務入口函數 fn(argc:u32,argv:*mut *mut u16),並從應用程序的 main 向系統註冊。

這個庫提供了一個方便的宏 [define_windows_service!] 來爲服務輸入函數生成一個低級別樣板,該函數解析來自系統的輸入並將處理委託給用戶定義的更高級別函數 fn(arguments:Vec)。

這是一個指南,關於低級別的入口函數 ffi_service_main,和高級別的函數 my_service_main,具體如何調用取決於開發人員。

#[macro_use]
extern crate windows_service;
use std::ffi::OsString;
use windows_service::service_dispatcher;
define_windows_service!(ffi_service_main, my_service_main);
fn my_service_main(arguments: Vec<OsString>) {
    // The entry point where execution will start on a background thread after a call to
    // `service_dispatcher::start` from `main`.
}
fn main() -> Result<(), windows_service::Error> {
    // Register generated `ffi_service_main` with the system and start the service, blocking
    // this thread until the service is stopped.
    service_dispatcher::start("myservice", ffi_service_main)?;
    Ok(())
}

處理服務的事件,Windows 服務在其生命週期早期應該做的第一件事是訂閱服務事件,例如停止和暫停等。

extern crate windows_service;
use std::ffi::OsString;
use windows_service::service::ServiceControl;
use windows_service::service_control_handler::{self, ServiceControlHandlerResult};
fn my_service_main(arguments: Vec<OsString>) {
    if let Err(_e) = run_service(arguments) {
        // Handle errors in some way.
    }
}
fn run_service(arguments: Vec<OsString>) -> Result<(), windows_service::Error> {
    let event_handler = move |control_event| -> ServiceControlHandlerResult {
        match control_event {
            ServiceControl::Stop => {
                // Handle stop event and return control back to the system.
                ServiceControlHandlerResult::NoError
            }
            // All services must accept Interrogate even if it's a no-op.
            ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
            _ => ServiceControlHandlerResult::NotImplemented,
        }
    };
    // Register system service event handler
    let status_handle = service_control_handler::register("myservice", event_handler)?;
    Ok(())
}

更新服務狀態,當系統啓動實現 windows 服務的應用程序時,它會自動處於 StartPending 狀態。

應用程序需要完成初始化,獲得 ServiceStatusHandle(請參閱 [service_control_handler::register])並轉換到正在運行狀態。

如果服務的初始化時間很長,它應該立即告訴系統它需要多少時間來完成初始化,方法是發送 StartPending 狀態,使用 ServiceStatus::wait_hint 估計時間,並在每次服務完成初始化步驟時增加 ServiceStatus::checkpoint。

在建議的 ServiceStatus::wait_int 過期之後,系統將嘗試終止無法轉換到 “正在運行” 狀態的服務。

當在其他未決狀態與其對應的目標狀態之間轉換時,同樣的概念也適用。

請注意,克隆 ServiceStatusHandle 並從任何線程使用它都是安全的。

extern crate windows_service;
use std::ffi::OsString;
use std::time::Duration;
use windows_service::service::{
    ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
    ServiceType,
};
use windows_service::service_control_handler::{self, ServiceControlHandlerResult};
fn my_service_main(arguments: Vec<OsString>) {
    if let Err(_e) = run_service(arguments) {
        // Handle error in some way.
    }
}
fn run_service(arguments: Vec<OsString>) -> windows_service::Result<()> {
    let event_handler = move |control_event| -> ServiceControlHandlerResult {
        match control_event {
            ServiceControl::Stop | ServiceControl::Interrogate => {
                ServiceControlHandlerResult::NoError
            }
            _ => ServiceControlHandlerResult::NotImplemented,
        }
    };
    // Register system service event handler
    let status_handle = service_control_handler::register("my_service_name", event_handler)?;
    let next_status = ServiceStatus {
        // Should match the one from system service registry
        service_type: ServiceType::OWN_PROCESS,
        // The new state
        current_state: ServiceState::Running,
        // Accept stop events when running
        controls_accepted: ServiceControlAccept::STOP,
        // Used to report an error when starting or stopping only, otherwise must be zero
        exit_code: ServiceExitCode::Win32(0),
        // Only used for pending states, otherwise must be zero
        checkpoint: 0,
        // Only used for pending states, otherwise must be zero
        wait_hint: Duration::default(),
    };
    // Tell the system that the service is running now
    status_handle.set_service_status(next_status)?;
    // Do some work
    Ok(())
}

更多內容請參考 Github:

https://github.com/mullvad/windows-service-rs

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/R-yK1mdRyPODEykKkrtuFQ