/**************************************************************************** ** ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Solutions component. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qtservice.h" #include "qtservice_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(QTSERVICE_DEBUG) #include #endif typedef SERVICE_STATUS_HANDLE(WINAPI*PRegisterServiceCtrlHandler)(const wchar_t*,LPHANDLER_FUNCTION); static PRegisterServiceCtrlHandler pRegisterServiceCtrlHandler = 0; typedef BOOL(WINAPI*PSetServiceStatus)(SERVICE_STATUS_HANDLE,LPSERVICE_STATUS); static PSetServiceStatus pSetServiceStatus = 0; typedef BOOL(WINAPI*PChangeServiceConfig2)(SC_HANDLE,DWORD,LPVOID); static PChangeServiceConfig2 pChangeServiceConfig2 = 0; typedef BOOL(WINAPI*PCloseServiceHandle)(SC_HANDLE); static PCloseServiceHandle pCloseServiceHandle = 0; typedef SC_HANDLE(WINAPI*PCreateService)(SC_HANDLE,LPCTSTR,LPCTSTR,DWORD,DWORD,DWORD,DWORD,LPCTSTR,LPCTSTR,LPDWORD,LPCTSTR,LPCTSTR,LPCTSTR); static PCreateService pCreateService = 0; typedef SC_HANDLE(WINAPI*POpenSCManager)(LPCTSTR,LPCTSTR,DWORD); static POpenSCManager pOpenSCManager = 0; typedef BOOL(WINAPI*PDeleteService)(SC_HANDLE); static PDeleteService pDeleteService = 0; typedef SC_HANDLE(WINAPI*POpenService)(SC_HANDLE,LPCTSTR,DWORD); static POpenService pOpenService = 0; typedef BOOL(WINAPI*PQueryServiceStatus)(SC_HANDLE,LPSERVICE_STATUS); static PQueryServiceStatus pQueryServiceStatus = 0; typedef BOOL(WINAPI*PStartServiceCtrlDispatcher)(CONST SERVICE_TABLE_ENTRY*); static PStartServiceCtrlDispatcher pStartServiceCtrlDispatcher = 0; typedef BOOL(WINAPI*PStartService)(SC_HANDLE,DWORD,const wchar_t**); static PStartService pStartService = 0; typedef BOOL(WINAPI*PControlService)(SC_HANDLE,DWORD,LPSERVICE_STATUS); static PControlService pControlService = 0; typedef HANDLE(WINAPI*PDeregisterEventSource)(HANDLE); static PDeregisterEventSource pDeregisterEventSource = 0; typedef BOOL(WINAPI*PReportEvent)(HANDLE,WORD,WORD,DWORD,PSID,WORD,DWORD,LPCTSTR*,LPVOID); static PReportEvent pReportEvent = 0; typedef HANDLE(WINAPI*PRegisterEventSource)(LPCTSTR,LPCTSTR); static PRegisterEventSource pRegisterEventSource = 0; typedef DWORD(WINAPI*PRegisterServiceProcess)(DWORD,DWORD); static PRegisterServiceProcess pRegisterServiceProcess = 0; typedef BOOL(WINAPI*PQueryServiceConfig)(SC_HANDLE,LPQUERY_SERVICE_CONFIG,DWORD,LPDWORD); static PQueryServiceConfig pQueryServiceConfig = 0; typedef BOOL(WINAPI*PQueryServiceConfig2)(SC_HANDLE,DWORD,LPBYTE,DWORD,LPDWORD); static PQueryServiceConfig2 pQueryServiceConfig2 = 0; #define RESOLVE(name) p##name = (P##name)lib.resolve(#name); #define RESOLVEA(name) p##name = (P##name)lib.resolve(#name"A"); #define RESOLVEW(name) p##name = (P##name)lib.resolve(#name"W"); static bool winServiceInit() { if (!pOpenSCManager) { QLibrary lib("advapi32"); // only resolve unicode versions RESOLVEW(RegisterServiceCtrlHandler); RESOLVE(SetServiceStatus); RESOLVEW(ChangeServiceConfig2); RESOLVE(CloseServiceHandle); RESOLVEW(CreateService); RESOLVEW(OpenSCManager); RESOLVE(DeleteService); RESOLVEW(OpenService); RESOLVE(QueryServiceStatus); RESOLVEW(StartServiceCtrlDispatcher); RESOLVEW(StartService); // need only Ansi version RESOLVE(ControlService); RESOLVE(DeregisterEventSource); RESOLVEW(ReportEvent); RESOLVEW(RegisterEventSource); RESOLVEW(QueryServiceConfig); RESOLVEW(QueryServiceConfig2); } return pOpenSCManager != 0; } bool QtServiceController::isInstalled() const { Q_D(const QtServiceController); bool result = false; if (!winServiceInit()) return result; // Open the Service Control Manager SC_HANDLE hSCM = pOpenSCManager(0, 0, 0); if (hSCM) { // Try to open the service SC_HANDLE hService = pOpenService(hSCM, (wchar_t*)d->serviceName.utf16(), SERVICE_QUERY_CONFIG); if (hService) { result = true; pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } bool QtServiceController::isRunning() const { Q_D(const QtServiceController); bool result = false; if (!winServiceInit()) return result; // Open the Service Control Manager SC_HANDLE hSCM = pOpenSCManager(0, 0, 0); if (hSCM) { // Try to open the service SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_QUERY_STATUS); if (hService) { SERVICE_STATUS info; int res = pQueryServiceStatus(hService, &info); if (res) result = info.dwCurrentState != SERVICE_STOPPED; pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } QString QtServiceController::serviceFilePath() const { Q_D(const QtServiceController); QString result; if (!winServiceInit()) return result; // Open the Service Control Manager SC_HANDLE hSCM = pOpenSCManager(0, 0, 0); if (hSCM) { // Try to open the service SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_QUERY_CONFIG); if (hService) { DWORD sizeNeeded = 0; char data[8 * 1024]; if (pQueryServiceConfig(hService, (LPQUERY_SERVICE_CONFIG)data, 8 * 1024, &sizeNeeded)) { LPQUERY_SERVICE_CONFIG config = (LPQUERY_SERVICE_CONFIG)data; result = QString::fromUtf16((const ushort*)config->lpBinaryPathName); } pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } QString QtServiceController::serviceDescription() const { Q_D(const QtServiceController); QString result; if (!winServiceInit()) return result; // Open the Service Control Manager SC_HANDLE hSCM = pOpenSCManager(0, 0, 0); if (hSCM) { // Try to open the service SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_QUERY_CONFIG); if (hService) { DWORD dwBytesNeeded; char data[8 * 1024]; if (pQueryServiceConfig2( hService, SERVICE_CONFIG_DESCRIPTION, (unsigned char *)data, 8096, &dwBytesNeeded)) { LPSERVICE_DESCRIPTION desc = (LPSERVICE_DESCRIPTION)data; if (desc->lpDescription) result = QString::fromUtf16((const ushort*)desc->lpDescription); } pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } QtServiceController::StartupType QtServiceController::startupType() const { Q_D(const QtServiceController); StartupType result = ManualStartup; if (!winServiceInit()) return result; // Open the Service Control Manager SC_HANDLE hSCM = pOpenSCManager(0, 0, 0); if (hSCM) { // Try to open the service SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_QUERY_CONFIG); if (hService) { DWORD sizeNeeded = 0; char data[8 * 1024]; if (pQueryServiceConfig(hService, (QUERY_SERVICE_CONFIG *)data, 8 * 1024, &sizeNeeded)) { QUERY_SERVICE_CONFIG *config = (QUERY_SERVICE_CONFIG *)data; result = config->dwStartType == SERVICE_DEMAND_START ? ManualStartup : AutoStartup; } pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } bool QtServiceController::uninstall() { Q_D(QtServiceController); bool result = false; if (!winServiceInit()) return result; // Open the Service Control Manager SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); if (hSCM) { // Try to open the service SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), DELETE); if (hService) { if (pDeleteService(hService)) result = true; pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } bool QtServiceController::start(const QStringList &args) { Q_D(QtServiceController); bool result = false; if (!winServiceInit()) return result; // Open the Service Control Manager SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT); if (hSCM) { // Try to open the service SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_START); if (hService) { QVector argv(args.size()); for (int i = 0; i < args.size(); ++i) argv[i] = (const wchar_t*)args.at(i).utf16(); if (pStartService(hService, args.size(), argv.data())) result = true; pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } bool QtServiceController::stop() { Q_D(QtServiceController); bool result = false; if (!winServiceInit()) return result; SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT); if (hSCM) { SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_STOP|SERVICE_QUERY_STATUS); if (hService) { SERVICE_STATUS status; if (pControlService(hService, SERVICE_CONTROL_STOP, &status)) { bool stopped = status.dwCurrentState == SERVICE_STOPPED; int i = 0; while(!stopped && i < 10) { Sleep(200); if (!pQueryServiceStatus(hService, &status)) break; stopped = status.dwCurrentState == SERVICE_STOPPED; ++i; } result = stopped; } else { qErrnoWarning(GetLastError(), "stopping"); } pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } bool QtServiceController::pause() { Q_D(QtServiceController); bool result = false; if (!winServiceInit()) return result; SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT); if (hSCM) { SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_PAUSE_CONTINUE); if (hService) { SERVICE_STATUS status; if (pControlService(hService, SERVICE_CONTROL_PAUSE, &status)) result = true; pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } bool QtServiceController::resume() { Q_D(QtServiceController); bool result = false; if (!winServiceInit()) return result; SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT); if (hSCM) { SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_PAUSE_CONTINUE); if (hService) { SERVICE_STATUS status; if (pControlService(hService, SERVICE_CONTROL_CONTINUE, &status)) result = true; pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } bool QtServiceController::sendCommand(int code) { Q_D(QtServiceController); bool result = false; if (!winServiceInit()) return result; if (code < 0 || code > 127 || !isRunning()) return result; SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT); if (hSCM) { SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_USER_DEFINED_CONTROL); if (hService) { SERVICE_STATUS status; if (pControlService(hService, 128 + code, &status)) result = true; pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } #if defined(QTSERVICE_DEBUG) extern void qtServiceLogDebug(QtMsgType type, const char* msg); #endif void QtServiceBase::logMessage(const QString &message, MessageType type, int id, uint category, const QByteArray &data) { #if defined(QTSERVICE_DEBUG) QByteArray dbgMsg("[LOGGED "); switch (type) { case Error: dbgMsg += "Error] " ; break; case Warning: dbgMsg += "Warning] "; break; case Success: dbgMsg += "Success] "; break; case Information: //fall through default: dbgMsg += "Information] "; break; } dbgMsg += message.toAscii(); qtServiceLogDebug((QtMsgType)-1, dbgMsg.constData()); #endif Q_D(QtServiceBase); if (!winServiceInit()) return; WORD wType; switch (type) { case Error: wType = EVENTLOG_ERROR_TYPE; break; case Warning: wType = EVENTLOG_WARNING_TYPE; break; case Information: wType = EVENTLOG_INFORMATION_TYPE; break; default: wType = EVENTLOG_SUCCESS; break; } HANDLE h = pRegisterEventSource(0, (wchar_t *)d->controller.serviceName().utf16()); if (h) { const wchar_t *msg = (wchar_t*)message.utf16(); const char *bindata = data.size() ? data.constData() : 0; pReportEvent(h, wType, category, id, 0, 1, data.size(),(const wchar_t **)&msg, const_cast(bindata)); pDeregisterEventSource(h); } } class QtServiceControllerHandler : public QObject { Q_OBJECT public: QtServiceControllerHandler(QtServiceSysPrivate *sys); protected: void customEvent(QEvent *e); private: QtServiceSysPrivate *d_sys; }; class QtServiceSysPrivate { public: enum { QTSERVICE_STARTUP = 256 }; QtServiceSysPrivate(); void setStatus( DWORD dwState ); void setServiceFlags(QtServiceBase::ServiceFlags flags); DWORD serviceFlags(QtServiceBase::ServiceFlags flags) const; inline bool available() const; static void WINAPI serviceMain( DWORD dwArgc, wchar_t** lpszArgv ); static void WINAPI handler( DWORD dwOpcode ); SERVICE_STATUS status; SERVICE_STATUS_HANDLE serviceStatus; QStringList serviceArgs; static QtServiceSysPrivate *instance; static QCoreApplication::EventFilter nextFilter; QWaitCondition condition; QMutex mutex; QSemaphore startSemaphore; QSemaphore startSemaphore2; QtServiceControllerHandler *controllerHandler; void handleCustomEvent(QEvent *e); }; QtServiceControllerHandler::QtServiceControllerHandler(QtServiceSysPrivate *sys) : QObject(), d_sys(sys) { } void QtServiceControllerHandler::customEvent(QEvent *e) { d_sys->handleCustomEvent(e); } QtServiceSysPrivate *QtServiceSysPrivate::instance = 0; QCoreApplication::EventFilter QtServiceSysPrivate::nextFilter = 0; QtServiceSysPrivate::QtServiceSysPrivate() { instance = this; } inline bool QtServiceSysPrivate::available() const { return 0 != pOpenSCManager; } void WINAPI QtServiceSysPrivate::serviceMain(DWORD dwArgc, wchar_t** lpszArgv) { if (!instance || !QtServiceBase::instance()) return; // Windows spins off a random thread to call this function on // startup, so here we just signal to the QApplication event loop // in the main thread to go ahead with start()'ing the service. for (DWORD i = 0; i < dwArgc; i++) instance->serviceArgs.append(QString::fromUtf16((unsigned short*)lpszArgv[i])); instance->startSemaphore.release(); // let the qapp creation start instance->startSemaphore2.acquire(); // wait until its done // Register the control request handler instance->serviceStatus = pRegisterServiceCtrlHandler((TCHAR*)QtServiceBase::instance()->serviceName().utf16(), handler); if (!instance->serviceStatus) // cannot happen - something is utterly wrong return; handler(QTSERVICE_STARTUP); // Signal startup to the application - // causes QtServiceBase::start() to be called in the main thread // The MSDN doc says that this thread should just exit - the service is // running in the main thread (here, via callbacks in the handler thread). } // The handler() is called from the thread that called // StartServiceCtrlDispatcher, i.e. our HandlerThread, and // not from the main thread that runs the event loop, so we // have to post an event to ourselves, and use a QWaitCondition // and a QMutex to synchronize. void QtServiceSysPrivate::handleCustomEvent(QEvent *e) { int code = e->type() - QEvent::User; switch(code) { case QTSERVICE_STARTUP: // Startup QtServiceBase::instance()->start(); break; case SERVICE_CONTROL_STOP: QtServiceBase::instance()->stop(); QCoreApplication::instance()->quit(); break; case SERVICE_CONTROL_PAUSE: QtServiceBase::instance()->pause(); break; case SERVICE_CONTROL_CONTINUE: QtServiceBase::instance()->resume(); break; default: if (code >= 128 && code <= 255) QtServiceBase::instance()->processCommand(code - 128); break; } mutex.lock(); condition.wakeAll(); mutex.unlock(); } void WINAPI QtServiceSysPrivate::handler( DWORD code ) { if (!instance) return; instance->mutex.lock(); switch (code) { case QTSERVICE_STARTUP: // QtService startup (called from WinMain when started) instance->setStatus(SERVICE_START_PENDING); QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code))); instance->condition.wait(&instance->mutex); instance->setStatus(SERVICE_RUNNING); break; case SERVICE_CONTROL_STOP: // 1 instance->setStatus(SERVICE_STOP_PENDING); QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code))); instance->condition.wait(&instance->mutex); // status will be reported as stopped by start() when qapp::exec returns break; case SERVICE_CONTROL_PAUSE: // 2 instance->setStatus(SERVICE_PAUSE_PENDING); QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code))); instance->condition.wait(&instance->mutex); instance->setStatus(SERVICE_PAUSED); break; case SERVICE_CONTROL_CONTINUE: // 3 instance->setStatus(SERVICE_CONTINUE_PENDING); QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code))); instance->condition.wait(&instance->mutex); instance->setStatus(SERVICE_RUNNING); break; case SERVICE_CONTROL_INTERROGATE: // 4 break; case SERVICE_CONTROL_SHUTDOWN: // 5 // Don't waste time with reporting stop pending, just do it QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + SERVICE_CONTROL_STOP))); instance->condition.wait(&instance->mutex); // status will be reported as stopped by start() when qapp::exec returns break; default: if ( code >= 128 && code <= 255 ) { QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code))); instance->condition.wait(&instance->mutex); } break; } instance->mutex.unlock(); // Report current status if (instance->available() && instance->status.dwCurrentState != SERVICE_STOPPED) pSetServiceStatus(instance->serviceStatus, &instance->status); } void QtServiceSysPrivate::setStatus(DWORD state) { if (!available()) return; status.dwCurrentState = state; pSetServiceStatus(serviceStatus, &status); } void QtServiceSysPrivate::setServiceFlags(QtServiceBase::ServiceFlags flags) { if (!available()) return; status.dwControlsAccepted = serviceFlags(flags); pSetServiceStatus(serviceStatus, &status); } DWORD QtServiceSysPrivate::serviceFlags(QtServiceBase::ServiceFlags flags) const { DWORD control = 0; if (flags & QtServiceBase::CanBeSuspended) control |= SERVICE_ACCEPT_PAUSE_CONTINUE; if (!(flags & QtServiceBase::CannotBeStopped)) control |= SERVICE_ACCEPT_STOP; if (flags & QtServiceBase::NeedsStopOnShutdown) control |= SERVICE_ACCEPT_SHUTDOWN; return control; } #include "qtservice_win.moc" class HandlerThread : public QThread { public: HandlerThread() : success(true), console(false), QThread() {} bool calledOk() { return success; } bool runningAsConsole() { return console; } protected: bool success, console; void run() { SERVICE_TABLE_ENTRYW st [2]; st[0].lpServiceName = (wchar_t*)QtServiceBase::instance()->serviceName().utf16(); st[0].lpServiceProc = QtServiceSysPrivate::serviceMain; st[1].lpServiceName = 0; st[1].lpServiceProc = 0; success = (pStartServiceCtrlDispatcher(st) != 0); // should block if (!success) { if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { // Means we're started from console, not from service mgr // start() will ask the mgr to start another instance of us as a service instead console = true; } else { QtServiceBase::instance()->logMessage(QString("The Service failed to start [%1]").arg(qt_error_string(GetLastError())), QtServiceBase::Error); } QtServiceSysPrivate::instance->startSemaphore.release(); // let start() continue, since serviceMain won't be doing it } } }; /* Ignore WM_ENDSESSION system events, since they make the Qt kernel quit */ bool myEventFilter(void* message, long* result) { MSG* msg = reinterpret_cast(message); if (!msg || (msg->message != WM_ENDSESSION) || !(msg->lParam & ENDSESSION_LOGOFF)) return QtServiceSysPrivate::nextFilter ? QtServiceSysPrivate::nextFilter(message, result) : false; if (QtServiceSysPrivate::nextFilter) QtServiceSysPrivate::nextFilter(message, result); if (result) *result = TRUE; return true; } /* There are three ways we can be started: - By a service controller (e.g. the Services control panel), with no (service-specific) arguments. ServiceBase::exec() will then call start() below, and the service will start. - From the console, but with no (service-specific) arguments. This means we should ask a controller to start the service (i.e. another instance of this executable), and then just terminate. We discover this case (as different from the above) by the fact that StartServiceCtrlDispatcher will return an error, instead of blocking. - From the console, with -e(xec) argument. ServiceBase::exec() will then call ServiceBasePrivate::exec(), which calls ServiceBasePrivate::run(), which runs the application as a normal program. */ bool QtServiceBasePrivate::start() { sysInit(); if (!winServiceInit()) return false; // Since StartServiceCtrlDispatcher() blocks waiting for service // control events, we need to call it in another thread, so that // the main thread can run the QApplication event loop. HandlerThread* ht = new HandlerThread(); ht->start(); QtServiceSysPrivate* sys = QtServiceSysPrivate::instance; // Wait until service args have been received by serviceMain. // If Windows doesn't call serviceMain (or // StartServiceControlDispatcher doesn't return an error) within // a timeout of 20 secs, something is very wrong; give up if (!sys->startSemaphore.tryAcquire(1, 20000)) return false; if (!ht->calledOk()) { if (ht->runningAsConsole()) return controller.start(args.mid(1)); else return false; } int argc = sys->serviceArgs.size(); QVector argv(argc); QList argvData; for (int i = 0; i < argc; ++i) argvData.append(sys->serviceArgs.at(i).toLocal8Bit()); for (int i = 0; i < argc; ++i) argv[i] = argvData[i].data(); q_ptr->createApplication(argc, argv.data()); QCoreApplication *app = QCoreApplication::instance(); if (!app) return false; QtServiceSysPrivate::nextFilter = app->setEventFilter(myEventFilter); sys->controllerHandler = new QtServiceControllerHandler(sys); sys->startSemaphore2.release(); // let serviceMain continue (and end) sys->status.dwWin32ExitCode = q_ptr->executeApplication(); sys->setStatus(SERVICE_STOPPED); if (ht->isRunning()) ht->wait(1000); // let the handler thread finish delete sys->controllerHandler; sys->controllerHandler = 0; if (ht->isFinished()) delete ht; delete app; sysCleanup(); return true; } bool QtServiceBasePrivate::install(const QString &account, const QString &password) { bool result = false; if (!winServiceInit()) return result; // Open the Service Control Manager SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS); if (hSCM) { QString acc = account; DWORD dwStartType = startupType == QtServiceController::AutoStartup ? SERVICE_AUTO_START : SERVICE_DEMAND_START; DWORD dwServiceType = SERVICE_WIN32_OWN_PROCESS; wchar_t *act = 0; wchar_t *pwd = 0; if (!acc.isEmpty()) { // The act string must contain a string of the format "Domain\UserName", // so if only a username was specified without a domain, default to the local machine domain. if (!acc.contains(QChar('\\'))) { acc.prepend(QLatin1String(".\\")); } if (!acc.endsWith(QLatin1String("\\LocalSystem"))) act = (wchar_t*)acc.utf16(); } if (!password.isEmpty() && act) { pwd = (wchar_t*)password.utf16(); } // Only set INTERACTIVE if act is LocalSystem. (and act should be 0 if it is LocalSystem). if (!act) dwServiceType |= SERVICE_INTERACTIVE_PROCESS; // Create the service SC_HANDLE hService = pCreateService(hSCM, (wchar_t *)controller.serviceName().utf16(), (wchar_t *)controller.serviceName().utf16(), SERVICE_ALL_ACCESS, dwServiceType, // QObject::inherits ( const char * className ) for no inter active ???? dwStartType, SERVICE_ERROR_NORMAL, (wchar_t *)filePath().utf16(), 0, 0, 0, act, pwd); if (hService) { result = true; if (!serviceDescription.isEmpty()) { SERVICE_DESCRIPTION sdesc; sdesc.lpDescription = (wchar_t *)serviceDescription.utf16(); pChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sdesc); } pCloseServiceHandle(hService); } pCloseServiceHandle(hSCM); } return result; } QString QtServiceBasePrivate::filePath() const { wchar_t path[_MAX_PATH]; ::GetModuleFileNameW( 0, path, sizeof(path) ); return QString::fromUtf16((unsigned short*)path); } bool QtServiceBasePrivate::sysInit() { sysd = new QtServiceSysPrivate(); sysd->serviceStatus = 0; sysd->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS; sysd->status.dwCurrentState = SERVICE_STOPPED; sysd->status.dwControlsAccepted = sysd->serviceFlags(serviceFlags); sysd->status.dwWin32ExitCode = NO_ERROR; sysd->status.dwServiceSpecificExitCode = 0; sysd->status.dwCheckPoint = 0; sysd->status.dwWaitHint = 0; return true; } void QtServiceBasePrivate::sysSetPath() { } void QtServiceBasePrivate::sysCleanup() { if (sysd) { delete sysd; sysd = 0; } } void QtServiceBase::setServiceFlags(QtServiceBase::ServiceFlags flags) { if (d_ptr->serviceFlags == flags) return; d_ptr->serviceFlags = flags; if (d_ptr->sysd) d_ptr->sysd->setServiceFlags(flags); }