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
//! Run a Windows system service without hassle. //! //! This crate exports two methods, but they should not be called directly from user code! //! Instead use the provided `Service!` macro. The exports are necessary because there is no //! way to provide the ServiceMain callback with a custom user pointer which could be used to //! extract its context. Funnily, this **is** possible for the ControlHandlerEx callback, so at //! least we only have to use this dirty trick once. #![cfg(target_os = "windows")] mod windows; mod service_status; use std::ffi::CStr; use std::os::raw::{c_int, c_char}; use std::sync::mpsc::Receiver; use windows::{SERVICE_TABLE_ENTRY, StartServiceCtrlDispatcherA}; use service_status::SERVICE_STATUS; /// Create and run the provided function as Windows system service. /// /// This takes the service name as a `str` expression and the function which contains the service's /// main loop as arguments and immediately starts the service. Once this macro returns, the Service /// Control Manager (SCM) has stopped the service and your program may terminate. The service /// function gets a `Vec<String>` containing the arguments provided by the SCM (these are **not** /// the command line arguments of your EXE!) as well as a `Receiver<()>` which, when signalled, /// indicates that the SCM wants the service to stop. When that happens, the service function /// should return a `u32` exit code, which will prompt this crate to perform some cleanup and /// return from `Service!`. /// /// To actually run the service, you have to install your binary from an Administrator console /// window with /// /// ```text /// sc create myService binPath=<Path to your compiled executable> /// ``` /// /// It is important that you provide the same service name to the macro (see below). /// /// Once everything is set up, you can start and stop your service from the SCM by typing /// `services.msc` into the Windows prompt; starting the EXE directly will have no effect since /// the SCM will reject all attempts to register a ServiceMain function which it did not request. /// /// # Examples /// /// ``` /// #![no_main] /// #![feature(link_args)] /// #![link_args = "-Wl,--subsystem,windows"] /// /// use std::os::raw::{c_char, c_int, c_void}; /// use std::sync::mpsc::Receiver; /// /// #[macro_use] /// extern crate winservice; /// /// #[allow(non_snake_case)] /// #[no_mangle] /// pub extern "system" fn WinMain(hInstance : *const c_void, hPrevInstance : *const c_void, /// lpCmdLine : *const c_char, nCmdShow : c_int) -> c_int /// { /// Service!("myService", service_main); /// 0 /// } /// /// fn service_main(args : Vec<String>, end : Receiver<()>) -> u32 { /// loop { /// // Do some work /// if let Ok(_) = end.try_recv() { break; } } /// 0 } /// /// # fn main() {} /// ``` /// /// # How it works /// /// Since The ServiceCtrlDispatcher doesn't allow for a custom pointer to be passed to ServiceMain, /// we cannot use a closure or any other means to obtain context information about the way we are /// called. Thus the only option is to have a separate ServiceMain function for each call of /// `Service!`. But since winservice will already be compiled when you want to create your service, /// we have to do it here. The macro creates said ServiceMain function as a wrapper which calls /// directly into the crate with your custom service_main and then feeds this wrapper to the /// ServiceCtrlDispatcher. #[macro_export] macro_rules! Service { ( $name:expr, $function:ident ) => { { use std::os::raw::{c_char, c_int}; extern "C" fn wrapper(argc : c_int, argv : *const *const c_char) { winservice::dispatch($name, $function, argc, argv); } winservice::serve(wrapper); } } } /// This should never be directly called from the user. pub fn dispatch(name : &str, service_main : fn(Vec<String>, Receiver<()>) -> u32, argc : c_int, argv : *const *const c_char) { let (mut status, rx) = SERVICE_STATUS::initialize(name); let args = (0..argc as isize).map(|i| unsafe { CStr::from_ptr(*argv.offset(i)) .to_string_lossy().into_owned() }).collect::<Vec<String>>(); status.exit_with(service_main(args, rx)); } /// This should never be directly called from the user. pub fn serve(wrapper: extern "C" fn(c_int, *const *const c_char)) { let table = SERVICE_TABLE_ENTRY::with_wrapper(wrapper); unsafe { StartServiceCtrlDispatcherA(&table as *const SERVICE_TABLE_ENTRY); } }