| 1 |
/** |
|---|
| 2 |
* Authors: Steve Teale - steve.teale@britseyeview.com |
|---|
| 3 |
* |
|---|
| 4 |
* Date: 2007/05/19 |
|---|
| 5 |
* History: V0.1 |
|---|
| 6 |
* License: Use freely for any purpose. |
|---|
| 7 |
*/ |
|---|
| 8 |
module bevutils.servicebase; |
|---|
| 9 |
|
|---|
| 10 |
import std.stdio; |
|---|
| 11 |
import std.c.windows.windows; |
|---|
| 12 |
import std.string; |
|---|
| 13 |
import bevutils.eventlogger; |
|---|
| 14 |
|
|---|
| 15 |
enum FMT_MSG : uint |
|---|
| 16 |
{ |
|---|
| 17 |
FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100, |
|---|
| 18 |
FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200, |
|---|
| 19 |
FORMAT_MESSAGE_FROM_STRING = 0x00000400, |
|---|
| 20 |
FORMAT_MESSAGE_FROM_HMODULE = 0x00000800, |
|---|
| 21 |
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000, |
|---|
| 22 |
FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000, |
|---|
| 23 |
FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF |
|---|
| 24 |
} |
|---|
| 25 |
|
|---|
| 26 |
const int SERVICE_WIN32_OWN_PROCESS = 0x00000010; |
|---|
| 27 |
enum ServiceControlManagerType : int |
|---|
| 28 |
{ |
|---|
| 29 |
SC_MANAGER_CONNECT = 0x1, |
|---|
| 30 |
SC_MANAGER_CREATE_SERVICE = 0x2, |
|---|
| 31 |
SC_MANAGER_ENUMERATE_SERVICE = 0x4, |
|---|
| 32 |
SC_MANAGER_LOCK = 0x8, |
|---|
| 33 |
SC_MANAGER_QUERY_LOCK_STATUS = 0x10, |
|---|
| 34 |
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x20, |
|---|
| 35 |
SC_MANAGER_ALL_ACCESS = |
|---|
| 36 |
SC_MANAGER_CONNECT | |
|---|
| 37 |
SC_MANAGER_CREATE_SERVICE | |
|---|
| 38 |
SC_MANAGER_ENUMERATE_SERVICE | |
|---|
| 39 |
SC_MANAGER_LOCK | |
|---|
| 40 |
SC_MANAGER_QUERY_LOCK_STATUS | |
|---|
| 41 |
SC_MANAGER_MODIFY_BOOT_CONFIG |
|---|
| 42 |
} |
|---|
| 43 |
|
|---|
| 44 |
enum ACCESS_TYPE : int |
|---|
| 45 |
{ |
|---|
| 46 |
SERVICE_QUERY_CONFIG = 0x1, |
|---|
| 47 |
SERVICE_CHANGE_CONFIG = 0x2, |
|---|
| 48 |
SERVICE_QUERY_STATUS = 0x4, |
|---|
| 49 |
SERVICE_ENUMERATE_DEPENDENTS = 0x8, |
|---|
| 50 |
SERVICE_START = 0x10, |
|---|
| 51 |
SERVICE_STOP = 0x20, |
|---|
| 52 |
SERVICE_PAUSE_CONTINUE = 0x40, |
|---|
| 53 |
SERVICE_INTERROGATE = 0x80, |
|---|
| 54 |
SERVICE_USER_DEFINED_CONTROL = 0x100, |
|---|
| 55 |
SERVICE_ALL_ACCESS = |
|---|
| 56 |
SERVICE_QUERY_CONFIG | |
|---|
| 57 |
SERVICE_CHANGE_CONFIG | |
|---|
| 58 |
SERVICE_QUERY_STATUS | |
|---|
| 59 |
SERVICE_ENUMERATE_DEPENDENTS | |
|---|
| 60 |
SERVICE_START | |
|---|
| 61 |
SERVICE_STOP | |
|---|
| 62 |
SERVICE_PAUSE_CONTINUE | |
|---|
| 63 |
SERVICE_INTERROGATE | |
|---|
| 64 |
SERVICE_USER_DEFINED_CONTROL, |
|---|
| 65 |
DELETE = 0x00010000 |
|---|
| 66 |
} |
|---|
| 67 |
|
|---|
| 68 |
enum SERVICE_COMMANDS : int |
|---|
| 69 |
{ |
|---|
| 70 |
SERVICE_CONTROL_STOP = 1, |
|---|
| 71 |
SERVICE_CONTROL_PAUSE = 2, |
|---|
| 72 |
SERVICE_CONTROL_CONTINUE = 3, |
|---|
| 73 |
SERVICE_CONTROL_INTERROGATE = 4, |
|---|
| 74 |
SERVICE_CONTROL_SHUTDOWN = 5, |
|---|
| 75 |
SERVICE_CONTROL_PARAMCHANGE = 6, |
|---|
| 76 |
SERVICE_CONTROL_NETBINDADD = 7, |
|---|
| 77 |
SERVICE_CONTROL_NETBINDREMOVE = 8, |
|---|
| 78 |
SERVICE_CONTROL_NETBINDENABLE = 9, |
|---|
| 79 |
SERVICE_CONTROL_NETBINDDISABLE = 10, |
|---|
| 80 |
SERVICE_CONTROL_DEVICEEVENT = 11, |
|---|
| 81 |
SERVICE_CONTROL_HARDWAREPROFILECHANGE = 12, |
|---|
| 82 |
SERVICE_CONTROL_POWEREVENT = 13, |
|---|
| 83 |
SERVICE_CONTROL_SESSIONCHANGE = 14 |
|---|
| 84 |
} |
|---|
| 85 |
|
|---|
| 86 |
enum SERVICE_STATES : uint |
|---|
| 87 |
{ |
|---|
| 88 |
SERVICE_STOPPED = 1, |
|---|
| 89 |
SERVICE_START_PENDING, |
|---|
| 90 |
SERVICE_STOP_PENDING, |
|---|
| 91 |
SERVICE_RUNNING, |
|---|
| 92 |
SERVICE_CONTINUE_PENDING, |
|---|
| 93 |
SERVICE_PAUSE_PENDING, |
|---|
| 94 |
SERVICE_PAUSED |
|---|
| 95 |
} |
|---|
| 96 |
|
|---|
| 97 |
enum SERVICE_START_TYPE : uint |
|---|
| 98 |
{ |
|---|
| 99 |
SERVICE_BOOT_START = 0x00000000, |
|---|
| 100 |
SERVICE_SYSTEM_START = 0x00000001, |
|---|
| 101 |
SERVICE_AUTO_START = 0x00000002, |
|---|
| 102 |
SERVICE_DEMAND_START = 0x00000003, |
|---|
| 103 |
SERVICE_DISABLED = 0x00000004 |
|---|
| 104 |
} |
|---|
| 105 |
|
|---|
| 106 |
enum SERVICE_CONTROL_TYPE : uint |
|---|
| 107 |
{ |
|---|
| 108 |
SERVICE_ERROR_IGNORE = 0x00000000, |
|---|
| 109 |
SERVICE_ERROR_NORMAL = 0x00000001, |
|---|
| 110 |
SERVICE_ERROR_SEVERE = 0x00000002, |
|---|
| 111 |
SERVICE_ERROR_CRITICAL = 0x00000003 |
|---|
| 112 |
} |
|---|
| 113 |
enum ACCEPT_COMMANDS : uint |
|---|
| 114 |
{ |
|---|
| 115 |
SERVICE_ACCEPT_STOP = 0x00000001, |
|---|
| 116 |
SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002, |
|---|
| 117 |
SERVICE_ACCEPT_SHUTDOWN = 0x00000004, |
|---|
| 118 |
SERVICE_ACCEPT_PARAMCHANGE = 0x00000008, |
|---|
| 119 |
SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010, |
|---|
| 120 |
SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020, |
|---|
| 121 |
SERVICE_ACCEPT_POWEREVENT = 0x00000040, |
|---|
| 122 |
SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080 |
|---|
| 123 |
} |
|---|
| 124 |
|
|---|
| 125 |
enum EVENTLOG_TYPES : int |
|---|
| 126 |
{ |
|---|
| 127 |
EVENTLOG_SUCCESS = 0x0000, |
|---|
| 128 |
EVENTLOG_ERROR_TYPE = 0x0001, |
|---|
| 129 |
EVENTLOG_WARNING_TYPE = 0x0002, |
|---|
| 130 |
EVENTLOG_INFORMATION_TYPE = 0x0004, |
|---|
| 131 |
EVENTLOG_AUDIT_SUCCESS = 0x0008, |
|---|
| 132 |
EVENTLOG_AUDIT_FAILURE = 0x0010 |
|---|
| 133 |
} |
|---|
| 134 |
|
|---|
| 135 |
alias void* SC_HANDLE; |
|---|
| 136 |
alias void* SERVICE_STATUS_HANDLE; |
|---|
| 137 |
alias PVOID PSID; |
|---|
| 138 |
|
|---|
| 139 |
struct SERVICE_TABLE_ENTRY |
|---|
| 140 |
{ |
|---|
| 141 |
char* ServiceName; |
|---|
| 142 |
void* ServiceMain; |
|---|
| 143 |
} |
|---|
| 144 |
struct SERVICE_STATUS |
|---|
| 145 |
{ |
|---|
| 146 |
uint dwServiceType; |
|---|
| 147 |
uint dwCurrentState; |
|---|
| 148 |
uint dwControlsAccepted; |
|---|
| 149 |
uint dwWin32ExitCode; |
|---|
| 150 |
uint dwServiceSpecificExitCode; |
|---|
| 151 |
uint dwCheckPoint; |
|---|
| 152 |
uint dwWaitHint; |
|---|
| 153 |
} |
|---|
| 154 |
|
|---|
| 155 |
extern (Windows) |
|---|
| 156 |
{ |
|---|
| 157 |
SC_HANDLE OpenServiceA( |
|---|
| 158 |
SC_HANDLE hSCManager, |
|---|
| 159 |
char* lpServiceName, |
|---|
| 160 |
ACCESS_TYPE dwDesiredAccess); |
|---|
| 161 |
SC_HANDLE OpenSCManagerA( |
|---|
| 162 |
char* lpMachineName, char* lpDatabaseName, |
|---|
| 163 |
ServiceControlManagerType dwDesiredAccess); |
|---|
| 164 |
BOOL CloseServiceHandle( |
|---|
| 165 |
SC_HANDLE hSCObject); |
|---|
| 166 |
BOOL DeleteService(SC_HANDLE hSCObject); |
|---|
| 167 |
BOOL StartServiceCtrlDispatcherA(SERVICE_TABLE_ENTRY*); |
|---|
| 168 |
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerA(char*, void*); |
|---|
| 169 |
BOOL SetServiceStatus(SERVICE_STATUS_HANDLE, SERVICE_STATUS*); |
|---|
| 170 |
SC_HANDLE CreateServiceA( |
|---|
| 171 |
SC_HANDLE hSCManager, |
|---|
| 172 |
LPCTSTR lpServiceName, |
|---|
| 173 |
LPCTSTR lpDisplayName, |
|---|
| 174 |
DWORD dwDesiredAccess, |
|---|
| 175 |
DWORD dwServiceType, |
|---|
| 176 |
DWORD dwStartType, |
|---|
| 177 |
DWORD dwErrorControl, |
|---|
| 178 |
LPCTSTR lpBinaryPathName, |
|---|
| 179 |
LPCTSTR lpLoadOrderGroup, |
|---|
| 180 |
LPDWORD lpdwTagId, |
|---|
| 181 |
LPCTSTR lpDependencies, |
|---|
| 182 |
LPCTSTR lpServiceStartName, |
|---|
| 183 |
LPCTSTR lpPassword); |
|---|
| 184 |
BOOL ControlService( |
|---|
| 185 |
SC_HANDLE hService, |
|---|
| 186 |
DWORD dwControl, |
|---|
| 187 |
SERVICE_STATUS* lpServiceStatus); |
|---|
| 188 |
BOOL QueryServiceStatus( |
|---|
| 189 |
SC_HANDLE hService, |
|---|
| 190 |
SERVICE_STATUS* lpServiceStatus); |
|---|
| 191 |
HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, |
|---|
| 192 |
BOOL bManualReset, |
|---|
| 193 |
BOOL bInitialState, |
|---|
| 194 |
LPCTSTR lpName); |
|---|
| 195 |
BOOL SetEvent(HANDLE hEvent); |
|---|
| 196 |
HANDLE RegisterEventSourceA(LPCTSTR lpUNCServerName, LPCTSTR lpSourceName); |
|---|
| 197 |
BOOL ReportEventA(HANDLE hEventLog, |
|---|
| 198 |
WORD wType, |
|---|
| 199 |
WORD wCategory, |
|---|
| 200 |
DWORD dwEventID, |
|---|
| 201 |
PSID lpUserSid, |
|---|
| 202 |
WORD wNumStrings, |
|---|
| 203 |
DWORD dwDataSize, |
|---|
| 204 |
LPCTSTR* lpStrings, |
|---|
| 205 |
LPVOID lpRawData); |
|---|
| 206 |
BOOL DeregisterEventSource(HANDLE hEventLog); |
|---|
| 207 |
BOOL Beep(DWORD dwFreq, DWORD dwDuration); |
|---|
| 208 |
|
|---|
| 209 |
|
|---|
| 210 |
} |
|---|
| 211 |
|
|---|
| 212 |
/++ |
|---|
| 213 |
+ A class to provide the internals of a windows service, including installation and removal. |
|---|
| 214 |
+ |
|---|
| 215 |
+ The class has few methods that you would call from a host program. |
|---|
| 216 |
+ All detailed implementation must be provided by a derived class. |
|---|
| 217 |
+ |
|---|
| 218 |
+ An example of its use to create a service executable is presented below. |
|---|
| 219 |
+ |
|---|
| 220 |
+ To install the service run derived.exe -i, to remove it run derived.exe -r. |
|---|
| 221 |
+ |
|---|
| 222 |
+ ----------------------------------------------------- |
|---|
| 223 |
// Derive a class from ServiceBase implementing the abstract methods. |
|---|
| 224 |
class NullService : ServiceBase |
|---|
| 225 |
{ |
|---|
| 226 |
this() |
|---|
| 227 |
{ |
|---|
| 228 |
super("Dummy", false); |
|---|
| 229 |
} |
|---|
| 230 |
|
|---|
| 231 |
public void onServiceStart(char[][] args) |
|---|
| 232 |
{ |
|---|
| 233 |
ServiceBase.EventLog.logMessage("NullService onServiceStart was called"); |
|---|
| 234 |
} |
|---|
| 235 |
|
|---|
| 236 |
public void onServiceStop() |
|---|
| 237 |
{ |
|---|
| 238 |
ServiceBase.EventLog.logMessage("NullService onServiceStop was called"); |
|---|
| 239 |
} |
|---|
| 240 |
} |
|---|
| 241 |
|
|---|
| 242 |
// Create an object of the derived class, then call the base class implementMain method. |
|---|
| 243 |
void main(char[][] args) |
|---|
| 244 |
{ |
|---|
| 245 |
try |
|---|
| 246 |
{ |
|---|
| 247 |
ServiceBase sb = new NullService(); |
|---|
| 248 |
ServiceBase.implementMain(args); |
|---|
| 249 |
} |
|---|
| 250 |
catch (Exception ex) |
|---|
| 251 |
{ |
|---|
| 252 |
writefln(ex.toString()); |
|---|
| 253 |
} |
|---|
| 254 |
} |
|---|
| 255 |
+ ----------------------------------------------------- |
|---|
| 256 |
+/ |
|---|
| 257 |
class ServiceBase |
|---|
| 258 |
{ |
|---|
| 259 |
protected: |
|---|
| 260 |
static char[] _serviceName; |
|---|
| 261 |
static SERVICE_TABLE_ENTRY[2] _sta; |
|---|
| 262 |
static bool _debug; |
|---|
| 263 |
static SERVICE_STATUS _ss; |
|---|
| 264 |
static SERVICE_STATUS_HANDLE _ssh; |
|---|
| 265 |
static char[][] _args; |
|---|
| 266 |
static int _inited; |
|---|
| 267 |
static ServiceBase _self; |
|---|
| 268 |
static HANDLE _stopEvent; |
|---|
| 269 |
static EventLogger _elog; |
|---|
| 270 |
|
|---|
| 271 |
this(char[] serviceName) |
|---|
| 272 |
{ |
|---|
| 273 |
_ssh = null; |
|---|
| 274 |
if (_inited == 0) |
|---|
| 275 |
{ |
|---|
| 276 |
//_debug = dBG; |
|---|
| 277 |
_serviceName = serviceName; |
|---|
| 278 |
_sta[0].ServiceName = toStringz(_serviceName); |
|---|
| 279 |
_sta[0].ServiceMain = cast(void*) &service_main; |
|---|
| 280 |
_sta[1].ServiceName = null; |
|---|
| 281 |
_sta[1].ServiceMain = null; |
|---|
| 282 |
_elog = new EventLogger(); |
|---|
| 283 |
_self = this; |
|---|
| 284 |
} |
|---|
| 285 |
_inited = 1; |
|---|
| 286 |
} |
|---|
| 287 |
|
|---|
| 288 |
static void StartService() |
|---|
| 289 |
{ |
|---|
| 290 |
if (!StartServiceCtrlDispatcherA(cast(SERVICE_TABLE_ENTRY *) &_sta[0])) |
|---|
| 291 |
{ |
|---|
| 292 |
_elog.logWindowsError("StartServiceCtrlDispatcherA failed."); |
|---|
| 293 |
} |
|---|
| 294 |
} |
|---|
| 295 |
|
|---|
| 296 |
void ServiceStart (char[][] args) |
|---|
| 297 |
{ |
|---|
| 298 |
try |
|---|
| 299 |
{ |
|---|
| 300 |
if (!ReportStatusToSCMgr(SERVICE_STATES.SERVICE_START_PENDING, // service state |
|---|
| 301 |
0, // exit code |
|---|
| 302 |
3000)) // wait hint |
|---|
| 303 |
throw new Exception("Failed to report status to SCM"); |
|---|
| 304 |
_stopEvent = CreateEventA( |
|---|
| 305 |
null, // no security attributes |
|---|
| 306 |
1, // manual reset event |
|---|
| 307 |
0, // not-signalled |
|---|
| 308 |
null); // no name |
|---|
| 309 |
if (_stopEvent == null) |
|---|
| 310 |
throw new Exception("Failed to create stop event"); |
|---|
| 311 |
if (!ReportStatusToSCMgr(SERVICE_STATES.SERVICE_START_PENDING, |
|---|
| 312 |
0, |
|---|
| 313 |
3000)) |
|---|
| 314 |
throw new Exception("Failed to report status to SCM"); |
|---|
| 315 |
onServiceStart(args); |
|---|
| 316 |
if (!ReportStatusToSCMgr(SERVICE_STATES.SERVICE_RUNNING, // service state |
|---|
| 317 |
0, // exit code |
|---|
| 318 |
0)) // wait hint |
|---|
| 319 |
throw new Exception("Failed to report status to SCM"); |
|---|
| 320 |
_elog.logInfo(_serviceName ~ " started."); |
|---|
| 321 |
WaitForSingleObject(_stopEvent, INFINITE); |
|---|
| 322 |
_elog.logInfo(_serviceName ~ " stopped."); |
|---|
| 323 |
ReportStatusToSCMgr(SERVICE_STATES.SERVICE_STOPPED, 0, 0); |
|---|
| 324 |
} |
|---|
| 325 |
finally {} |
|---|
| 326 |
} |
|---|
| 327 |
|
|---|
| 328 |
void ServicePause() |
|---|
| 329 |
{ |
|---|
| 330 |
onServicePause(); |
|---|
| 331 |
ReportStatusToSCMgr(SERVICE_STATES.SERVICE_PAUSED, 0, 0); |
|---|
| 332 |
_elog.logInfo(_serviceName ~ " paused."); |
|---|
| 333 |
} |
|---|
| 334 |
|
|---|
| 335 |
void ServiceContinue() |
|---|
| 336 |
{ |
|---|
| 337 |
onServiceContinue(); |
|---|
| 338 |
ReportStatusToSCMgr(SERVICE_STATES.SERVICE_RUNNING, 0, 0); |
|---|
| 339 |
_elog.logInfo(_serviceName ~ " resumed."); |
|---|
| 340 |
} |
|---|
| 341 |
|
|---|
| 342 |
void ServiceStop() |
|---|
| 343 |
{ |
|---|
| 344 |
onServiceStop(); |
|---|
| 345 |
if (_stopEvent) |
|---|
| 346 |
SetEvent(_stopEvent); |
|---|
| 347 |
} |
|---|
| 348 |
|
|---|
| 349 |
public abstract void onServiceStart(char[][] args) |
|---|
| 350 |
{ |
|---|
| 351 |
} |
|---|
| 352 |
|
|---|
| 353 |
public abstract void onServiceStop() |
|---|
| 354 |
{ |
|---|
| 355 |
// Override this method to shut down the worker thread(s) of your service |
|---|
| 356 |
} |
|---|
| 357 |
|
|---|
| 358 |
public void onServicePause() |
|---|
| 359 |
{ |
|---|
| 360 |
// Override this method to pause the worker thread(s) of your service |
|---|
| 361 |
} |
|---|
| 362 |
|
|---|
| 363 |
public void onServiceContinue() |
|---|
| 364 |
{ |
|---|
| 365 |
// Override this method to resume the worker thread(s) of your service |
|---|
| 366 |
} |
|---|
| 367 |
|
|---|
| 368 |
static void InstallService() |
|---|
| 369 |
{ |
|---|
| 370 |
SC_HANDLE schService; |
|---|
| 371 |
SC_HANDLE schSCManager; |
|---|
| 372 |
|
|---|
| 373 |
char[512] szPath; |
|---|
| 374 |
if (GetModuleFileNameA(null, szPath.ptr, 512) == 0 ) |
|---|
| 375 |
{ |
|---|
| 376 |
writefln("Unable to get executable path - service installation failed"); |
|---|
| 377 |
return; |
|---|
| 378 |
} |
|---|
| 379 |
|
|---|
| 380 |
schSCManager = OpenSCManagerA( |
|---|
| 381 |
null, // machine (NULL == local) |
|---|
| 382 |
null, // database (NULL == default) |
|---|
| 383 |
ServiceControlManagerType.SC_MANAGER_ALL_ACCESS // access required |
|---|
| 384 |
); |
|---|
| 385 |
if (schSCManager) |
|---|
| 386 |
{ |
|---|
| 387 |
schService = CreateServiceA( |
|---|
| 388 |
schSCManager, // SCManager database |
|---|
| 389 |
CName(), // name of service |
|---|
| 390 |
CName(), // name to display |
|---|
| 391 |
ACCESS_TYPE.SERVICE_QUERY_STATUS, // desired access |
|---|
| 392 |
SERVICE_WIN32_OWN_PROCESS, // service type |
|---|
| 393 |
SERVICE_START_TYPE.SERVICE_DEMAND_START, // start type |
|---|
| 394 |
SERVICE_CONTROL_TYPE.SERVICE_ERROR_NORMAL,// error control type |
|---|
| 395 |
toStringz(szPath), // service's binary |
|---|
| 396 |
null, // no load ordering group |
|---|
| 397 |
null, // no tag identifier |
|---|
| 398 |
null, // dependencies |
|---|
| 399 |
null, // LocalSystem account |
|---|
| 400 |
null); // no password |
|---|
| 401 |
|
|---|
| 402 |
if (schService) |
|---|
| 403 |
{ |
|---|
| 404 |
writefln("%s installed", _serviceName); |
|---|
| 405 |
CloseServiceHandle(schService); |
|---|
| 406 |
} |
|---|
| 407 |
else |
|---|
| 408 |
{ |
|---|
| 409 |
uint err = GetLastError(); |
|---|
| 410 |
char[] errMsg = _elog.getWindowsErrorText(err); |
|---|
| 411 |
writefln("CreateService failed (%d) - %s", err, errMsg); |
|---|
| 412 |
} |
|---|
| 413 |
|
|---|
| 414 |
CloseServiceHandle(schSCManager); |
|---|
| 415 |
} |
|---|
| 416 |
else |
|---|
| 417 |
{ |
|---|
| 418 |
uint err; |
|---|
| 419 |
char[] errMsg = _elog.getWindowsErrorText(err); |
|---|
| 420 |
writefln("OpenSCManager failed (%d) - %s", err, errMsg); |
|---|
| 421 |
} |
|---|
| 422 |
} |
|---|
| 423 |
|
|---|
| 424 |
static void RemoveService() |
|---|
| 425 |
{ |
|---|
| 426 |
SC_HANDLE schService; |
|---|
| 427 |
SC_HANDLE schSCManager; |
|---|
| 428 |
|
|---|
| 429 |
schSCManager = OpenSCManagerA( |
|---|
| 430 |
null, // machine (NULL == local) |
|---|
| 431 |
null, // database (NULL == default) |
|---|
| 432 |
ServiceControlManagerType.SC_MANAGER_CONNECT); // access required |
|---|
| 433 |
if ( schSCManager ) |
|---|
| 434 |
{ |
|---|
| 435 |
schService = OpenServiceA(schSCManager, CName(), |
|---|
| 436 |
ACCESS_TYPE.DELETE | ACCESS_TYPE.SERVICE_STOP | ACCESS_TYPE.SERVICE_QUERY_STATUS); |
|---|
| 437 |
|
|---|
| 438 |
if (schService) |
|---|
| 439 |
{ |
|---|
| 440 |
// try to stop the service |
|---|
| 441 |
if (ControlService(schService, SERVICE_COMMANDS.SERVICE_CONTROL_STOP, &_ss)) |
|---|
| 442 |
{ |
|---|
| 443 |
writefln("Stopping %s.", _serviceName); |
|---|
| 444 |
Sleep(1000); |
|---|
| 445 |
|
|---|
| 446 |
while (QueryServiceStatus( schService, &_ss )) |
|---|
| 447 |
{ |
|---|
| 448 |
if ( _ss.dwCurrentState == SERVICE_STATES.SERVICE_STOP_PENDING ) |
|---|
| 449 |
{ |
|---|
| 450 |
writef("."); |
|---|
| 451 |
Sleep( 1000 ); |
|---|
| 452 |
} |
|---|
| 453 |
else |
|---|
| 454 |
break; |
|---|
| 455 |
} |
|---|
| 456 |
|
|---|
| 457 |
if (_ss.dwCurrentState == SERVICE_STATES.SERVICE_STOPPED) |
|---|
| 458 |
writefln("\n%s stopped.", _serviceName); |
|---|
| 459 |
else |
|---|
| 460 |
writefln("\n%s failed to stop.\n", _serviceName); |
|---|
| 461 |
|
|---|
| 462 |
} |
|---|
| 463 |
|
|---|
| 464 |
// now remove the service |
|---|
| 465 |
if (DeleteService(schService)) |
|---|
| 466 |
writefln("%s removed.", _serviceName); |
|---|
| 467 |
else |
|---|
| 468 |
{ |
|---|
| 469 |
uint err = GetLastError(); |
|---|
| 470 |
char[] errMsg = _elog.getWindowsErrorText(err); |
|---|
| 471 |
writefln("DeleteService failed (%d) - %s", err, errMsg); |
|---|
| 472 |
} |
|---|
| 473 |
|
|---|
| 474 |
CloseServiceHandle(schService); |
|---|
| 475 |
} |
|---|
| 476 |
else |
|---|
| 477 |
{ |
|---|
| 478 |
uint err = GetLastError(); |
|---|
| 479 |
char[] errMsg = _elog.getWindowsErrorText(err); |
|---|
| 480 |
writefln("OpenService failed (%d) - %s", err, errMsg); |
|---|
| 481 |
} |
|---|
| 482 |
CloseServiceHandle(schSCManager); |
|---|
| 483 |
} |
|---|
| 484 |
else |
|---|
| 485 |
{ |
|---|
| 486 |
uint err = GetLastError(); |
|---|
| 487 |
char[] errMsg = _elog.getWindowsErrorText(err); |
|---|
| 488 |
writefln("OpenSCManager failed (%d) - %s", err, errMsg); |
|---|
| 489 |
} |
|---|
| 490 |
} |
|---|
| 491 |
|
|---|
| 492 |
public: |
|---|
| 493 |
|
|---|
| 494 |
/** |
|---|
| 495 |
* Get the service name as a C style string. |
|---|
| 496 |
*/ |
|---|
| 497 |
static char *CName() { return toStringz(_serviceName); } |
|---|
| 498 |
|
|---|
| 499 |
/** |
|---|
| 500 |
* Get a reference to the services event logger. |
|---|
| 501 |
*/ |
|---|
| 502 |
static EventLogger EventLog() { return _elog; } |
|---|
| 503 |
|
|---|
| 504 |
/** |
|---|
| 505 |
* Get the service name as a D style string. |
|---|
| 506 |
*/ |
|---|
| 507 |
static char[] Name() { return _serviceName; } |
|---|
| 508 |
|
|---|
| 509 |
/** |
|---|
| 510 |
* This method should be called only by the SCM. |
|---|
| 511 |
*/ |
|---|
| 512 |
extern (Windows) static export void service_ctrl(uint dwCtrlCode) |
|---|
| 513 |
{ |
|---|
| 514 |
// Handle the requested control code. |
|---|
| 515 |
switch (dwCtrlCode) |
|---|
| 516 |
{ |
|---|
| 517 |
// Stop the service. |
|---|
| 518 |
// |
|---|
| 519 |
// SERVICE_STOP_PENDING should be reported before |
|---|
| 520 |
// setting the Stop Event - hServerStopEvent - in |
|---|
| 521 |
// ServiceStop(). This avoids a race condition |
|---|
| 522 |
// which may result in a 1053 - The Service did not respond... |
|---|
| 523 |
// error. |
|---|
| 524 |
case SERVICE_COMMANDS.SERVICE_CONTROL_STOP: |
|---|
| 525 |
ReportStatusToSCMgr(SERVICE_STATES.SERVICE_STOP_PENDING, 0, 0); |
|---|
| 526 |
_self.ServiceStop(); |
|---|
| 527 |
return; |
|---|
| 528 |
case SERVICE_COMMANDS.SERVICE_CONTROL_PAUSE: |
|---|
| 529 |
ReportStatusToSCMgr(SERVICE_STATES.SERVICE_PAUSE_PENDING, 0, 0); |
|---|
| 530 |
_self.ServicePause(); |
|---|
| 531 |
return; |
|---|
| 532 |
case SERVICE_COMMANDS.SERVICE_CONTROL_CONTINUE: |
|---|
| 533 |
ReportStatusToSCMgr(SERVICE_STATES.SERVICE_CONTINUE_PENDING, 0, 0); |
|---|
| 534 |
_self.ServiceContinue(); |
|---|
| 535 |
return; |
|---|
| 536 |
|
|---|
| 537 |
// Update the service status. |
|---|
| 538 |
// |
|---|
| 539 |
case SERVICE_COMMANDS.SERVICE_CONTROL_INTERROGATE: |
|---|
| 540 |
break; |
|---|
| 541 |
|
|---|
| 542 |
// invalid control code |
|---|
| 543 |
// |
|---|
| 544 |
default: |
|---|
| 545 |
break; |
|---|
| 546 |
} |
|---|
| 547 |
|
|---|
| 548 |
ReportStatusToSCMgr(_ss.dwCurrentState, 0, 0); |
|---|
| 549 |
} |
|---|
| 550 |
|
|---|
| 551 |
/** |
|---|
| 552 |
* This method should be called only by the SCM. |
|---|
| 553 |
*/ |
|---|
| 554 |
extern (Windows) static export void service_main(uint dwArgc, char **args) |
|---|
| 555 |
{ |
|---|
| 556 |
_args.length = dwArgc; |
|---|
| 557 |
for (uint u = 0; u < dwArgc; u++) |
|---|
| 558 |
_args[u] = std.string.toString(args[u]); |
|---|
| 559 |
_ssh = RegisterServiceCtrlHandlerA(toStringz(_serviceName), &service_ctrl); |
|---|
| 560 |
|
|---|
| 561 |
// SERVICE_STATUS members that don't change in example |
|---|
| 562 |
_ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; |
|---|
| 563 |
_ss.dwServiceSpecificExitCode = 0; |
|---|
| 564 |
|
|---|
| 565 |
|
|---|
| 566 |
// report the status to the service control manager. |
|---|
| 567 |
if (!ReportStatusToSCMgr( |
|---|
| 568 |
SERVICE_STATES.SERVICE_START_PENDING, // service state |
|---|
| 569 |
0, // exit code |
|---|
| 570 |
3000)) // wait hint |
|---|
| 571 |
{ |
|---|
| 572 |
if (_ssh) |
|---|
| 573 |
ReportStatusToSCMgr(SERVICE_STATES.SERVICE_STOPPED, 0, 0); |
|---|
| 574 |
} |
|---|
| 575 |
|
|---|
| 576 |
_self.ServiceStart(_args); |
|---|
| 577 |
} |
|---|
| 578 |
|
|---|
| 579 |
/** |
|---|
| 580 |
* This method can be called by a derived class during service stop to keep |
|---|
| 581 |
* the SCM appraised of progress. |
|---|
| 582 |
*/ |
|---|
| 583 |
public static bool ReportStatusToSCMgr(uint dwCurrentState, |
|---|
| 584 |
uint dwWin32ExitCode, |
|---|
| 585 |
uint dwWaitHint) |
|---|
| 586 |
{ |
|---|
| 587 |
uint dwCheckPoint = 1; |
|---|
| 588 |
bool fResult = true; |
|---|
| 589 |
|
|---|
| 590 |
|
|---|
| 591 |
if (!_debug) // when debugging we don't report to the SCM |
|---|
| 592 |
{ |
|---|
| 593 |
if (dwCurrentState == SERVICE_STATES.SERVICE_START_PENDING) |
|---|
| 594 |
_ss.dwControlsAccepted = 0; |
|---|
| 595 |
else |
|---|
| 596 |
_ss.dwControlsAccepted = ACCEPT_COMMANDS.SERVICE_ACCEPT_STOP; |
|---|
| 597 |
|
|---|
| 598 |
_ss.dwCurrentState = dwCurrentState; |
|---|
| 599 |
_ss.dwWin32ExitCode = dwWin32ExitCode; |
|---|
| 600 |
_ss.dwWaitHint = dwWaitHint; |
|---|
| 601 |
|
|---|
| 602 |
if (dwCurrentState == SERVICE_STATES.SERVICE_RUNNING || |
|---|
| 603 |
dwCurrentState == SERVICE_STATES.SERVICE_STOPPED) |
|---|
| 604 |
_ss.dwCheckPoint = 0; |
|---|
| 605 |
else |
|---|
| 606 |
_ss.dwCheckPoint = dwCheckPoint++; |
|---|
| 607 |
|
|---|
| 608 |
|
|---|
| 609 |
// Report the status of the service to the service control manager. |
|---|
| 610 |
// |
|---|
| 611 |
fResult = (SetServiceStatus( _ssh, &_ss) == 1); |
|---|
| 612 |
} |
|---|
| 613 |
return fResult; |
|---|
| 614 |
} |
|---|
| 615 |
|
|---|
| 616 |
|
|---|
| 617 |
/** |
|---|
| 618 |
* This method should be called by your main() function to implement the service. |
|---|
| 619 |
*/ |
|---|
| 620 |
static void implementMain(char[][] args) |
|---|
| 621 |
{ |
|---|
| 622 |
if (args.length == 2 && (args[1][0] == '-' || args[1][0] == '/')) |
|---|
| 623 |
{ |
|---|
| 624 |
if (args[1][1] == 'i' || args[1][1] == 'I') |
|---|
| 625 |
{ |
|---|
| 626 |
writefln("Installing service: %s", ServiceBase.Name); |
|---|
| 627 |
ServiceBase.InstallService(); |
|---|
| 628 |
return; |
|---|
| 629 |
} |
|---|
| 630 |
else if (args[1][1] == 'r' || args[1][1] == 'R') |
|---|
| 631 |
{ |
|---|
| 632 |
writefln("Removing service: %s", ServiceBase.Name); |
|---|
| 633 |
ServiceBase.RemoveService(); |
|---|
| 634 |
return; |
|---|
| 635 |
} |
|---|
| 636 |
else if (args[1][1] == 'd' || args[1][1] == 'D') |
|---|
| 637 |
{ |
|---|
| 638 |
writefln("Service - debug mode requested"); |
|---|
| 639 |
return; |
|---|
| 640 |
} |
|---|
| 641 |
else |
|---|
| 642 |
{ |
|---|
| 643 |
writefln("Usage: <service Name> [-i|-I|-r|-R|-d|-D|/i|/I|/r|/R|/d|/D]"); |
|---|
| 644 |
return; |
|---|
| 645 |
} |
|---|
| 646 |
} |
|---|
| 647 |
else if (args.length == 1) |
|---|
| 648 |
ServiceBase.StartService(); |
|---|
| 649 |
else |
|---|
| 650 |
writefln("Usage: <service Name> [-i|-I|-r|-R|-d|-D|/i|/I|/r|/R|/d|/D]"); |
|---|
| 651 |
} |
|---|
| 652 |
} |
|---|
| 653 |
|
|---|
| 654 |
/+ |
|---|
| 655 |
// Derive a class from ServiceBase implementing the abstract methods. |
|---|
| 656 |
class NullService : ServiceBase |
|---|
| 657 |
{ |
|---|
| 658 |
this() |
|---|
| 659 |
{ |
|---|
| 660 |
super("Dummy", false); |
|---|
| 661 |
} |
|---|
| 662 |
|
|---|
| 663 |
public void onServiceStart(char[][] args) |
|---|
| 664 |
{ |
|---|
| 665 |
ServiceBase.EventLog.logMessage("NullService onServiceStart was called"); |
|---|
| 666 |
} |
|---|
| 667 |
|
|---|
| 668 |
public void onServiceStop() |
|---|
| 669 |
{ |
|---|
| 670 |
ServiceBase.EventLog.logMessage("NullService onServiceStop was called"); |
|---|
| 671 |
} |
|---|
| 672 |
} |
|---|
| 673 |
|
|---|
| 674 |
// Create an object of the derived class, then call the base class implementMain method. |
|---|
| 675 |
void main(char[][] args) |
|---|
| 676 |
{ |
|---|
| 677 |
try |
|---|
| 678 |
{ |
|---|
| 679 |
ServiceBase sb = new NullService(); |
|---|
| 680 |
ServiceBase.implementMain(args); |
|---|
| 681 |
} |
|---|
| 682 |
catch (Exception ex) |
|---|
| 683 |
{ |
|---|
| 684 |
writefln(ex.toString()); |
|---|
| 685 |
} |
|---|
| 686 |
} |
|---|
| 687 |
+/ |
|---|