From 70768189bae0f4d5cfb24b57347cf7207dfc5496 Mon Sep 17 00:00:00 2001 From: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> Date: Sat, 22 Oct 2022 17:56:27 +0300 Subject: [PATCH] Windows: implement FS::createShortcut Signed-off-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com> --- launcher/FileSystem.cpp | 145 +++++++++++++++++++++++++++++++------ launcher/FileSystem.h | 5 ++ launcher/ui/MainWindow.cpp | 88 +++++++--------------- 3 files changed, 154 insertions(+), 84 deletions(-) diff --git a/launcher/FileSystem.cpp b/launcher/FileSystem.cpp index 4a8f4bd3..90f0313f 100644 --- a/launcher/FileSystem.cpp +++ b/launcher/FileSystem.cpp @@ -49,6 +49,7 @@ #include "StringUtils.h" #if defined Q_OS_WIN32 +#define WIN32_LEAN_AND_MEAN #include #include #include @@ -339,12 +340,12 @@ QString getDesktopDir() } // Cross-platform Shortcut creation -bool createShortCut(QString location, QString dest, QStringList args, QString name, QString icon) +bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon) { #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) - location = PathCombine(location, name + ".desktop"); + destination = PathCombine(destination, name + ".desktop"); - QFile f(location); + QFile f(destination); f.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream stream(&f); @@ -356,10 +357,13 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na << "\n"; stream << "Type=Application" << "\n"; - stream << "TryExec=" << dest.toLocal8Bit() << "\n"; - stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n"; + stream << "TryExec=" << target.toLocal8Bit() << "\n"; + stream << "Exec=" << target.toLocal8Bit() << argstring.toLocal8Bit() << "\n"; stream << "Name=" << name.toLocal8Bit() << "\n"; - stream << "Icon=" << icon.toLocal8Bit() << "\n"; + if (!icon.isEmpty()) + { + stream << "Icon=" << icon.toLocal8Bit() << "\n"; + } stream.flush(); f.close(); @@ -368,24 +372,121 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na return true; #elif defined Q_OS_WIN - // TODO: Fix - // QFile file(PathCombine(location, name + ".lnk")); - // WCHAR *file_w; - // WCHAR *dest_w; - // WCHAR *args_w; - // file.fileName().toWCharArray(file_w); - // dest.toWCharArray(dest_w); + QFileInfo targetInfo(target); - // QString argStr; - // for (int i = 0; i < args.count(); i++) - // { - // argStr.append(args[i]); - // argStr.append(" "); - // } - // argStr.toWCharArray(args_w); + if (!targetInfo.exists()) + { + qWarning() << "Target file does not exist!"; + return false; + } - // return SUCCEEDED(CreateLink(file_w, dest_w, args_w)); - return false; + target = targetInfo.absoluteFilePath(); + + if (target.length() >= MAX_PATH) + { + qWarning() << "Target file path is too long!"; + return false; + } + + if (!icon.isEmpty() && icon.length() >= MAX_PATH) + { + qWarning() << "Icon path is too long!"; + return false; + } + + destination += ".lnk"; + + if (destination.length() >= MAX_PATH) + { + qWarning() << "Destination path is too long!"; + return false; + } + + QString argStr; + int argCount = args.count(); + for (int i = 0; i < argCount; i++) + { + if (args[i].contains(' ')) + { + argStr.append('"').append(args[i]).append('"'); + } + else + { + argStr.append(args[i]); + } + + if (i < argCount - 1) + { + argStr.append(" "); + } + } + + if (argStr.length() >= MAX_PATH) + { + qWarning() << "Arguments string is too long!"; + return false; + } + + WCHAR wsz[MAX_PATH]; + + // ...yes, you need to initialize the entire COM stack to make a shortcut in Windows + CoInitialize(nullptr); + + HRESULT hres; + IShellLink* psl; + + hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); + if (SUCCEEDED(hres)) + { + wmemset(wsz, 0, MAX_PATH); + target.toWCharArray(wsz); + psl->SetPath(wsz); + + wmemset(wsz, 0, MAX_PATH); + argStr.toWCharArray(wsz); + psl->SetArguments(wsz); + + wmemset(wsz, 0, MAX_PATH); + targetInfo.absolutePath().toWCharArray(wsz); + psl->SetWorkingDirectory(wsz); + + if (!icon.isEmpty()) + { + wmemset(wsz, 0, MAX_PATH); + icon.toWCharArray(wsz); + psl->SetIconLocation(wsz, 0); + } + + IPersistFile* ppf; + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); + if (SUCCEEDED(hres)) + { + wmemset(wsz, 0, MAX_PATH); + destination.toWCharArray(wsz); + hres = ppf->Save(wsz, TRUE); + if (FAILED(hres)) + { + qWarning() << "IPresistFile->Save() failed"; + qWarning() << "hres = " << hres; + } + ppf->Release(); + } + else + { + qWarning() << "Failed to query IPersistFile interface from IShellLink instance"; + qWarning() << "hres = " << hres; + } + psl->Release(); + } + else + { + qWarning() << "Failed to create IShellLink instance"; + qWarning() << "hres = " << hres; + } + + CoUninitialize(); + + return SUCCEEDED(hres); #else qWarning("Desktop Shortcuts not supported on your platform!"); return false; diff --git a/launcher/FileSystem.h b/launcher/FileSystem.h index b7e175fd..23cc575b 100644 --- a/launcher/FileSystem.h +++ b/launcher/FileSystem.h @@ -156,4 +156,9 @@ QString getDesktopDir(); // Overrides one folder with the contents of another, preserving items exclusive to the first folder // Equivalent to doing QDir::rename, but allowing for overrides bool overrideFolder(QString overwritten_path, QString override_path); + +/** + * Creates a shortcut to the specified target file at the specified destination path. + */ +bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon); } diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 419bb9bd..1ad9713a 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -39,6 +39,7 @@ #include "Application.h" #include "BuildConfig.h" +#include "FileSystem.h" #include "MainWindow.h" @@ -739,9 +740,9 @@ public: actionCreateInstanceShortcut = TranslatedAction(MainWindow); actionCreateInstanceShortcut->setObjectName(QStringLiteral("actionCreateInstanceShortcut")); - actionCreateInstanceShortcut.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Create shortcut")); + actionCreateInstanceShortcut.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Create Shortcut")); actionCreateInstanceShortcut.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Creates a shortcut on your desktop to launch the selected instance.")); - actionCreateInstanceShortcut->setShortcut(QKeySequence(tr("Ctrl+D"))); + //actionCreateInstanceShortcut->setShortcut(QKeySequence(tr("Ctrl+D"))); // TODO //actionCreateInstanceShortcut->setIcon(APPLICATION->getThemedIcon("copy")); // TODO all_actions.append(&actionCreateInstanceShortcut); @@ -793,7 +794,7 @@ public: } } - instanceToolBar->addAction(actionCreateInstanceShortcut); + instanceToolBar->addAction(actionCreateInstanceShortcut); // TODO find better position for this all_toolbars.append(&instanceToolBar); MainWindow->addToolBar(Qt::RightToolBarArea, instanceToolBar); @@ -2087,76 +2088,39 @@ void MainWindow::on_actionKillInstance_triggered() } } -#ifdef Q_OS_WIN -#define WIN32_LEAN_AND_MEAN -#include "windows.h" -#include "winnls.h" -#include "shobjidl.h" -#include "objbase.h" -#include "objidl.h" -#include "shlguid.h" -#endif - void MainWindow::on_actionCreateInstanceShortcut_triggered() { if (m_selectedInstance) { - auto desktopDir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); - if (desktopDir.isEmpty()) { + auto desktopPath = FS::getDesktopDir(); + if (desktopPath.isEmpty()) { // TODO come up with an alternative solution (open "save file" dialog) - QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop!")); + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop?!")); return; } -#if defined(Q_OS_WIN) - // Windows - WCHAR wsz[MAX_PATH]; - - // ...yes, you need to initialize the entire COM stack to make a shortcut in Windows. - // I hate it. - CoInitialize(nullptr); - - HRESULT hres; - IShellLink* psl; - - hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); - if (SUCCEEDED(hres)) - { - IPersistFile* ppf; - - QApplication::applicationFilePath().left(MAX_PATH - 1).toWCharArray(wsz); - psl->SetPath(wsz); - - wmemset(wsz, 0, MAX_PATH); - QStringLiteral("--launch %1").arg(m_selectedInstance->id()).left(MAX_PATH - 1).toWCharArray(wsz); - psl->SetArguments(wsz); - - // TODO set icon - - hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); - - if (SUCCEEDED(hres)) - { - wmemset(wsz, 0, MAX_PATH); - - desktopDir - .append('/') - .append(QStringLiteral("%1.lnk").arg(m_selectedInstance->name())) - .left(MAX_PATH - 1).toWCharArray(wsz); - - hres = ppf->Save(wsz, TRUE); - ppf->Release(); - } - psl->Release(); - } - - CoUninitialize(); -#elif defined(Q_OS_LINUX) - // Linux -#elif defined(Q_OS_MACOS) +#if defined(Q_OS_MACOS) // macOSX // TODO actually write this path QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on macOSX yet!")); +#else + QString iconPath; + +#if defined(Q_OS_WIN) + // TODO + // need to convert icon to ICO format and save it somewhere... + iconPath = ""; +#elif defined(Q_OS_UNIX) + iconPath = ""; // TODO get instance icon path +#endif + if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()), + QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() }, m_selectedInstance->name(), iconPath)) { + QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!")); + } + else + { + QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!")); + } #endif } }