blob: c385fa05c63093267414be16e2a3529dbd44c101 [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19// -=- Service.cxx
20
21#include <rfb_win32/Service.h>
22#include <rfb_win32/MsgWindow.h>
23#include <rfb_win32/DynamicFn.h>
24#include <rfb_win32/ModuleFileName.h>
25#include <rfb_win32/Registry.h>
26#include <rfb_win32/OSVersion.h>
27#include <rfb/Threading.h>
28#include <logmessages/messages.h>
29#include <rdr/Exception.h>
30#include <rfb/LogWriter.h>
31
32
33using namespace rdr;
34using namespace rfb;
35using namespace win32;
36
37static LogWriter vlog("Service");
38
39
40// - Internal service implementation functions
41
42Service* service = 0;
Samuel Mannehed60c41932014-02-07 14:53:24 +000043bool runAsService = false;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000044
45VOID WINAPI serviceHandler(DWORD control) {
46 switch (control) {
47 case SERVICE_CONTROL_INTERROGATE:
48 vlog.info("cmd: report status");
49 service->setStatus();
50 return;
51 case SERVICE_CONTROL_PARAMCHANGE:
52 vlog.info("cmd: param change");
53 service->readParams();
54 return;
55 case SERVICE_CONTROL_SHUTDOWN:
56 vlog.info("cmd: OS shutdown");
57 service->osShuttingDown();
58 return;
59 case SERVICE_CONTROL_STOP:
60 vlog.info("cmd: stop");
61 service->setStatus(SERVICE_STOP_PENDING);
62 service->stop();
63 return;
64 };
65 vlog.debug("cmd: unknown %lu", control);
66}
67
68
69// -=- Message window derived class used under Win9x to implement stopService
70
71#define WM_SMSG_SERVICE_STOP WM_USER
72
73class ServiceMsgWindow : public MsgWindow {
74public:
75 ServiceMsgWindow(const TCHAR* name) : MsgWindow(name) {}
76 LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
77 switch (msg) {
78 case WM_SMSG_SERVICE_STOP:
79 service->stop();
80 return TRUE;
81 }
82 return MsgWindow::processMessage(msg, wParam, lParam);
83 }
84
85 static const TCHAR* baseName;
86};
87
88const TCHAR* ServiceMsgWindow::baseName = _T("ServiceWindow:");
89
90
91// -=- Service main procedure, used under WinNT/2K/XP by the SCM
92
93VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) {
94 vlog.debug("entering %s serviceProc", service->getName());
95 vlog.info("registering handler...");
96 service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
97 if (!service->status_handle) {
98 DWORD err = GetLastError();
99 vlog.error("failed to register handler: %lu", err);
100 ExitProcess(err);
101 }
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100102 vlog.debug("registered handler (%p)", service->status_handle);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000103 service->setStatus(SERVICE_START_PENDING);
104 vlog.debug("entering %s serviceMain", service->getName());
105 service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv);
106 vlog.debug("leaving %s serviceMain", service->getName());
107 service->setStatus(SERVICE_STOPPED);
108}
109
110
111// -=- Service
112
113Service::Service(const TCHAR* name_) : name(name_) {
114 vlog.debug("Service");
115 status_handle = 0;
116 status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
117 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
118 status.dwWin32ExitCode = NO_ERROR;
119 status.dwServiceSpecificExitCode = 0;
120 status.dwCheckPoint = 0;
121 status.dwWaitHint = 30000;
122 status.dwCurrentState = SERVICE_STOPPED;
123}
124
125void
126Service::start() {
127 if (osVersion.isPlatformNT) {
128 SERVICE_TABLE_ENTRY entry[2];
129 entry[0].lpServiceName = (TCHAR*)name;
130 entry[0].lpServiceProc = serviceProc;
131 entry[1].lpServiceName = NULL;
132 entry[1].lpServiceProc = NULL;
133 vlog.debug("entering dispatcher");
134 if (!SetProcessShutdownParameters(0x100, 0))
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100135 vlog.error("unable to set shutdown parameters: %lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000136 service = this;
137 if (!StartServiceCtrlDispatcher(entry))
138 throw SystemException("unable to start service", GetLastError());
139 } else {
140
141 // - Create the service window, so the service can be stopped
142 TCharArray wndName(_tcslen(getName()) + _tcslen(ServiceMsgWindow::baseName) + 1);
143 _tcscpy(wndName.buf, ServiceMsgWindow::baseName);
144 _tcscat(wndName.buf, getName());
145 ServiceMsgWindow service_window(wndName.buf);
146
147 // - Locate the RegisterServiceProcess function
148 typedef DWORD (WINAPI * _RegisterServiceProcess_proto)(DWORD, DWORD);
149 DynamicFn<_RegisterServiceProcess_proto> _RegisterServiceProcess(_T("kernel32.dll"), "RegisterServiceProcess");
150 if (!_RegisterServiceProcess.isValid())
151 throw Exception("unable to find RegisterServiceProcess");
152
153 // - Run the service
154 (*_RegisterServiceProcess)(NULL, 1);
155 service = this;
156 serviceMain(0, 0);
157 (*_RegisterServiceProcess)(NULL, 0);
158 }
159}
160
161void
162Service::setStatus() {
163 setStatus(status.dwCurrentState);
164}
165
166void
167Service::setStatus(DWORD state) {
168 if (!osVersion.isPlatformNT)
169 return;
170 if (status_handle == 0) {
171 vlog.debug("warning - cannot setStatus");
172 return;
173 }
174 status.dwCurrentState = state;
175 status.dwCheckPoint++;
176 if (!SetServiceStatus(status_handle, &status)) {
177 status.dwCurrentState = SERVICE_STOPPED;
178 status.dwWin32ExitCode = GetLastError();
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100179 vlog.error("unable to set service status:%lu", status.dwWin32ExitCode);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000180 }
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100181 vlog.debug("set status to %lu(%lu)", state, status.dwCheckPoint);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000182}
183
184Service::~Service() {
185 vlog.debug("~Service");
186 service = 0;
187}
188
189
190// Find out whether this process is running as the WinVNC service
191bool thisIsService() {
192 return service && (service->status.dwCurrentState != SERVICE_STOPPED);
193}
194
195
196// -=- Desktop handling code
197
198// Switch the current thread to the specified desktop
199static bool
200switchToDesktop(HDESK desktop) {
201 HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
202 if (!SetThreadDesktop(desktop)) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100203 vlog.debug("switchToDesktop failed:%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000204 return false;
205 }
206 if (!CloseDesktop(old_desktop))
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100207 vlog.debug("unable to close old desktop:%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000208 return true;
209}
210
211// Determine whether the thread's current desktop is the input one
212static bool
213inputDesktopSelected() {
214 HDESK current = GetThreadDesktop(GetCurrentThreadId());
215 HDESK input = OpenInputDesktop(0, FALSE,
216 DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
217 DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
218 DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
219 DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
220 if (!input) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100221 vlog.debug("unable to OpenInputDesktop(1):%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000222 return false;
223 }
224
225 DWORD size;
226 char currentname[256];
227 char inputname[256];
228
229 if (!GetUserObjectInformation(current, UOI_NAME, currentname, 256, &size)) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100230 vlog.debug("unable to GetUserObjectInformation(1):%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000231 CloseDesktop(input);
232 return false;
233 }
234 if (!GetUserObjectInformation(input, UOI_NAME, inputname, 256, &size)) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100235 vlog.debug("unable to GetUserObjectInformation(2):%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000236 CloseDesktop(input);
237 return false;
238 }
239 if (!CloseDesktop(input))
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100240 vlog.debug("unable to close input desktop:%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000241
242 // *** vlog.debug("current=%s, input=%s", currentname, inputname);
243 bool result = strcmp(currentname, inputname) == 0;
244 return result;
245}
246
247// Switch the current thread into the input desktop
248static bool
249selectInputDesktop() {
250 // - Open the input desktop
251 HDESK desktop = OpenInputDesktop(0, FALSE,
252 DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
253 DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
254 DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
255 DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
256 if (!desktop) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100257 vlog.debug("unable to OpenInputDesktop(2):%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000258 return false;
259 }
260
261 // - Switch into it
262 if (!switchToDesktop(desktop)) {
263 CloseDesktop(desktop);
264 return false;
265 }
266
267 // ***
268 DWORD size = 256;
269 char currentname[256];
270 if (GetUserObjectInformation(desktop, UOI_NAME, currentname, 256, &size)) {
271 vlog.debug("switched to %s", currentname);
272 }
273 // ***
274
275 vlog.debug("switched to input desktop");
276
277 return true;
278}
279
280
281// -=- Access points to desktop-switching routines
282
283bool
284rfb::win32::desktopChangeRequired() {
285 if (!osVersion.isPlatformNT)
286 return false;
287
288 return !inputDesktopSelected();
289}
290
291bool
292rfb::win32::changeDesktop() {
293 if (!osVersion.isPlatformNT)
294 return true;
295 if (osVersion.cannotSwitchDesktop)
296 return false;
297
298 return selectInputDesktop();
299}
300
301
302// -=- Ctrl-Alt-Del emulation
303
304class CADThread : public Thread {
305public:
306 CADThread() : Thread("CtrlAltDel Emulator"), result(false) {}
307 virtual void run() {
308 HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
309
310 if (switchToDesktop(OpenDesktop(_T("Winlogon"), 0, FALSE, DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
311 DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
312 DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
313 DESKTOP_SWITCHDESKTOP | GENERIC_WRITE))) {
314 PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG(MOD_ALT | MOD_CONTROL, VK_DELETE));
315 switchToDesktop(old_desktop);
316 result = true;
317 }
318 }
319 bool result;
320};
321
322bool
323rfb::win32::emulateCtrlAltDel() {
324 if (!osVersion.isPlatformNT)
325 return false;
326
Samuel Mannehed60c41932014-02-07 14:53:24 +0000327 if (osVersion.dwMajorVersion >= 6) {
328 rfb::win32::Handle sessionEventCad =
329 CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNCCad");
330 SetEvent(sessionEventCad);
331 return true;
332 }
333
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000334 CADThread* cad_thread = new CADThread();
335 vlog.debug("emulate Ctrl-Alt-Del");
336 if (cad_thread) {
337 cad_thread->start();
338 cad_thread->join();
339 bool result = cad_thread->result;
340 delete cad_thread;
341 return result;
342 }
343 return false;
344}
345
346
347// -=- Application Event Log target Logger class
348
349class Logger_EventLog : public Logger {
350public:
351 Logger_EventLog(const TCHAR* srcname) : Logger("EventLog") {
352 eventlog = RegisterEventSource(NULL, srcname);
353 if (!eventlog)
354 printf("Unable to open event log:%ld\n", GetLastError());
355 }
356 ~Logger_EventLog() {
357 if (eventlog)
358 DeregisterEventSource(eventlog);
359 }
360
361 virtual void write(int level, const char *logname, const char *message) {
362 if (!eventlog) return;
363 TStr log(logname), msg(message);
364 const TCHAR* strings[] = {log, msg};
365 WORD type = EVENTLOG_INFORMATION_TYPE;
366 if (level == 0) type = EVENTLOG_ERROR_TYPE;
367 if (!ReportEvent(eventlog, type, 0, VNC4LogMessage, NULL, 2, 0, strings, NULL)) {
368 // *** It's not at all clear what is the correct behaviour if this fails...
369 printf("ReportEvent failed:%ld\n", GetLastError());
370 }
371 }
372
373protected:
374 HANDLE eventlog;
375};
376
377static Logger_EventLog* logger = 0;
378
379bool rfb::win32::initEventLogLogger(const TCHAR* srcname) {
380 if (logger)
381 return false;
382 if (osVersion.isPlatformNT) {
383 logger = new Logger_EventLog(srcname);
384 logger->registerLogger();
385 return true;
386 } else {
387 return false;
388 }
389}
390
391
392// -=- Registering and unregistering the service
393
Pierre Ossman018c67e2016-01-12 10:13:17 +0100394bool rfb::win32::registerService(const TCHAR* name,
395 const TCHAR* display,
396 const TCHAR* desc,
Adam Tkac8fb6ac02010-06-25 11:24:27 +0000397 int argc, char** argv) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000398
399 // - Initialise the default service parameters
400 const TCHAR* defaultcmdline;
401 if (osVersion.isPlatformNT)
402 defaultcmdline = _T("-service");
403 else
404 defaultcmdline = _T("-noconsole -service");
405
406 // - Get the full pathname of our executable
407 ModuleFileName buffer;
408
409 // - Calculate the command-line length
410 int cmdline_len = _tcslen(buffer.buf) + 4;
411 int i;
412 for (i=0; i<argc; i++) {
413 cmdline_len += strlen(argv[i]) + 3;
414 }
415
416 // - Add the supplied extra parameters to the command line
417 TCharArray cmdline(cmdline_len+_tcslen(defaultcmdline));
418 _stprintf(cmdline.buf, _T("\"%s\" %s"), buffer.buf, defaultcmdline);
419 for (i=0; i<argc; i++) {
420 _tcscat(cmdline.buf, _T(" \""));
421 _tcscat(cmdline.buf, TStr(argv[i]));
422 _tcscat(cmdline.buf, _T("\""));
423 }
424
425 // - Register the service
426
427 if (osVersion.isPlatformNT) {
428
429 // - Open the SCM
430 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
431 if (!scm)
432 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
433
Pierre Ossman018c67e2016-01-12 10:13:17 +0100434 // - Add the service
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000435 ServiceHandle service = CreateService(scm,
Pierre Ossman018c67e2016-01-12 10:13:17 +0100436 name, display, SC_MANAGER_ALL_ACCESS,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000437 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
438 SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
439 cmdline.buf, NULL, NULL, NULL, NULL, NULL);
440 if (!service)
441 throw rdr::SystemException("unable to create service", GetLastError());
442
Pierre Ossman018c67e2016-01-12 10:13:17 +0100443 // - Set a description
444 SERVICE_DESCRIPTION sdesc = {(LPTSTR)desc};
445 ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sdesc);
446
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000447 // - Register the event log source
448 RegKey hk, hk2;
449
450 hk2.createKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
451 hk.createKey(hk2, name);
452
453 for (i=_tcslen(buffer.buf); i>0; i--) {
454 if (buffer.buf[i] == _T('\\')) {
455 buffer.buf[i+1] = 0;
456 break;
457 }
458 }
459
460 const TCHAR* dllFilename = _T("logmessages.dll");
461 TCharArray dllPath(_tcslen(buffer.buf) + _tcslen(dllFilename) + 1);
462 _tcscpy(dllPath.buf, buffer.buf);
463 _tcscat(dllPath.buf, dllFilename);
464
465 hk.setExpandString(_T("EventMessageFile"), dllPath.buf);
466 hk.setInt(_T("TypesSupported"), EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE);
467
468 } else {
469
470 RegKey services;
471 services.createKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
472 services.setString(name, cmdline.buf);
473
474 }
475
476 Sleep(500);
477
478 return true;
479}
480
481bool rfb::win32::unregisterService(const TCHAR* name) {
482 if (osVersion.isPlatformNT) {
483
484 // - Open the SCM
485 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
486 if (!scm)
487 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
488
489 // - Create the service
490 ServiceHandle service = OpenService(scm, name, SC_MANAGER_ALL_ACCESS);
491 if (!service)
492 throw rdr::SystemException("unable to locate the service", GetLastError());
493 if (!DeleteService(service))
494 throw rdr::SystemException("unable to remove the service", GetLastError());
495
496 // - Register the event log source
497 RegKey hk;
498 hk.openKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
499 hk.deleteKey(name);
500
501 } else {
502
503 RegKey services;
504 services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
505 services.deleteValue(name);
506
507 }
508
509 Sleep(500);
510
511 return true;
512}
513
514
515// -=- Starting and stopping the service
516
517HWND findServiceWindow(const TCHAR* name) {
518 TCharArray wndName(_tcslen(ServiceMsgWindow::baseName)+_tcslen(name)+1);
519 _tcscpy(wndName.buf, ServiceMsgWindow::baseName);
520 _tcscat(wndName.buf, name);
Peter Åstrand79ed17a2008-12-10 13:02:50 +0000521 vlog.debug("searching for %s window", wndName.buf);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000522 return FindWindow(0, wndName.buf);
523}
524
525bool rfb::win32::startService(const TCHAR* name) {
526
527 if (osVersion.isPlatformNT) {
528 // - Open the SCM
529 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
530 if (!scm)
531 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
532
533 // - Locate the service
534 ServiceHandle service = OpenService(scm, name, SERVICE_START);
535 if (!service)
536 throw rdr::SystemException("unable to open the service", GetLastError());
537
538 // - Start the service
539 if (!StartService(service, 0, NULL))
540 throw rdr::SystemException("unable to start the service", GetLastError());
541 } else {
542 // - Check there is no service window
543 if (findServiceWindow(name))
544 throw rdr::Exception("the service is already running");
545
546 // - Find the RunServices registry key
547 RegKey services;
548 services.openKey(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\Windows\\CurrentVersion\\RunServices"));
549
550 // - Read the command-line from it
Peter Åstrandb22dbef2008-12-09 14:57:53 +0000551 TCharArray cmdLine(services.getString(name));
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000552
553 // - Start the service
554 PROCESS_INFORMATION proc_info;
555 STARTUPINFO startup_info;
556 ZeroMemory(&startup_info, sizeof(startup_info));
557 startup_info.cb = sizeof(startup_info);
558 if (!CreateProcess(0, cmdLine.buf, 0, 0, FALSE, CREATE_NEW_CONSOLE, 0, 0, &startup_info, &proc_info)) {
559 throw SystemException("unable to start service", GetLastError());
560 }
561 }
562
563 Sleep(500);
564
565 return true;
566}
567
568bool rfb::win32::stopService(const TCHAR* name) {
569 if (osVersion.isPlatformNT) {
570 // - Open the SCM
571 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
572 if (!scm)
573 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
574
575 // - Locate the service
576 ServiceHandle service = OpenService(scm, name, SERVICE_STOP);
577 if (!service)
578 throw rdr::SystemException("unable to open the service", GetLastError());
579
580 // - Start the service
581 SERVICE_STATUS status;
582 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
583 throw rdr::SystemException("unable to stop the service", GetLastError());
584
585 } else {
586 // - Find the service window
587 HWND service_window = findServiceWindow(name);
588 if (!service_window)
589 throw Exception("unable to locate running service");
590
591 // Tell it to quit
592 vlog.debug("sending service stop request");
593 if (!SendMessage(service_window, WM_SMSG_SERVICE_STOP, 0, 0))
594 throw Exception("unable to stop service");
595
596 // Check it's quitting...
597 DWORD process_id = 0;
598 HANDLE process = 0;
599 if (!GetWindowThreadProcessId(service_window, &process_id))
600 throw SystemException("unable to verify service has quit", GetLastError());
601 process = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, process_id);
602 if (!process)
603 throw SystemException("unable to obtain service handle", GetLastError());
604 int retries = 5;
605 vlog.debug("checking status");
606 while (retries-- && (WaitForSingleObject(process, 1000) != WAIT_OBJECT_0)) {}
607 if (!retries) {
608 vlog.debug("failed to quit - terminating");
609 // May not have quit because of silly Win9x registry watching bug..
610 if (!TerminateProcess(process, 1))
611 throw SystemException("unable to terminate process!", GetLastError());
612 throw Exception("service failed to quit - called TerminateProcess");
613 }
614 }
615
616 Sleep(500);
617
618 return true;
619}
620
621DWORD rfb::win32::getServiceState(const TCHAR* name) {
622 if (osVersion.isPlatformNT) {
623 // - Open the SCM
624 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
625 if (!scm)
626 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
627
628 // - Locate the service
629 ServiceHandle service = OpenService(scm, name, SERVICE_INTERROGATE);
630 if (!service)
631 throw rdr::SystemException("unable to open the service", GetLastError());
632
633 // - Get the service status
634 SERVICE_STATUS status;
635 if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status))
636 throw rdr::SystemException("unable to query the service", GetLastError());
637
638 return status.dwCurrentState;
639 } else {
640 HWND service_window = findServiceWindow(name);
641 return service_window ? SERVICE_RUNNING : SERVICE_STOPPED;
642 }
643}
644
645char* rfb::win32::serviceStateName(DWORD state) {
646 switch (state) {
647 case SERVICE_RUNNING: return strDup("Running");
648 case SERVICE_STOPPED: return strDup("Stopped");
649 case SERVICE_STOP_PENDING: return strDup("Stopping");
650 };
651 CharArray tmp(32);
652 sprintf(tmp.buf, "Unknown (%lu)", state);
653 return tmp.takeBuf();
654}
655
656
657bool rfb::win32::isServiceProcess() {
Samuel Mannehed60c41932014-02-07 14:53:24 +0000658 return runAsService;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000659}