1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use std::ffi::{OsStr, OsString};
use std::{io, ptr};

use widestring::{WideCStr, WideCString};
use winapi::um::winsvc;

use crate::{Error, Result};

/// A macro to generate an entry point function (aka "service_main") for Windows service.
///
/// The `$function_name` function parses service arguments provided by the system
/// and passes them with a call to `$service_main_handler`.
///
/// `$function_name` - name of the "service_main" callback.
///
/// `$service_main_handler` - function with a signature `fn(Vec<OsString>)` that's called from
/// generated `$function_name`. Accepts parsed service arguments as `Vec<OsString>`. Its
/// responsibility is to create a `ServiceControlHandler`, start processing control events and
/// report the service status to the system.
///
/// # Example
///
/// ```rust,no_run
/// #[macro_use]
/// extern crate windows_service;
///
/// use std::ffi::OsString;
///
/// define_windows_service!(ffi_service_main, my_service_main);
///
/// fn my_service_main(arguments: Vec<OsString>) {
///     // Service entry point
/// }
///
/// # fn main() {}
/// ```
#[macro_export]
macro_rules! define_windows_service {
    ($function_name:ident, $service_main_handler:ident) => {
        /// Static callback used by the system to bootstrap the service.
        /// Do not call it directly.
        extern "system" fn $function_name(
            num_service_arguments: u32,
            service_arguments: *mut *mut u16,
        ) {
            let arguments = unsafe {
                $crate::service_dispatcher::parse_service_arguments(
                    num_service_arguments,
                    service_arguments,
                )
            };

            $service_main_handler(arguments);
        }
    };
}

/// Start service control dispatcher.
///
/// Once started the service control dispatcher blocks the current thread execution
/// until the service is stopped.
///
/// Upon successful initialization, system calls the `service_main` on background thread.
///
/// On failure: immediately returns an error, no threads are spawned.
///
/// # Example
///
/// ```rust,no_run
/// #[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() -> windows_service::Result<()> {
///     // 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(())
/// }
/// ```
pub fn start(
    service_name: impl AsRef<OsStr>,
    service_main: extern "system" fn(u32, *mut *mut u16),
) -> Result<()> {
    let service_name = WideCString::from_os_str(service_name).map_err(Error::InvalidServiceName)?;
    let service_table: &[winsvc::SERVICE_TABLE_ENTRYW] = &[
        winsvc::SERVICE_TABLE_ENTRYW {
            lpServiceName: service_name.as_ptr(),
            lpServiceProc: Some(service_main),
        },
        // the last item has to be { null, null }
        winsvc::SERVICE_TABLE_ENTRYW {
            lpServiceName: ptr::null(),
            lpServiceProc: None,
        },
    ];

    let result = unsafe { winsvc::StartServiceCtrlDispatcherW(service_table.as_ptr()) };
    if result == 0 {
        Err(Error::Winapi(io::Error::last_os_error()))
    } else {
        Ok(())
    }
}

/// Parse raw arguments received in `service_main` into `Vec<OsString>`.
///
/// This is an implementation detail and *should not* be called directly!
#[doc(hidden)]
pub unsafe fn parse_service_arguments(argc: u32, argv: *mut *mut u16) -> Vec<OsString> {
    (0..argc)
        .map(|i| {
            let array_element_ptr: *mut *mut u16 = argv.offset(i as isize);
            WideCStr::from_ptr_str(*array_element_ptr).to_os_string()
        })
        .collect()
}