Index: 1.3.1/lib/arch/CArchMiscWindows.h =================================================================== --- 1.3.1/lib/arch/CArchMiscWindows.h (revision 897) +++ 1.3.1/lib/arch/CArchMiscWindows.h (working copy) @@ -56,6 +56,12 @@ */ static bool isWindowsModern(); + //! Test if windows 95, et al. + /*! + Returns true iff the platform is vista or newer + */ + static bool isWindowsNT6Plus(); + //! Set the application icons /*! Set the application icons. @@ -134,7 +140,10 @@ const std::string& value); //! Read a string value from the registry - static std::string readValueString(HKEY, const TCHAR* name); + static std::string readValueString(HKEY, const TCHAR* name); + + //! Read an expandable string value from the registry + static std::string readValueExpandString(HKEY, const TCHAR* name); //! Read a DWORD value from the registry static DWORD readValueInt(HKEY, const TCHAR* name); Index: 1.3.1/lib/arch/CArchDaemonWindows.h =================================================================== --- 1.3.1/lib/arch/CArchDaemonWindows.h (revision 897) +++ 1.3.1/lib/arch/CArchDaemonWindows.h (working copy) @@ -20,7 +20,8 @@ #include "IArchDaemon.h" #include "IArchMultithread.h" #include "stdstring.h" -#include +#include +#include #include #define ARCH_DAEMON CArchDaemonWindows @@ -92,6 +93,15 @@ void doDaemonRunning(bool running); UINT doGetDaemonQuitMessage(); + //! Create new process + /*! + If Vista+ then the service needs to create a new process of synergyX + on the current session (most probably 1) instead of service session 0. This + is different from XP- when the first user session and services both used + session 0. Returns 0 on success. + */ + int createProcessOnCurrentSession(); + static void setStatus(DWORD state); static void setStatus(DWORD state, DWORD step, DWORD waitHint); static void setStatusError(DWORD error); @@ -104,6 +114,9 @@ void serviceHandler(DWORD ctrl); static void WINAPI serviceHandlerEntry(DWORD ctrl); + std::string m_commandLine; + std::string m_imagePath; + private: class XArchDaemonRunFailed { public: Index: 1.3.1/lib/arch/CArchMiscWindows.cpp =================================================================== --- 1.3.1/lib/arch/CArchMiscWindows.cpp (revision 897) +++ 1.3.1/lib/arch/CArchMiscWindows.cpp (working copy) @@ -89,6 +89,29 @@ return result; } +bool +CArchMiscWindows::isWindowsNT6Plus() +{ + static bool init = false; + static bool result = false; + + if (!init) { + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume not 6+ + result = false; + } + else { + result = (version.dwPlatformId == VER_PLATFORM_WIN32_NT && + version.dwMajorVersion >= 6); + } + init = true; + } + return result; +} + + void CArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon) { @@ -317,6 +340,12 @@ CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) { return readBinaryOrString(key, name, REG_SZ); +} + +std::string +CArchMiscWindows::readValueExpandString(HKEY key, const TCHAR* name) +{ + return readBinaryOrString(key, name, REG_EXPAND_SZ); } std::string Index: 1.3.1/lib/arch/CArchDaemonWindows.cpp =================================================================== --- 1.3.1/lib/arch/CArchDaemonWindows.cpp (revision 897) +++ 1.3.1/lib/arch/CArchDaemonWindows.cpp (working copy) @@ -1,770 +1,884 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file COPYING that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "CArchDaemonWindows.h" -#include "CArch.h" -#include "CArchMiscWindows.h" -#include "XArchWindows.h" -#include "stdvector.h" - -// -// CArchDaemonWindows -// - -CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; - -CArchDaemonWindows::CArchDaemonWindows() -{ - m_quitMessage = RegisterWindowMessage("SynergyDaemonExit"); -} - -CArchDaemonWindows::~CArchDaemonWindows() -{ - // do nothing -} - -int -CArchDaemonWindows::runDaemon(RunFunc runFunc) -{ - assert(s_daemon != NULL); - - return s_daemon->doRunDaemon(runFunc); -} - -void -CArchDaemonWindows::daemonRunning(bool running) -{ - // if s_daemon is NULL we assume we're running on the windows - // 95 family and we just ignore this call so the caller doesn't - // have to go through the trouble of not calling it on the - // windows 95 family. - if (s_daemon != NULL) { - s_daemon->doDaemonRunning(running); - } -} - -UINT -CArchDaemonWindows::getDaemonQuitMessage() -{ - if (s_daemon != NULL) { - return s_daemon->doGetDaemonQuitMessage(); - } - else { - return 0; - } -} - -void -CArchDaemonWindows::daemonFailed(int result) -{ - // if s_daemon is NULL we assume we're running on the windows - // 95 family and we just ignore this call so the caller doesn't - // have to go through the trouble of not calling it on the - // windows 95 family. - if (s_daemon != NULL) { - throw XArchDaemonRunFailed(result); - } -} - -void -CArchDaemonWindows::installDaemon(const char* name, - const char* description, - const char* pathname, - const char* commandLine, - const char* dependencies, - bool allUsers) -{ - // if not for all users then use the user's autostart registry. - // key. if windows 95 family then use windows 95 services key. - if (!allUsers || CArchMiscWindows::isWindows95Family()) { - // open registry - HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? - open95ServicesKey() : openUserStartupKey(); - if (key == NULL) { - // can't open key - throw XArchDaemonInstallFailed(new XArchEvalWindows); - } - - // construct entry - std::string value; - value += "\""; - value += pathname; - value += "\" "; - value += commandLine; - - // install entry - CArchMiscWindows::setValue(key, name, value); - - // clean up - CArchMiscWindows::closeKey(key); - } - - // windows NT family services - else { - // open service manager - SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); - if (mgr == NULL) { - // can't open service manager - throw XArchDaemonInstallFailed(new XArchEvalWindows); - } - - // create the service - SC_HANDLE service = CreateService(mgr, - name, - name, - 0, - SERVICE_WIN32_OWN_PROCESS | - SERVICE_INTERACTIVE_PROCESS, - SERVICE_AUTO_START, - SERVICE_ERROR_NORMAL, - pathname, - NULL, - NULL, - dependencies, - NULL, - NULL); - if (service == NULL) { - // can't create service - DWORD err = GetLastError(); - if (err != ERROR_SERVICE_EXISTS) { - CloseServiceHandle(mgr); - throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); - } - } - - // done with service and manager - CloseServiceHandle(service); - CloseServiceHandle(mgr); - - // open the registry key for this service - HKEY key = openNTServicesKey(); - key = CArchMiscWindows::addKey(key, name); - if (key == NULL) { - // can't open key - DWORD err = GetLastError(); - try { - uninstallDaemon(name, allUsers); - } - catch (...) { - // ignore - } - throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); - } - - // set the description - CArchMiscWindows::setValue(key, _T("Description"), description); - - // set command line - key = CArchMiscWindows::addKey(key, _T("Parameters")); - if (key == NULL) { - // can't open key - DWORD err = GetLastError(); - CArchMiscWindows::closeKey(key); - try { - uninstallDaemon(name, allUsers); - } - catch (...) { - // ignore - } - throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); - } - CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine); - - // done with registry - CArchMiscWindows::closeKey(key); - } -} - -void -CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) -{ - // if not for all users then use the user's autostart registry. - // key. if windows 95 family then use windows 95 services key. - if (!allUsers || CArchMiscWindows::isWindows95Family()) { - // open registry - HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? - open95ServicesKey() : openUserStartupKey(); - if (key == NULL) { - // can't open key. daemon is probably not installed. - throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows); - } - - // remove entry - CArchMiscWindows::deleteValue(key, name); - - // clean up - CArchMiscWindows::closeKey(key); - } - - // windows NT family services - else { - // remove parameters for this service. ignore failures. - HKEY key = openNTServicesKey(); - key = CArchMiscWindows::openKey(key, name); - if (key != NULL) { - CArchMiscWindows::deleteKey(key, _T("Parameters")); - CArchMiscWindows::closeKey(key); - } - - // open service manager - SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); - if (mgr == NULL) { - // can't open service manager - throw XArchDaemonUninstallFailed(new XArchEvalWindows); - } - - // open the service. oddly, you must open a service to delete it. - SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP); - if (service == NULL) { - DWORD err = GetLastError(); - CloseServiceHandle(mgr); - if (err != ERROR_SERVICE_DOES_NOT_EXIST) { - throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); - } - throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); - } - - // stop the service. we don't care if we fail. - SERVICE_STATUS status; - ControlService(service, SERVICE_CONTROL_STOP, &status); - - // delete the service - const bool okay = (DeleteService(service) == 0); - const DWORD err = GetLastError(); - - // clean up - CloseServiceHandle(service); - CloseServiceHandle(mgr); - - // handle failure. ignore error if service isn't installed anymore. - if (!okay && isDaemonInstalled(name, allUsers)) { - if (err == ERROR_IO_PENDING) { - // this seems to be a spurious error - return; - } - if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { - throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); - } - throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); - } - } -} - -int -CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) -{ - assert(name != NULL); - assert(func != NULL); - - // windows 95 family services - if (CArchMiscWindows::isWindows95Family()) { - typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD); - - // mark this process as a service so it's not killed when the - // user logs off. - HINSTANCE kernel = LoadLibrary("kernel32.dll"); - if (kernel == NULL) { - throw XArchDaemonFailed(new XArchEvalWindows); - } - RegisterServiceProcessT RegisterServiceProcess = - reinterpret_cast( - GetProcAddress(kernel, - "RegisterServiceProcess")); - if (RegisterServiceProcess == NULL) { - // missing RegisterServiceProcess function - DWORD err = GetLastError(); - FreeLibrary(kernel); - throw XArchDaemonFailed(new XArchEvalWindows(err)); - } - if (RegisterServiceProcess(0, 1) == 0) { - // RegisterServiceProcess failed - DWORD err = GetLastError(); - FreeLibrary(kernel); - throw XArchDaemonFailed(new XArchEvalWindows(err)); - } - FreeLibrary(kernel); - - // now simply call the daemon function - return func(1, &name); - } - - // windows NT family services - else { - // save daemon function - m_daemonFunc = func; - - // construct the service entry - SERVICE_TABLE_ENTRY entry[2]; - entry[0].lpServiceName = const_cast(name); - entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry; - entry[1].lpServiceName = NULL; - entry[1].lpServiceProc = NULL; - - // hook us up to the service control manager. this won't return - // (if successful) until the processes have terminated. - s_daemon = this; - if (StartServiceCtrlDispatcher(entry) == 0) { - // StartServiceCtrlDispatcher failed - s_daemon = NULL; - throw XArchDaemonFailed(new XArchEvalWindows); - } - - s_daemon = NULL; - return m_daemonResult; - } -} - -bool -CArchDaemonWindows::canInstallDaemon(const char* /*name*/, bool allUsers) -{ - // if not for all users then use the user's autostart registry. - // key. if windows 95 family then use windows 95 services key. - if (!allUsers || CArchMiscWindows::isWindows95Family()) { - // check if we can open the registry key - HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? - open95ServicesKey() : openUserStartupKey(); - CArchMiscWindows::closeKey(key); - return (key != NULL); - } - - // windows NT family services - else { - // check if we can open service manager for write - SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); - if (mgr == NULL) { - return false; - } - CloseServiceHandle(mgr); - - // check if we can open the registry key - HKEY key = openNTServicesKey(); -// key = CArchMiscWindows::addKey(key, name); -// key = CArchMiscWindows::addKey(key, _T("Parameters")); - CArchMiscWindows::closeKey(key); - - return (key != NULL); - } -} - -bool -CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) -{ - // if not for all users then use the user's autostart registry. - // key. if windows 95 family then use windows 95 services key. - if (!allUsers || CArchMiscWindows::isWindows95Family()) { - // check if we can open the registry key - HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? - open95ServicesKey() : openUserStartupKey(); - if (key == NULL) { - return false; - } - - // check for entry - const bool installed = !CArchMiscWindows::readValueString(key, - name).empty(); - - // clean up - CArchMiscWindows::closeKey(key); - - return installed; - } - - // windows NT family services - else { - // check parameters for this service - HKEY key = openNTServicesKey(); - key = CArchMiscWindows::openKey(key, name); - key = CArchMiscWindows::openKey(key, _T("Parameters")); - if (key != NULL) { - const bool installed = !CArchMiscWindows::readValueString(key, - _T("CommandLine")).empty(); - CArchMiscWindows::closeKey(key); - if (!installed) { - return false; - } - } - - // open service manager - SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); - if (mgr == NULL) { - return false; - } - - // open the service - SC_HANDLE service = OpenService(mgr, name, GENERIC_READ); - - // clean up - if (service != NULL) { - CloseServiceHandle(service); - } - CloseServiceHandle(mgr); - - return (service != NULL); - } -} - -HKEY -CArchDaemonWindows::openNTServicesKey() -{ - static const char* s_keyNames[] = { - _T("SYSTEM"), - _T("CurrentControlSet"), - _T("Services"), - NULL - }; - - return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); -} - -HKEY -CArchDaemonWindows::open95ServicesKey() -{ - static const char* s_keyNames[] = { - _T("Software"), - _T("Microsoft"), - _T("Windows"), - _T("CurrentVersion"), - _T("RunServices"), - NULL - }; - - return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); -} - -HKEY -CArchDaemonWindows::openUserStartupKey() -{ - static const char* s_keyNames[] = { - _T("Software"), - _T("Microsoft"), - _T("Windows"), - _T("CurrentVersion"), - _T("Run"), - NULL - }; - - return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames); -} - -bool -CArchDaemonWindows::isRunState(DWORD state) -{ - switch (state) { - case SERVICE_START_PENDING: - case SERVICE_CONTINUE_PENDING: - case SERVICE_RUNNING: - return true; - - default: - return false; - } -} - -int -CArchDaemonWindows::doRunDaemon(RunFunc run) -{ - // should only be called from DaemonFunc - assert(m_serviceMutex != NULL); - assert(run != NULL); - - // create message queue for this thread - MSG dummy; - PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE); - - int result = 0; - ARCH->lockMutex(m_serviceMutex); - m_daemonThreadID = GetCurrentThreadId(); - while (m_serviceState != SERVICE_STOPPED) { - // wait until we're told to start - while (!isRunState(m_serviceState) && - m_serviceState != SERVICE_STOP_PENDING) { - ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); - } - - // run unless told to stop - if (m_serviceState != SERVICE_STOP_PENDING) { - ARCH->unlockMutex(m_serviceMutex); - try { - result = run(); - } - catch (...) { - ARCH->lockMutex(m_serviceMutex); - setStatusError(0); - m_serviceState = SERVICE_STOPPED; - setStatus(m_serviceState); - ARCH->broadcastCondVar(m_serviceCondVar); - ARCH->unlockMutex(m_serviceMutex); - throw; - } - ARCH->lockMutex(m_serviceMutex); - } - - // notify of new state - if (m_serviceState == SERVICE_PAUSE_PENDING) { - m_serviceState = SERVICE_PAUSED; - } - else { - m_serviceState = SERVICE_STOPPED; - } - setStatus(m_serviceState); - ARCH->broadcastCondVar(m_serviceCondVar); - } - ARCH->unlockMutex(m_serviceMutex); - return result; -} - -void -CArchDaemonWindows::doDaemonRunning(bool running) -{ - ARCH->lockMutex(m_serviceMutex); - if (running) { - m_serviceState = SERVICE_RUNNING; - setStatus(m_serviceState); - ARCH->broadcastCondVar(m_serviceCondVar); - } - ARCH->unlockMutex(m_serviceMutex); -} - -UINT -CArchDaemonWindows::doGetDaemonQuitMessage() -{ - return m_quitMessage; -} - -void -CArchDaemonWindows::setStatus(DWORD state) -{ - setStatus(state, 0, 0); -} - -void -CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint) -{ - assert(s_daemon != NULL); - - SERVICE_STATUS status; - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | - SERVICE_INTERACTIVE_PROCESS; - status.dwCurrentState = state; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP | - SERVICE_ACCEPT_PAUSE_CONTINUE | - SERVICE_ACCEPT_SHUTDOWN; - status.dwWin32ExitCode = NO_ERROR; - status.dwServiceSpecificExitCode = 0; - status.dwCheckPoint = step; - status.dwWaitHint = waitHint; - SetServiceStatus(s_daemon->m_statusHandle, &status); -} - -void -CArchDaemonWindows::setStatusError(DWORD error) -{ - assert(s_daemon != NULL); - - SERVICE_STATUS status; - status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | - SERVICE_INTERACTIVE_PROCESS; - status.dwCurrentState = SERVICE_STOPPED; - status.dwControlsAccepted = SERVICE_ACCEPT_STOP | - SERVICE_ACCEPT_PAUSE_CONTINUE | - SERVICE_ACCEPT_SHUTDOWN; - status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; - status.dwServiceSpecificExitCode = error; - status.dwCheckPoint = 0; - status.dwWaitHint = 0; - SetServiceStatus(s_daemon->m_statusHandle, &status); -} - -void -CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) -{ - typedef std::vector ArgList; - typedef std::vector Arguments; - const char** argv = const_cast(argvIn); - - // create synchronization objects - m_serviceMutex = ARCH->newMutex(); - m_serviceCondVar = ARCH->newCondVar(); - - // register our service handler function - m_statusHandle = RegisterServiceCtrlHandler(argv[0], - &CArchDaemonWindows::serviceHandlerEntry); - if (m_statusHandle == 0) { - // cannot start as service - m_daemonResult = -1; - ARCH->closeCondVar(m_serviceCondVar); - ARCH->closeMutex(m_serviceMutex); - return; - } - - // tell service control manager that we're starting - m_serviceState = SERVICE_START_PENDING; - setStatus(m_serviceState, 0, 10000); - - // if no arguments supplied then try getting them from the registry. - // the first argument doesn't count because it's the service name. - Arguments args; - ArgList myArgv; - if (argc <= 1) { - // read command line - std::string commandLine; - HKEY key = openNTServicesKey(); - key = CArchMiscWindows::openKey(key, argvIn[0]); - key = CArchMiscWindows::openKey(key, _T("Parameters")); - if (key != NULL) { - commandLine = CArchMiscWindows::readValueString(key, - _T("CommandLine")); - } - - // if the command line isn't empty then parse and use it - if (!commandLine.empty()) { - // parse, honoring double quoted substrings - std::string::size_type i = commandLine.find_first_not_of(" \t"); - while (i != std::string::npos && i != commandLine.size()) { - // find end of string - std::string::size_type e; - if (commandLine[i] == '\"') { - // quoted. find closing quote. - ++i; - e = commandLine.find("\"", i); - - // whitespace must follow closing quote - if (e == std::string::npos || - (e + 1 != commandLine.size() && - commandLine[e + 1] != ' ' && - commandLine[e + 1] != '\t')) { - args.clear(); - break; - } - - // extract - args.push_back(commandLine.substr(i, e - i)); - i = e + 1; - } - else { - // unquoted. find next whitespace. - e = commandLine.find_first_of(" \t", i); - if (e == std::string::npos) { - e = commandLine.size(); - } - - // extract - args.push_back(commandLine.substr(i, e - i)); - i = e + 1; - } - - // next argument - i = commandLine.find_first_not_of(" \t", i); - } - - // service name goes first - myArgv.push_back(argv[0]); - - // get pointers - for (size_t i = 0; i < args.size(); ++i) { - myArgv.push_back(args[i].c_str()); - } - - // adjust argc/argv - argc = myArgv.size(); - argv = &myArgv[0]; - } - } - - try { - // invoke daemon function - m_daemonResult = m_daemonFunc(static_cast(argc), argv); - } - catch (XArchDaemonRunFailed& e) { - setStatusError(e.m_result); - m_daemonResult = -1; - } - catch (...) { - setStatusError(1); - m_daemonResult = -1; - } - - // clean up - ARCH->closeCondVar(m_serviceCondVar); - ARCH->closeMutex(m_serviceMutex); -} - -void WINAPI -CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv) -{ - s_daemon->serviceMain(argc, argv); -} - -void -CArchDaemonWindows::serviceHandler(DWORD ctrl) -{ - assert(m_serviceMutex != NULL); - assert(m_serviceCondVar != NULL); - - ARCH->lockMutex(m_serviceMutex); - - // ignore request if service is already stopped - if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) { - if (s_daemon != NULL) { - setStatus(m_serviceState); - } - ARCH->unlockMutex(m_serviceMutex); - return; - } - - switch (ctrl) { - case SERVICE_CONTROL_PAUSE: - m_serviceState = SERVICE_PAUSE_PENDING; - setStatus(m_serviceState, 0, 5000); - PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0); - while (isRunState(m_serviceState)) { - ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); - } - break; - - case SERVICE_CONTROL_CONTINUE: - // FIXME -- maybe should flush quit messages from queue - m_serviceState = SERVICE_CONTINUE_PENDING; - setStatus(m_serviceState, 0, 5000); - ARCH->broadcastCondVar(m_serviceCondVar); - break; - - case SERVICE_CONTROL_STOP: - case SERVICE_CONTROL_SHUTDOWN: - m_serviceState = SERVICE_STOP_PENDING; - setStatus(m_serviceState, 0, 5000); - PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0); - ARCH->broadcastCondVar(m_serviceCondVar); - while (isRunState(m_serviceState)) { - ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); - } - break; - - default: - // unknown service command - // fall through - - case SERVICE_CONTROL_INTERROGATE: - setStatus(m_serviceState); - break; - } - - ARCH->unlockMutex(m_serviceMutex); -} - -void WINAPI -CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl) -{ - s_daemon->serviceHandler(ctrl); -} +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "CArchDaemonWindows.h" +#include "CArch.h" +#include "CArchMiscWindows.h" +#include "XArchWindows.h" +#include "stdvector.h" + +// +// CArchDaemonWindows +// + +CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; + +CArchDaemonWindows::CArchDaemonWindows() +{ + m_quitMessage = RegisterWindowMessage("SynergyDaemonExit"); +} + +CArchDaemonWindows::~CArchDaemonWindows() +{ + // do nothing +} + +int +CArchDaemonWindows::runDaemon(RunFunc runFunc) +{ + assert(s_daemon != NULL); + + return s_daemon->doRunDaemon(runFunc); +} + +void +CArchDaemonWindows::daemonRunning(bool running) +{ + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + s_daemon->doDaemonRunning(running); + } +} + +UINT +CArchDaemonWindows::getDaemonQuitMessage() +{ + if (s_daemon != NULL) { + return s_daemon->doGetDaemonQuitMessage(); + } + else { + return 0; + } +} + +void +CArchDaemonWindows::daemonFailed(int result) +{ + // if s_daemon is NULL we assume we're running on the windows + // 95 family and we just ignore this call so the caller doesn't + // have to go through the trouble of not calling it on the + // windows 95 family. + if (s_daemon != NULL) { + throw XArchDaemonRunFailed(result); + } +} + +void +CArchDaemonWindows::installDaemon(const char* name, + const char* description, + const char* pathname, + const char* commandLine, + const char* dependencies, + bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // open registry + HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + // can't open key + throw XArchDaemonInstallFailed(new XArchEvalWindows); + } + + // construct entry + std::string value; + value += "\""; + value += pathname; + value += "\" "; + value += commandLine; + + // install entry + CArchMiscWindows::setValue(key, name, value); + + // clean up + CArchMiscWindows::closeKey(key); + } + + // windows NT family services + else { + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + // can't open service manager + throw XArchDaemonInstallFailed(new XArchEvalWindows); + } + + // create the service + SC_HANDLE service = CreateService(mgr, + name, + name, + 0, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, + SERVICE_ERROR_NORMAL, + pathname, + NULL, + NULL, + dependencies, + NULL, + NULL); + if (service == NULL) { + // can't create service + DWORD err = GetLastError(); + if (err != ERROR_SERVICE_EXISTS) { + CloseServiceHandle(mgr); + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + } + + // done with service and manager + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + // open the registry key for this service + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::addKey(key, name); + if (key == NULL) { + // can't open key + DWORD err = GetLastError(); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + + // set the description + CArchMiscWindows::setValue(key, _T("Description"), description); + + // set command line + key = CArchMiscWindows::addKey(key, _T("Parameters")); + if (key == NULL) { + // can't open key + DWORD err = GetLastError(); + CArchMiscWindows::closeKey(key); + try { + uninstallDaemon(name, allUsers); + } + catch (...) { + // ignore + } + throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); + } + CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine); + + // done with registry + CArchMiscWindows::closeKey(key); + } +} + +void +CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // open registry + HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + // can't open key. daemon is probably not installed. + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows); + } + + // remove entry + CArchMiscWindows::deleteValue(key, name); + + // clean up + CArchMiscWindows::closeKey(key); + } + + // windows NT family services + else { + // remove parameters for this service. ignore failures. + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + if (key != NULL) { + CArchMiscWindows::deleteKey(key, _T("Parameters")); + CArchMiscWindows::closeKey(key); + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + // can't open service manager + throw XArchDaemonUninstallFailed(new XArchEvalWindows); + } + + // open the service. oddly, you must open a service to delete it. + SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP); + if (service == NULL) { + DWORD err = GetLastError(); + CloseServiceHandle(mgr); + if (err != ERROR_SERVICE_DOES_NOT_EXIST) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); + } + + // stop the service. we don't care if we fail. + SERVICE_STATUS status; + ControlService(service, SERVICE_CONTROL_STOP, &status); + + // delete the service + const bool okay = (DeleteService(service) == 0); + const DWORD err = GetLastError(); + + // clean up + CloseServiceHandle(service); + CloseServiceHandle(mgr); + + // handle failure. ignore error if service isn't installed anymore. + if (!okay && isDaemonInstalled(name, allUsers)) { + if (err == ERROR_IO_PENDING) { + // this seems to be a spurious error + return; + } + if (err != ERROR_SERVICE_MARKED_FOR_DELETE) { + throw XArchDaemonUninstallFailed(new XArchEvalWindows(err)); + } + throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err)); + } + } +} + +int +CArchDaemonWindows::daemonize(const char* name, DaemonFunc func) +{ + assert(name != NULL); + assert(func != NULL); + + // windows 95 family services + if (CArchMiscWindows::isWindows95Family()) { + typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD); + + // mark this process as a service so it's not killed when the + // user logs off. + HINSTANCE kernel = LoadLibrary("kernel32.dll"); + if (kernel == NULL) { + throw XArchDaemonFailed(new XArchEvalWindows); + } + RegisterServiceProcessT RegisterServiceProcess = + reinterpret_cast( + GetProcAddress(kernel, + "RegisterServiceProcess")); + if (RegisterServiceProcess == NULL) { + // missing RegisterServiceProcess function + DWORD err = GetLastError(); + FreeLibrary(kernel); + throw XArchDaemonFailed(new XArchEvalWindows(err)); + } + if (RegisterServiceProcess(0, 1) == 0) { + // RegisterServiceProcess failed + DWORD err = GetLastError(); + FreeLibrary(kernel); + throw XArchDaemonFailed(new XArchEvalWindows(err)); + } + FreeLibrary(kernel); + + // now simply call the daemon function + return func(1, &name); + } + + // windows NT family services + else { + // save daemon function + m_daemonFunc = func; + + // construct the service entry + SERVICE_TABLE_ENTRY entry[2]; + entry[0].lpServiceName = const_cast(name); + entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry; + entry[1].lpServiceName = NULL; + entry[1].lpServiceProc = NULL; + + // hook us up to the service control manager. this won't return + // (if successful) until the processes have terminated. + s_daemon = this; + if (StartServiceCtrlDispatcher(entry) == 0) { + // StartServiceCtrlDispatcher failed + s_daemon = NULL; + throw XArchDaemonFailed(new XArchEvalWindows); + } + + s_daemon = NULL; + return m_daemonResult; + } +} + +bool +CArchDaemonWindows::canInstallDaemon(const char* /*name*/, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // check if we can open the registry key + HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? + open95ServicesKey() : openUserStartupKey(); + CArchMiscWindows::closeKey(key); + return (key != NULL); + } + + // windows NT family services + else { + // check if we can open service manager for write + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE); + if (mgr == NULL) { + return false; + } + CloseServiceHandle(mgr); + + // check if we can open the registry key + HKEY key = openNTServicesKey(); +// key = CArchMiscWindows::addKey(key, name); +// key = CArchMiscWindows::addKey(key, _T("Parameters")); + CArchMiscWindows::closeKey(key); + + return (key != NULL); + } +} + +bool +CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers) +{ + // if not for all users then use the user's autostart registry. + // key. if windows 95 family then use windows 95 services key. + if (!allUsers || CArchMiscWindows::isWindows95Family()) { + // check if we can open the registry key + HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ? + open95ServicesKey() : openUserStartupKey(); + if (key == NULL) { + return false; + } + + // check for entry + const bool installed = !CArchMiscWindows::readValueString(key, + name).empty(); + + // clean up + CArchMiscWindows::closeKey(key); + + return installed; + } + + // windows NT family services + else { + // check parameters for this service + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, name); + key = CArchMiscWindows::openKey(key, _T("Parameters")); + if (key != NULL) { + const bool installed = !CArchMiscWindows::readValueString(key, + _T("CommandLine")).empty(); + CArchMiscWindows::closeKey(key); + if (!installed) { + return false; + } + } + + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); + if (mgr == NULL) { + return false; + } + + // open the service + SC_HANDLE service = OpenService(mgr, name, GENERIC_READ); + + // clean up + if (service != NULL) { + CloseServiceHandle(service); + } + CloseServiceHandle(mgr); + + return (service != NULL); + } +} + +HKEY +CArchDaemonWindows::openNTServicesKey() +{ + static const char* s_keyNames[] = { + _T("SYSTEM"), + _T("CurrentControlSet"), + _T("Services"), + NULL + }; + + return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); +} + +HKEY +CArchDaemonWindows::open95ServicesKey() +{ + static const char* s_keyNames[] = { + _T("Software"), + _T("Microsoft"), + _T("Windows"), + _T("CurrentVersion"), + _T("RunServices"), + NULL + }; + + return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); +} + +HKEY +CArchDaemonWindows::openUserStartupKey() +{ + static const char* s_keyNames[] = { + _T("Software"), + _T("Microsoft"), + _T("Windows"), + _T("CurrentVersion"), + _T("Run"), + NULL + }; + + return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames); +} + +bool +CArchDaemonWindows::isRunState(DWORD state) +{ + switch (state) { + case SERVICE_START_PENDING: + case SERVICE_CONTINUE_PENDING: + case SERVICE_RUNNING: + return true; + + default: + return false; + } +} + +int +CArchDaemonWindows::doRunDaemon(RunFunc run) +{ + // should only be called from DaemonFunc + assert(m_serviceMutex != NULL); + assert(run != NULL); + + // create message queue for this thread + MSG dummy; + PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE); + + int result = 0; + ARCH->lockMutex(m_serviceMutex); + m_daemonThreadID = GetCurrentThreadId(); + while (m_serviceState != SERVICE_STOPPED) { + // wait until we're told to start + while (!isRunState(m_serviceState) && + m_serviceState != SERVICE_STOP_PENDING) { + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + + // run unless told to stop + if (m_serviceState != SERVICE_STOP_PENDING) { + ARCH->unlockMutex(m_serviceMutex); + try { + if (!CArchMiscWindows::isWindowsNT6Plus()) { + result = run(); + } + else { + result = createProcessOnCurrentSession(); + } + } + catch (...) { + ARCH->lockMutex(m_serviceMutex); + setStatusError(0); + m_serviceState = SERVICE_STOPPED; + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->unlockMutex(m_serviceMutex); + throw; + } + ARCH->lockMutex(m_serviceMutex); + } + + // notify of new state + if (m_serviceState == SERVICE_PAUSE_PENDING) { + m_serviceState = SERVICE_PAUSED; + } + else { + m_serviceState = SERVICE_STOPPED; + } + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); + } + ARCH->unlockMutex(m_serviceMutex); + return result; +} + +int +CArchDaemonWindows::createProcessOnCurrentSession() +{ + int err = -1; + + // Running as Service so create access token for LocalSystem + if (ImpersonateSelf(SecurityImpersonation)) { + HANDLE hToken = NULL; + + // Access token is stored on this thread + if (OpenThreadToken(GetCurrentThread(), + TOKEN_ALL_ACCESS, FALSE, &hToken)) { + + // Get Session ID of console, will most probably be 1 on Vista+ + DWORD dwSessionId = WTSGetActiveConsoleSessionId(); + if (-1 != dwSessionId) { + + // Set Session ID on our token + if (SetTokenInformation(hToken, TokenSessionId, + &dwSessionId, sizeof(DWORD))) { + + // Create Primary token for CreateProcessAsUser + HANDLE hTokenPrimary = NULL; + if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, + SecurityImpersonation, TokenPrimary, + &hTokenPrimary)) { + + // CreateProcessAsUser may edit lpCommandLine param, + // needs to be non-constant + LPSTR lpstrCmd = new CHAR[m_commandLine.length() + 1]; + if (NULL != lpstrCmd) { + if (NULL != strcpy(lpstrCmd, + m_commandLine.c_str())) { + + // Set the desktop to winlogon + STARTUPINFO stStartupInfo; + ZeroMemory(&stStartupInfo, + sizeof(STARTUPINFO)); + stStartupInfo.cb = sizeof(STARTUPINFO); + stStartupInfo.lpDesktop = "winsta0\\winlogon"; + + PROCESS_INFORMATION stProcessInformation; + ZeroMemory(&stProcessInformation, + sizeof(stProcessInformation)); + + // Create the process + if (CreateProcessAsUser( + hTokenPrimary, + m_imagePath.c_str(), + lpstrCmd, + NULL, + NULL, + FALSE, + NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, + NULL, + NULL, + &stStartupInfo, + &stProcessInformation)) { + + // Successfully created process + err = 0; + } + else { + err = GetLastError(); + } + } + + delete [] lpstrCmd; + } + else { + // Alloc of lpstrCmd failed + err = ERROR_NOT_ENOUGH_MEMORY; + } + + CloseHandle(hTokenPrimary); + } + else { + // DuplicateTokenEx failed + err = GetLastError(); + } + } + else { + // SetTokenInformation failed + err = GetLastError(); + } + } + + CloseHandle(hToken); + } + else { + // OpenThreadToken failed + err = GetLastError(); + } + + RevertToSelf(); + } + else { + // ImpersonateSelf failed + err = GetLastError(); + } + + return err; +} + +void +CArchDaemonWindows::doDaemonRunning(bool running) +{ + ARCH->lockMutex(m_serviceMutex); + if (running) { + m_serviceState = SERVICE_RUNNING; + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); + } + ARCH->unlockMutex(m_serviceMutex); +} + +UINT +CArchDaemonWindows::doGetDaemonQuitMessage() +{ + return m_quitMessage; +} + +void +CArchDaemonWindows::setStatus(DWORD state) +{ + setStatus(state, 0, 0); +} + +void +CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint) +{ + assert(s_daemon != NULL); + + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = state; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = 0; + status.dwCheckPoint = step; + status.dwWaitHint = waitHint; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::setStatusError(DWORD error) +{ + assert(s_daemon != NULL); + + SERVICE_STATUS status; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = SERVICE_STOPPED; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_PAUSE_CONTINUE | + SERVICE_ACCEPT_SHUTDOWN; + status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + status.dwServiceSpecificExitCode = error; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + SetServiceStatus(s_daemon->m_statusHandle, &status); +} + +void +CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) +{ + typedef std::vector ArgList; + typedef std::vector Arguments; + const char** argv = const_cast(argvIn); + + // create synchronization objects + m_serviceMutex = ARCH->newMutex(); + m_serviceCondVar = ARCH->newCondVar(); + + // register our service handler function + m_statusHandle = RegisterServiceCtrlHandler(argv[0], + &CArchDaemonWindows::serviceHandlerEntry); + if (m_statusHandle == 0) { + // cannot start as service + m_daemonResult = -1; + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); + return; + } + + // tell service control manager that we're starting + m_serviceState = SERVICE_START_PENDING; + setStatus(m_serviceState, 0, 10000); + + // if no arguments supplied then try getting them from the registry. + // the first argument doesn't count because it's the service name. + Arguments args; + ArgList myArgv; + if (argc <= 1) { + // read command line + std::string commandLine; + HKEY key = openNTServicesKey(); + key = CArchMiscWindows::openKey(key, argvIn[0]); + if (key != NULL) { + // save ImagePath for possible future use + m_imagePath = CArchMiscWindows::readValueExpandString(key, + _T("ImagePath")); + } + key = CArchMiscWindows::openKey(key, _T("Parameters")); + if (key != NULL) { + commandLine = CArchMiscWindows::readValueString(key, + _T("CommandLine")); + } + + // save commandLine for possible future use + m_commandLine = std::string(commandLine); + + // if the command line isn't empty then parse and use it + if (!commandLine.empty()) { + // parse, honoring double quoted substrings + std::string::size_type i = commandLine.find_first_not_of(" \t"); + while (i != std::string::npos && i != commandLine.size()) { + // find end of string + std::string::size_type e; + if (commandLine[i] == '\"') { + // quoted. find closing quote. + ++i; + e = commandLine.find("\"", i); + + // whitespace must follow closing quote + if (e == std::string::npos || + (e + 1 != commandLine.size() && + commandLine[e + 1] != ' ' && + commandLine[e + 1] != '\t')) { + args.clear(); + break; + } + + // extract + args.push_back(commandLine.substr(i, e - i)); + i = e + 1; + } + else { + // unquoted. find next whitespace. + e = commandLine.find_first_of(" \t", i); + if (e == std::string::npos) { + e = commandLine.size(); + } + + // extract + args.push_back(commandLine.substr(i, e - i)); + i = e + 1; + } + + // next argument + i = commandLine.find_first_not_of(" \t", i); + } + + // service name goes first + myArgv.push_back(argv[0]); + + // get pointers + for (size_t i = 0; i < args.size(); ++i) { + myArgv.push_back(args[i].c_str()); + } + + // adjust argc/argv + argc = myArgv.size(); + argv = &myArgv[0]; + } + } + + try { + // invoke daemon function + m_daemonResult = m_daemonFunc(static_cast(argc), argv); + } + catch (XArchDaemonRunFailed& e) { + setStatusError(e.m_result); + m_daemonResult = -1; + } + catch (...) { + setStatusError(1); + m_daemonResult = -1; + } + + // clean up + ARCH->closeCondVar(m_serviceCondVar); + ARCH->closeMutex(m_serviceMutex); +} + +void WINAPI +CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv) +{ + s_daemon->serviceMain(argc, argv); +} + +void +CArchDaemonWindows::serviceHandler(DWORD ctrl) +{ + assert(m_serviceMutex != NULL); + assert(m_serviceCondVar != NULL); + + ARCH->lockMutex(m_serviceMutex); + + // ignore request if service is already stopped + if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) { + if (s_daemon != NULL) { + setStatus(m_serviceState); + } + ARCH->unlockMutex(m_serviceMutex); + return; + } + + switch (ctrl) { + case SERVICE_CONTROL_PAUSE: + m_serviceState = SERVICE_PAUSE_PENDING; + setStatus(m_serviceState, 0, 5000); + PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0); + while (isRunState(m_serviceState)) { + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + break; + + case SERVICE_CONTROL_CONTINUE: + // FIXME -- maybe should flush quit messages from queue + m_serviceState = SERVICE_CONTINUE_PENDING; + setStatus(m_serviceState, 0, 5000); + ARCH->broadcastCondVar(m_serviceCondVar); + break; + + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + m_serviceState = SERVICE_STOP_PENDING; + setStatus(m_serviceState, 0, 5000); + PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0); + ARCH->broadcastCondVar(m_serviceCondVar); + while (isRunState(m_serviceState)) { + ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); + } + break; + + default: + // unknown service command + // fall through + + case SERVICE_CONTROL_INTERROGATE: + setStatus(m_serviceState); + break; + } + + ARCH->unlockMutex(m_serviceMutex); +} + +void WINAPI +CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl) +{ + s_daemon->serviceHandler(ctrl); +} Index: 1.3.1/lib/arch/arch.dsp =================================================================== --- 1.3.1/lib/arch/arch.dsp (revision 897) +++ 1.3.1/lib/arch/arch.dsp (working copy) @@ -41,7 +41,7 @@ # PROP Intermediate_Dir "..\..\gen\build" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c -# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\build\arch.pdb" /FD /c +# ADD CPP /nologo /MT /W4 /GR /GX /O2 /I "..\common" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /D _WIN32_WINNT=0x0501 /Fd"..\..\gen\build\arch.pdb" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" @@ -65,7 +65,7 @@ # PROP Intermediate_Dir "..\..\gen\debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Fd"..\..\gen\debug\arch.pdb" /FD /GZ /c +# ADD CPP /nologo /MTd /W4 /Gm /GR /GX /ZI /Od /I "..\base" /I "..\mt" /I "..\common" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /D _WIN32_WINNT=0x0501 /Fd"..\..\gen\debug\arch.pdb" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG"