| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Windows main function 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$ |
| ** |
| ****************************************************************************/ |
| |
| /* |
| 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. |
| */ |
| |
| extern "C" { |
| int main(int, char **); |
| } |
| |
| #include <qbytearray.h> |
| #include <qstring.h> |
| #include <qdir.h> |
| #include <qstandardpaths.h> |
| #include <qfunctions_winrt.h> |
| #include <qcoreapplication.h> |
| #include <qmutex.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; |
| |
| const quint32 resizeMessageType = QtInfoMsg + 1; |
| |
| const PCWSTR shmemName = L"qdebug-shmem"; |
| const PCWSTR eventName = L"qdebug-event"; |
| const PCWSTR ackEventName = L"qdebug-event-ack"; |
| |
| static QtMessageHandler defaultMessageHandler; |
| static void devMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &message) |
| { |
| static HANDLE shmem = 0; |
| static HANDLE event = 0; |
| static HANDLE ackEvent = 0; |
| |
| static QMutex messageMutex; |
| QMutexLocker locker(&messageMutex); |
| |
| static quint64 mappingSize = 4096; |
| const quint32 copiedMessageLength = message.length() + 1; |
| // Message format is message type + message. We need the message's length + 4 bytes for the type |
| const quint64 copiedMessageSize = copiedMessageLength * sizeof(wchar_t) + sizeof(quint32); |
| if (copiedMessageSize > mappingSize) { |
| if (!shmem) |
| shmem = CreateFileMappingFromApp(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, mappingSize, shmemName); |
| Q_ASSERT_X(shmem, Q_FUNC_INFO, "Could not create file mapping"); |
| |
| quint32 *data = reinterpret_cast<quint32 *>(MapViewOfFileFromApp(shmem, FILE_MAP_WRITE, 0, mappingSize)); |
| Q_ASSERT_X(data, Q_FUNC_INFO, "Could not map size file"); |
| |
| mappingSize = copiedMessageSize; |
| |
| memcpy(data, (void *)&resizeMessageType, sizeof(quint32)); |
| memcpy(data + 1, (void *)&mappingSize, sizeof(quint64)); |
| UnmapViewOfFile(data); |
| SetEvent(event); |
| WaitForSingleObjectEx(ackEvent, INFINITE, false); |
| if (shmem) { |
| if (!CloseHandle(shmem)) |
| Q_ASSERT_X(false, Q_FUNC_INFO, "Could not close shared file handle"); |
| shmem = 0; |
| } |
| } |
| |
| if (!shmem) |
| shmem = CreateFileMappingFromApp(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, mappingSize, shmemName); |
| if (!event) |
| event = CreateEventEx(NULL, eventName, 0, EVENT_ALL_ACCESS); |
| if (!ackEvent) |
| ackEvent = CreateEventEx(NULL, ackEventName, 0, EVENT_ALL_ACCESS); |
| |
| Q_ASSERT_X(shmem, Q_FUNC_INFO, "Could not create file mapping"); |
| Q_ASSERT_X(event, Q_FUNC_INFO, "Could not create debug event"); |
| |
| void *data = MapViewOfFileFromApp(shmem, FILE_MAP_WRITE, 0, mappingSize); |
| Q_ASSERT_X(data, Q_FUNC_INFO, "Could not map file"); |
| |
| memset(data, quint32(type), sizeof(quint32)); |
| memcpy_s(static_cast<quint32 *>(data) + 1, mappingSize - sizeof(quint32), |
| message.data(), copiedMessageLength * sizeof(wchar_t)); |
| UnmapViewOfFile(data); |
| SetEvent(event); |
| WaitForSingleObjectEx(ackEvent, INFINITE, false); |
| locker.unlock(); |
| 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); |
| |
| hr = applicationFactory->CreateInstance(this, &base, &core); |
| RETURN_VOID_IF_FAILED("Failed to create application container instance"); |
| |
| pidFile = INVALID_HANDLE_VALUE; |
| } |
| |
| ~AppContainer() |
| { |
| } |
| |
| int exec() |
| { |
| mainThread = CreateThread(NULL, 0, [](void *param) -> DWORD { |
| AppContainer *app = reinterpret_cast<AppContainer *>(param); |
| int argc = app->args.count() - 1; |
| 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); |
| Q_ASSERT_X(mainThread, Q_FUNC_INFO, "Could not create Qt main thread"); |
| |
| 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 activatedLaunch(IInspectable *activateArgs) { |
| // Check if an application instance is already running |
| // This is mostly needed for Windows Phone and file pickers |
| QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher(); |
| if (dispatcher) { |
| QCoreApplication::postEvent(dispatcher, new QActivationEvent(activateArgs)); |
| return S_OK; |
| } |
| |
| QCoreApplication *app = QCoreApplication::instance(); |
| |
| // Check whether the app already runs |
| if (!app) { |
| // I*EventArgs have no launch arguments, hence we |
| // need to prepend the application binary manually |
| wchar_t fn[513]; |
| DWORD res = GetModuleFileName(0, fn, 512); |
| |
| if (SUCCEEDED(res)) |
| args.prepend(QString::fromWCharArray(fn, res).toUtf8().data()); |
| |
| ResumeThread(mainThread); |
| |
| // We give main() a max of 100ms to create an application object. |
| // No eventhandling needs to happen at that point, all we want is |
| // append our activation event |
| int iterations = 0; |
| while (true) { |
| app = QCoreApplication::instance(); |
| if (app || iterations++ > 10) |
| break; |
| Sleep(10); |
| } |
| } |
| |
| if (app) |
| QCoreApplication::postEvent(app, new QActivationEvent(activateArgs)); |
| return S_OK; |
| } |
| |
| HRESULT __stdcall OnActivated(IActivatedEventArgs *args) override |
| { |
| return activatedLaunch(args); |
| } |
| |
| HRESULT __stdcall OnLaunched(ILaunchActivatedEventArgs *launchArgs) override |
| { |
| ComPtr<IPrelaunchActivatedEventArgs> 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(); |
| |
| 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.isEmpty() && args.last() && args.last()[0] != '\0') |
| args.append(commandLine.data() + i + 1); |
| // fall through |
| default: |
| if (!args.isEmpty() && args.last() && args.last()[0] == '\0') |
| args.last() = commandLine.data() + i; |
| escape = false; // only quotes are escaped |
| break; |
| } |
| } |
| |
| if (args.count() >= 2 && args.at(1) && strncmp(args.at(1), "-ServerName:", 12) == 0) |
| args.remove(1); |
| |
| bool develMode = false; |
| bool debugWait = false; |
| for (int i = args.count() - 1; i >= 0; --i) { |
| if (!args.at(i)) |
| continue; |
| |
| const char *arg = args.at(i); |
| if (strcmp(arg, "-qdevel") == 0) { |
| develMode = true; |
| args.remove(i); |
| } else if (strcmp(arg, "-qdebug") == 0) { |
| debugWait = true; |
| args.remove(i); |
| } |
| } |
| args.append(nullptr); |
| |
| if (develMode) { |
| // Write a PID file to help runner |
| const QString pidFileName = QDir(QStandardPaths::writableLocation(QStandardPaths::DataLocation)) |
| .absoluteFilePath(QString::asprintf("%u.pid", uint(GetCurrentProcessId()))); |
| 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, ¶ms); |
| // Install the develMode message handler |
| defaultMessageHandler = qInstallMessageHandler(devMessageHandler); |
| } |
| // Wait for debugger before continuing |
| if (debugWait) { |
| while (!IsDebuggerPresent()) |
| WaitForSingleObjectEx(GetCurrentThread(), 1, true); |
| } |
| |
| ResumeThread(mainThread); |
| return S_OK; |
| } |
| |
| HRESULT __stdcall OnFileActivated(IFileActivatedEventArgs *args) override |
| { |
| return activatedLaunch(args); |
| } |
| |
| HRESULT __stdcall OnSearchActivated(ISearchActivatedEventArgs *args) override |
| { |
| Q_UNUSED(args); |
| return S_OK; |
| } |
| |
| HRESULT __stdcall OnShareTargetActivated(IShareTargetActivatedEventArgs *args) override |
| { |
| return activatedLaunch(args); |
| } |
| |
| HRESULT __stdcall OnFileOpenPickerActivated(IFileOpenPickerActivatedEventArgs *args) override |
| { |
| Q_UNUSED(args); |
| return S_OK; |
| } |
| |
| HRESULT __stdcall OnFileSavePickerActivated(IFileSavePickerActivatedEventArgs *args) override |
| { |
| Q_UNUSED(args); |
| return S_OK; |
| } |
| |
| HRESULT __stdcall OnCachedFileUpdaterActivated(ICachedFileUpdaterActivatedEventArgs *args) override |
| { |
| Q_UNUSED(args); |
| return S_OK; |
| } |
| |
| HRESULT __stdcall OnWindowCreated(Xaml::IWindowCreatedEventArgs *args) override |
| { |
| Q_UNUSED(args); |
| return S_OK; |
| } |
| |
| ComPtr<Xaml::IApplicationOverrides> base; |
| ComPtr<Xaml::IApplication> core; |
| QByteArray commandLine; |
| QVarLengthArray<char *> args; |
| HANDLE mainThread{0}; |
| HANDLE pidFile; |
| }; |
| |
| // Main entry point for Appx containers |
| int __stdcall WinMain(HINSTANCE, HINSTANCE, LPSTR, int) |
| { |
| if (FAILED(RoInitialize(RO_INIT_MULTITHREADED))) |
| return 1; |
| |
| ComPtr<AppContainer> app = Make<AppContainer>(); |
| return app->exec(); |
| } |