Revision control

Copy as Markdown

// Copyright (C) 2015 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
// Qt-Security score:significant reason:default
#include <qstringlist.h>
#include <qlist.h>
#include <private/qcoreapplication_p.h>
#include "qaxfactory.h"
#include <string.h>
#include <qt_windows.h>
#include <QtCore/private/qcomptr_p.h>
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 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 qsizetype keyCount = keys.size();
if (!keyCount)
return false;
if (!qAxFactory()->isService())
StartMonitor();
classRegistration = new DWORD[keyCount];
qsizetype object = 0;
for (object = 0; object < keyCount; ++object) {
ComPtr<IUnknown> p;
CLSID clsid = qAxFactory()->classID(keys.at(object));
// Create a QClassFactory (implemented in qaxserverbase.cpp)
HRESULT hRes = GetClassObject(clsid, IID_IClassFactory, &p);
if (SUCCEEDED(hRes))
hRes = CoRegisterClassObject(clsid, p.Get(), CLSCTX_LOCAL_SERVER,
type == QAxFactory::MultipleInstances ? REGCLS_MULTIPLEUSE : REGCLS_SINGLEUSE,
classRegistration+object);
}
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
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.right(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()
{
// Note: QGuiApplication may shift the array when removing Qt arguments
for (int i = 0, last = argv.size() - 1; i < last && argv.at(i) != nullptr; ++i)
free(argv.at(i));
}
QList<char *> argv;
};
} // namespace
EXTERN_C int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, LPSTR, int /* nShowCmd */)
{
QT_USE_NAMESPACE
qAxOutProcServer = true;
GetModuleFileName(nullptr, qAxModuleFilename, MAX_PATH);
QCoreApplicationPrivate::mainInstanceHandle = hInstance;
const QStringList cmds = commandLineArguments();
QStringList unprocessed;
unprocessed.reserve(cmds.size());
int nRet = 0;
bool run = true;
bool runServer = false;
for (qsizetype i = 0; i < cmds.size(); ++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.size()) {
const QString &outfile = cmds.at(i);
++i;
QString version;
if (i < cmds.size() && matchesOption(cmds.at(i), "version")) {
++i;
if (i < cmds.size())
version = cmds.at(i);
else
version = QStringLiteral("1.0");
}
nRet = DumpIDL(outfile, version);
} else {
qWarning("Wrong commandline syntax: <app> -dumpidl <idl file> [-version <x.y.z>]");
}
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.
ComPtr<IGlobalOptions> globalOptions;
if (SUCCEEDED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER,
IID_IGlobalOptions, &globalOptions))) {
globalOptions->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE_ANY);
}
{
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;
}