Macro winservice::Service
[−]
[src]
macro_rules! Service { ( $name:expr, $function:ident ) => { ... }; }
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
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) } fn service_main(args : Vec<String>, end : Receiver<()>) -> u32 { loop { // Do some work if let Ok(_) = end.try_recv() { break; } } 0 }
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.