blob: 764899d8d7a786d7347b21d5c84c2a9d86dff470 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the ActiveQt framework of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** 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.
**
** BSD License Usage
** Alternatively, 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 The Qt Company Ltd 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 <qstringlist.h>
#include <qvector.h>
#include "qaxfactory.h"
#include <string.h>
#include <qt_windows.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 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<void **>(&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<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);
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: <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.
IGlobalOptions *globalOptions = nullptr;
if (SUCCEEDED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC_SERVER,
IID_IGlobalOptions, reinterpret_cast<void **>(&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;
}