-
Maurice Kalinowski authored
We have to call Exit() to successfully close an application as done in 25dcc90d . Unfortunately Exit() always sets the exit code to 1 and this cannot be changed programmatically. Hence write the exit code into the pid file which is created when launched via winrtrunner. winrtrunner then fetches the content and passes the exit code to its callee. This implies that the pidFile is not deleted by the app itself anymore. Task-number: QTBUG-38654 Change-Id: Ib9b6ae4a0d61c9bf7e530e984aae3ad6204c39a0 Reviewed-by:
Oliver Wolff <oliver.wolff@theqtcompany.com>
0ea3d630
qtmain_winrt.cpp 12.02 KiB
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Windows main function of the Qt Toolkit.
**
** $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 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$
**
****************************************************************************/
/*
This file contains the code in the qtmain library for WinRT.
qtmain contains the WinRT startup code and is required for
linking to the Qt DLL.
When a Windows application starts, the WinMain function is
invoked. This WinMain creates the WinRT application
container, which in turn calls the application's main()
entry point within the newly created GUI thread.
*/
#if _MSC_VER < 1900
#include <new.h>
typedef struct
{
int newmode;
} _startupinfo;
#endif // _MSC_VER < 1900
extern "C" {
#if _MSC_VER < 1900
int __getmainargs(int *argc, char ***argv, char ***env, int expandWildcards, _startupinfo *info);
#endif
int main(int, char **);
}
#include <qbytearray.h>
#include <qstring.h>
#include <qdir.h>
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
#include <qstandardpaths.h>
#include <qfunctions_winrt.h>
#include <qcoreapplication.h>
#include <wrl.h>
#include <Windows.ApplicationModel.core.h>
#include <windows.ui.xaml.h>
#include <windows.ui.xaml.controls.h>
using namespace ABI::Windows::ApplicationModel;
using namespace ABI::Windows::ApplicationModel::Activation;
using namespace ABI::Windows::ApplicationModel::Core;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::UI;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
#define qHString(x) Wrappers::HString::MakeReference(x).Get()
#define CoreApplicationClass RuntimeClass_Windows_ApplicationModel_Core_CoreApplication
typedef ITypedEventHandler<CoreApplicationView *, Activation::IActivatedEventArgs *> ActivatedHandler;
static QtMessageHandler defaultMessageHandler;
static void devMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message)
{
#ifndef Q_OS_WINPHONE
static HANDLE shmem = 0;
static HANDLE event = 0;
if (!shmem)
shmem = CreateFileMappingFromApp(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 4096, L"qdebug-shmem");
if (!event)
event = CreateEventEx(NULL, L"qdebug-event", 0, EVENT_ALL_ACCESS);
void *data = MapViewOfFileFromApp(shmem, FILE_MAP_WRITE, 0, 4096);
memset(data, quint32(type), sizeof(quint32));
memcpy_s(static_cast<quint32 *>(data) + 1, 4096 - sizeof(quint32),
message.data(), (message.length() + 1) * sizeof(wchar_t));
UnmapViewOfFile(data);
SetEvent(event);
#endif // !Q_OS_WINPHONE
defaultMessageHandler(type, context, message);
}
class QActivationEvent : public QEvent
{
public:
explicit QActivationEvent(IInspectable *args)
: QEvent(QEvent::WinEventAct)
{
setAccepted(false);
args->AddRef();
d = reinterpret_cast<QEventPrivate *>(args);
}
~QActivationEvent() {
IUnknown *args = reinterpret_cast<IUnknown *>(d);
args->Release();
d = nullptr;
}
};
class AppContainer : public RuntimeClass<Xaml::IApplicationOverrides>
{
public:
AppContainer()
{
ComPtr<Xaml::IApplicationFactory> applicationFactory;
HRESULT hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Application).Get(),
IID_PPV_ARGS(&applicationFactory));
Q_ASSERT_SUCCEEDED(hr);
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
hr = applicationFactory->CreateInstance(this, &base, &core);
RETURN_VOID_IF_FAILED("Failed to create application container instance");
pidFile = INVALID_HANDLE_VALUE;
}
~AppContainer()
{
}
int exec(int argc, char **argv)
{
args.reserve(argc);
for (int i = 0; i < argc; ++i)
args.append(argv[i]);
mainThread = CreateThread(NULL, 0, [](void *param) -> DWORD {
AppContainer *app = reinterpret_cast<AppContainer *>(param);
int argc = app->args.count();
char **argv = app->args.data();
const int res = main(argc, argv);
if (app->pidFile != INVALID_HANDLE_VALUE) {
const QByteArray resString = QByteArray::number(res);
WriteFile(app->pidFile, reinterpret_cast<LPCVOID>(resString.constData()),
resString.size(), NULL, NULL);
FlushFileBuffers(app->pidFile);
CloseHandle(app->pidFile);
}
app->core->Exit();
return res;
}, this, CREATE_SUSPENDED, nullptr);
HRESULT hr;
ComPtr<Xaml::IApplicationStatics> appStatics;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Application).Get(),
IID_PPV_ARGS(&appStatics));
Q_ASSERT_SUCCEEDED(hr);
hr = appStatics->Start(Callback<Xaml::IApplicationInitializationCallback>([](Xaml::IApplicationInitializationCallbackParams *) {
return S_OK;
}).Get());
Q_ASSERT_SUCCEEDED(hr);
WaitForSingleObjectEx(mainThread, INFINITE, FALSE);
DWORD exitCode;
GetExitCodeThread(mainThread, &exitCode);
return exitCode;
}
private:
HRESULT __stdcall OnActivated(IActivatedEventArgs *args) Q_DECL_OVERRIDE
{
QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
if (dispatcher)
QCoreApplication::postEvent(dispatcher, new QActivationEvent(args));
return S_OK;
}
HRESULT __stdcall OnLaunched(ILaunchActivatedEventArgs *launchArgs) Q_DECL_OVERRIDE
{
#if _MSC_VER >= 1900
commandLine = QString::fromWCharArray(GetCommandLine()).toUtf8();
#endif
HString launchCommandLine;
launchArgs->get_Arguments(launchCommandLine.GetAddressOf());
if (launchCommandLine.IsValid()) {
quint32 launchCommandLineLength;
const wchar_t *launchCommandLineBuffer = launchCommandLine.GetRawBuffer(&launchCommandLineLength);
if (!commandLine.isEmpty() && launchCommandLineLength)
commandLine += ' ';
if (launchCommandLineLength)
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
commandLine += QString::fromWCharArray(launchCommandLineBuffer, launchCommandLineLength).toUtf8();
}
if (!commandLine.isEmpty())
args.append(commandLine.data());
bool quote = false;
bool escape = false;
for (int i = 0; i < commandLine.size(); ++i) {
switch (commandLine.at(i)) {
case '\\':
escape = true;
break;
case '"':
if (escape) {
escape = false;
break;
}
quote = !quote;
commandLine[i] = '\0';
break;
case ' ':
if (quote)
break;
commandLine[i] = '\0';
if (args.last()[0] != '\0')
args.append(commandLine.data() + i + 1);
// fall through
default:
if (args.last()[0] == '\0')
args.last() = commandLine.data() + i;
escape = false; // only quotes are escaped
break;
}
}
bool develMode = false;
bool debugWait = false;
foreach (const char *arg, args) {
if (strcmp(arg, "-qdevel") == 0)
develMode = true;
if (strcmp(arg, "-qdebug") == 0)
debugWait = true;
}
if (develMode) {
// Write a PID file to help runner
const QString pidFileName = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation))
.absoluteFilePath(QString::number(uint(GetCurrentProcessId())) + QStringLiteral(".pid"));
CREATEFILE2_EXTENDED_PARAMETERS params = {
sizeof(CREATEFILE2_EXTENDED_PARAMETERS),
FILE_ATTRIBUTE_NORMAL
};
pidFile = CreateFile2(reinterpret_cast<LPCWSTR>(pidFileName.utf16()),
GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, 0);
// Install the develMode message handler
#ifndef Q_OS_WINPHONE
defaultMessageHandler = qInstallMessageHandler(devMessageHandler);
#endif
}
// Wait for debugger before continuing
if (debugWait) {
while (!IsDebuggerPresent())
WaitForSingleObjectEx(GetCurrentThread(), 1, true);
}
ResumeThread(mainThread);
return S_OK;
}
HRESULT __stdcall OnFileActivated(IFileActivatedEventArgs *args) Q_DECL_OVERRIDE
{
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
Q_UNUSED(args);
return S_OK;
}
HRESULT __stdcall OnSearchActivated(ISearchActivatedEventArgs *args) Q_DECL_OVERRIDE
{
Q_UNUSED(args);
return S_OK;
}
HRESULT __stdcall OnShareTargetActivated(IShareTargetActivatedEventArgs *args) Q_DECL_OVERRIDE
{
Q_UNUSED(args);
return S_OK;
}
HRESULT __stdcall OnFileOpenPickerActivated(IFileOpenPickerActivatedEventArgs *args) Q_DECL_OVERRIDE
{
Q_UNUSED(args);
return S_OK;
}
HRESULT __stdcall OnFileSavePickerActivated(IFileSavePickerActivatedEventArgs *args) Q_DECL_OVERRIDE
{
Q_UNUSED(args);
return S_OK;
}
HRESULT __stdcall OnCachedFileUpdaterActivated(ICachedFileUpdaterActivatedEventArgs *args) Q_DECL_OVERRIDE
{
Q_UNUSED(args);
return S_OK;
}
HRESULT __stdcall OnWindowCreated(Xaml::IWindowCreatedEventArgs *args) Q_DECL_OVERRIDE
{
Q_UNUSED(args);
return S_OK;
}
ComPtr<Xaml::IApplicationOverrides> base;
ComPtr<Xaml::IApplication> core;
QByteArray commandLine;
QVarLengthArray<char *> args;
HANDLE mainThread;
HANDLE pidFile;
};
// Main entry point for Appx containers
int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
int argc = 0;
char **argv = 0, **env = 0;
#if _MSC_VER < 1900
_startupinfo info = { _query_new_mode() };
if (int init = __getmainargs(&argc, &argv, &env, false, &info))
return init;
#endif // _MSC_VER >= 1900
for (int i = 0; env && env[i]; ++i) {
QByteArray var(env[i]);
int split = var.indexOf('=');
if (split > 0)
qputenv(var.mid(0, split), var.mid(split + 1));
}
if (FAILED(RoInitialize(RO_INIT_MULTITHREADED)))
return 1;
ComPtr<AppContainer> app = Make<AppContainer>();
return app->exec(argc, argv);
351352
}