/**************************************************************************** ** ** 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 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 #include #include #include #include #include #include #include #include #include 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 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(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(args); } ~QActivationEvent() { IUnknown *args = reinterpret_cast(d); args->Release(); d = nullptr; } }; class AppContainer : public RuntimeClass { public: AppContainer() { ComPtr applicationFactory; HRESULT hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Application).Get(), IID_PPV_ARGS(&applicationFactory)); Q_ASSERT_SUCCEEDED(hr); 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(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(resString.constData()), resString.size(), NULL, NULL); FlushFileBuffers(app->pidFile); CloseHandle(app->pidFile); } app->core->Exit(); return res; }, this, CREATE_SUSPENDED, nullptr); HRESULT hr; ComPtr appStatics; hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Application).Get(), IID_PPV_ARGS(&appStatics)); Q_ASSERT_SUCCEEDED(hr); hr = appStatics->Start(Callback([](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 ComPtr preArgs; HRESULT hr = launchArgs->QueryInterface(preArgs.GetAddressOf()); if (SUCCEEDED(hr)) { boolean prelaunched; preArgs->get_PrelaunchActivated(&prelaunched); if (prelaunched) return S_OK; } 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) 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; } } if (args.count() >= 2 && strncmp(args.at(1), "-ServerName:", 12) == 0) args.remove(1); 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(pidFileName.utf16()), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, ¶ms); // 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 { 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 base; ComPtr core; QByteArray commandLine; QVarLengthArray 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 app = Make(); return app->exec(argc, argv); }