Rust Windows Service Example: Building a Windows Service in Rust

Windows services are long-running background processes that start automatically when the system boots. Writing a Windows service in Rust combines the language's performance and safety features with system-level functionality. This guide walks you through creating a simple Windows service using Rust.


What Is a Windows Service?

A Windows service is a program designed to run in the background and perform tasks without user interaction. Common examples include database servers, antivirus programs, and monitoring tools.


Why Use Rust for Windows Services?

  1. Performance: Rust’s zero-cost abstractions ensure efficient execution.
  2. Memory Safety: Eliminates common bugs like null pointer dereferences and buffer overflows.
  3. Cross-Platform Compatibility: Rust code can be extended or adapted for use on other platforms.

Setting Up a Rust Windows Service

Step 1: Install Rust

Ensure Rust is installed via rustup:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Install the MSVC toolchain for Windows development:

rustup default stable-x86_64-pc-windows-msvc

Step 2: Create a New Rust Project

Create a new project:

cargo new rust_windows_service
cd rust_windows_service

Step 3: Add Dependencies

Update your Cargo.toml file with the following dependencies:

[dependencies]
windows-service = "0.5"
log = "0.4"
simplelog = "0.11"
  • windows-service: Provides utilities for creating and managing Windows services.
  • log and simplelog: Enable logging for debugging and monitoring.

Run cargo build to download and compile the dependencies.


Step 4: Implement the Windows Service

Replace the content of src/main.rs with the following code:

use log::{info, error};
use simplelog::*;
use std::{thread, time::Duration};
use windows_service::{
    define_windows_service,
    service::{
        ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
        ServiceType,
    },
    service_control_handler::{self, ServiceControlHandlerResult},
    service_dispatcher, Result,
};

// Define the service name
const SERVICE_NAME: &str = "RustWindowsService";
const SERVICE_DISPLAY_NAME: &str = "Rust Windows Service Example";
const SERVICE_DESCRIPTION: &str = "An example Windows service written in Rust.";

// Main service entry point
define_windows_service!(ffi_service_main, service_main);

fn service_main(_arguments: Vec<std::ffi::OsString>) {
    if let Err(e) = run_service() {
        error!("Service failed: {:?}", e);
    }
}

fn run_service() -> Result<()> {
    // Set up logging
    SimpleLogger::init(LevelFilter::Info, Config::default())
        .expect("Failed to initialize logger");

    info!("Starting Rust Windows Service");

    // Define service status
    let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
    let status_handle = service_control_handler::register(
        SERVICE_NAME,
        move |control_event| match control_event {
            ServiceControl::Stop => {
                shutdown_tx.send(()).unwrap();
                ServiceControlHandlerResult::NoError
            }
            _ => ServiceControlHandlerResult::NotImplemented,
        },
    )?;

    status_handle.set_service_status(ServiceStatus {
        service_type: ServiceType::OWN_PROCESS,
        current_state: ServiceState::Running,
        controls_accepted: ServiceControlAccept::STOP,
        exit_code: ServiceExitCode::Win32(0),
        checkpoint: 0,
        wait_hint: Duration::from_secs(10).as_secs() as u32,
    })?;

    // Main service loop
    info!("Service is running...");
    loop {
        if shutdown_rx.try_recv().is_ok() {
            info!("Shutdown signal received");
            break;
        }
        thread::sleep(Duration::from_secs(1));
    }

    info!("Service is stopping...");
    status_handle.set_service_status(ServiceStatus {
        service_type: ServiceType::OWN_PROCESS,
        current_state: ServiceState::Stopped,
        controls_accepted: ServiceControlAccept::empty(),
        exit_code: ServiceExitCode::Win32(0),
        checkpoint: 0,
        wait_hint: 0,
    })?;

    Ok(())
}

Step 5: Register the Service

Create a new file called register.rs in the src directory to handle service installation and removal:

use windows_service::{
    service::{
        ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceType,
    },
    service_manager::{ServiceManager, ServiceManagerAccess},
};

const SERVICE_NAME: &str = "RustWindowsService";

pub fn install_service() {
    let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CREATE_SERVICE)
        .unwrap();
    let service_info = ServiceInfo {
        name: SERVICE_NAME.into(),
        display_name: "Rust Windows Service Example".into(),
        service_type: ServiceType::OWN_PROCESS,
        start_type: ServiceStartType::AutoStart,
        error_control: ServiceErrorControl::Normal,
        executable_path: std::env::current_exe().unwrap(),
        launch_arguments: vec![],
        dependencies: vec![],
        account_name: None, // Local system account
        account_password: None,
    };
    manager.create_service(service_info, ServiceAccess::START_STOP).unwrap();
    println!("Service installed successfully");
}

pub fn uninstall_service() {
    let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::DELETE)
        .unwrap();
    let service = manager.open_service(SERVICE_NAME, ServiceAccess::DELETE).unwrap();
    service.delete().unwrap();
    println!("Service uninstalled successfully");
}

Step 6: Test the Service

  1. Run Commands:

Uninstall the service:

cargo run -- uninstall

Stop the service:

net stop RustWindowsService

Start the service:

net start RustWindowsService

Install the service:

cargo run -- install

Install the Service:
Add the following lines to main.rs for testing:

fn main() {
    if std::env::args().any(|arg| arg == "install") {
        register::install_service();
        return;
    }

    if std::env::args().any(|arg| arg == "uninstall") {
        register::uninstall_service();
        return;
    }

    if let Err(e) = service_dispatcher::start(SERVICE_NAME, ffi_service_main) {
        eprintln!("Failed to start service: {:?}", e);
    }
}

Conclusion

This guide demonstrates how to create a Windows service in Rust using the windows-service crate. With Rust's safety and performance, you can build robust background processes for various use cases. Extend this example by adding more functionality, such as monitoring files, interacting with APIs, or logging metrics.