/**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the ActiveQt framework of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:COMM$ ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** $QT_END_LICENSE$ ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ****************************************************************************/ #include #include #include "qaxfactory.h" #include #include QT_BEGIN_NAMESPACE static DWORD *classRegistration = nullptr; static DWORD dwThreadID; static bool qAxActivity = false; static HANDLE hEventShutdown; #ifdef QT_DEBUG static const DWORD dwTimeOut = 1000; static const DWORD dwPause = 500; #else static const DWORD dwTimeOut = 5000; // time for EXE to be idle before shutting down static const DWORD dwPause = 1000; // time to wait for threads to finish up #endif extern HANDLE qAxInstance; extern bool qAxIsServer; extern bool qAxOutProcServer; extern wchar_t qAxModuleFilename[MAX_PATH]; extern QString qAxInit(); extern void qAxCleanup(); extern HRESULT UpdateRegistry(bool bRegister, bool perUser); extern HRESULT GetClassObject(const GUID &clsid, const GUID &iid, void **ppUnk); extern ulong qAxLockCount(); extern bool qax_winEventFilter(void *message); STDAPI DumpIDL(const QString &outfile, const QString &ver); // Monitors the shutdown event static DWORD WINAPI MonitorProc(void* /* pv */) { while (true) { WaitForSingleObject(hEventShutdown, INFINITE); DWORD dwWait=0; do { qAxActivity = false; dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut); } while (dwWait == WAIT_OBJECT_0); // timed out if (!qAxActivity && !qAxLockCount()) // if no activity let's really bail break; } CloseHandle(hEventShutdown); PostThreadMessage(dwThreadID, WM_QUIT, 0, 0); PostQuitMessage(0); return 0; } // Starts the monitoring thread static bool StartMonitor() { dwThreadID = GetCurrentThreadId(); hEventShutdown = CreateEvent(nullptr, false, false, nullptr); if (hEventShutdown == nullptr) return false; DWORD dwThreadID; HANDLE h = CreateThread(nullptr, 0, MonitorProc, nullptr, 0, &dwThreadID); return (h != nullptr); } void qax_shutDown() { qAxActivity = true; if (hEventShutdown) SetEvent(hEventShutdown); // tell monitor that we transitioned to zero } /* Start the COM server (if necessary). */ bool qax_startServer(QAxFactory::ServerType type) { if (qAxIsServer) return true; const QStringList keys = qAxFactory()->featureList(); const int keyCount = keys.count(); if (!keyCount) return false; if (!qAxFactory()->isService()) StartMonitor(); classRegistration = new DWORD[keyCount]; int object = 0; for (object = 0; object < keyCount; ++object) { IUnknown* p = nullptr; CLSID clsid = qAxFactory()->classID(keys.at(object)); // Create a QClassFactory (implemented in qaxserverbase.cpp) HRESULT hRes = GetClassObject(clsid, IID_IClassFactory, reinterpret_cast(&p)); if (SUCCEEDED(hRes)) hRes = CoRegisterClassObject(clsid, p, CLSCTX_LOCAL_SERVER, type == QAxFactory::MultipleInstances ? REGCLS_MULTIPLEUSE : REGCLS_SINGLEUSE, classRegistration+object); if (p) p->Release(); } qAxIsServer = true; return true; } /* Stop the COM server (if necessary). */ bool qax_stopServer() { if (!qAxIsServer || !classRegistration) return true; qAxIsServer = false; const int keyCount = qAxFactory()->featureList().size(); for (int object = 0; object < keyCount; ++object) CoRevokeClassObject(classRegistration[object]); delete []classRegistration; classRegistration = nullptr; Sleep(dwPause); //wait for any threads to finish return true; } QT_END_NAMESPACE #if defined(QT_NEEDS_QMAIN) int qMain(int, char **); #define main qMain #else extern "C" int main(int, char **); #endif static inline QStringList commandLineArguments() { QStringList result; int size; if (wchar_t **argv = CommandLineToArgvW(GetCommandLine(), &size)) { result.reserve(size); wchar_t **argvEnd = argv + size; for (wchar_t **a = argv; a < argvEnd; ++a) result.append(QString::fromWCharArray(*a)); LocalFree(argv); } return result; } static inline bool matchesOption(const QString &arg, const char *option) { return (arg.startsWith(QLatin1Char('/')) || arg.startsWith(QLatin1Char('-'))) && arg.rightRef(arg.size() - 1).compare(QLatin1String(option), Qt::CaseInsensitive) == 0; } namespace { struct Arg { explicit Arg(const QStringList &args) { argv.reserve(args.size() + 1); for (const QString &a : args) argv.append(_strdup(a.toLocal8Bit().constData())); argv.append(nullptr); } ~Arg() { for (int i = 0, last = argv.size() - 1; i < last; ++i) free(argv.at(i)); } QVector argv; }; } // namespace EXTERN_C int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, LPSTR, int /* nShowCmd */) { QT_USE_NAMESPACE qAxOutProcServer = true; GetModuleFileName(nullptr, qAxModuleFilename, MAX_PATH); qAxInstance = hInstance; const QStringList cmds = commandLineArguments(); QStringList unprocessed; unprocessed.reserve(cmds.size()); int nRet = 0; bool run = true; bool runServer = false; for (int i = 0; i < cmds.count(); ++i) { const QString &cmd = cmds.at(i); if (matchesOption(cmd, "activex") || matchesOption(cmd, "embedding")) { runServer = true; } else if (matchesOption(cmd, "unregserver")) { nRet = UpdateRegistry(false, false); run = false; break; } else if (matchesOption(cmd, "regserver")) { nRet = UpdateRegistry(true, false); run = false; break; } else if (matchesOption(cmd, "unregserverperuser")) { nRet = UpdateRegistry(false, true); run = false; break; } else if (matchesOption(cmd, "regserverperuser")) { nRet = UpdateRegistry(true, true); run = false; break; } else if (matchesOption(cmd, "dumpidl")) { ++i; if (i < cmds.count()) { const QString &outfile = cmds.at(i); ++i; QString version; if (i < cmds.count() && matchesOption(cmds.at(i), "version")) { ++i; if (i < cmds.count()) version = cmds.at(i); else version = QStringLiteral("1.0"); } nRet = DumpIDL(outfile, version); } else { qWarning("Wrong commandline syntax: -dumpidl [-version ]"); } run = false; break; } else { unprocessed.append(cmds.at(i)); } } if (run) { if (SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))) { #ifdef Q_CC_MINGW // define GlobalOptions class ID locally for MinGW, since it's missing from the distribution static const CLSID CLSID_GlobalOptions = { 0x0000034B, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; #endif // Disable C++ & SEH exception handling by the COM runtime for out-of-process COM controls. // Done to prevent silent crashes and enable crash dump generation. IGlobalOptions *globalOptions = nullptr; if (SUCCEEDED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER, IID_IGlobalOptions, reinterpret_cast(&globalOptions)))) { globalOptions->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE_ANY); globalOptions->Release(); globalOptions = nullptr; } { Arg args(unprocessed); qAxInit(); if (runServer) QAxFactory::startServer(); nRet = ::main(args.argv.size() - 1, args.argv.data()); QAxFactory::stopServer(); qAxCleanup(); } CoUninitialize(); } else { qErrnoWarning("CoInitializeEx() failed."); nRet = -1; } } return nRet; }