#include #include #include "shortcutcreator.h" #include "win32.hpp" #include "converter.hpp" #include "ginvoke.hpp" #ifdef UNICODE #define _UNICODE #endif #include // magic #include // COM stuff #include // IShellLink #include // InitPropVariantFromString #include // PKEY_AppUserModel_ID #include // 'cause iostreams are bloat #include #include "ComPtr.hpp" // Not available in MINGW headers for some reason PSSTDAPI PropVariantToString(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); int32_t InstallShortcut(const std::wstring& exe_path, const std::wstring& aumid, const std::wstring& shortcut_path) { ComPtr shellLink; auto hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); if (SUCCEEDED(hr)) { hr = shellLink->SetPath(exe_path.c_str()); if (SUCCEEDED(hr)) { hr = shellLink->SetArguments(TEXT("")); if (SUCCEEDED(hr)) { hr = shellLink->SetWorkingDirectory(exe_path.c_str()); if (SUCCEEDED(hr)) { ComPtr propertyStore; hr = shellLink.As(&propertyStore); if (SUCCEEDED(hr)) { PROPVARIANT appIdPropVar; hr = InitPropVariantFromString(aumid.c_str(), &appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->Commit(); if (SUCCEEDED(hr)) { ComPtr persistFile; hr = shellLink.As(&persistFile); if (SUCCEEDED(hr)) { hr = persistFile->Save(shortcut_path.c_str(), TRUE); } } } PropVariantClear(&appIdPropVar); } } } } } } return hr; } int32_t ValidateShortcut(const std::wstring& shortcut_path, const std::wstring& currentAumid) { bool wasChanged = false; ComPtr shellLink; auto hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); if (SUCCEEDED(hr)) { ComPtr persistFile; hr = shellLink.As(&persistFile); if (SUCCEEDED(hr)) { hr = persistFile->Load(shortcut_path.c_str(), STGM_READWRITE); if (SUCCEEDED(hr)) { ComPtr propertyStore; hr = shellLink.As(&propertyStore); if (SUCCEEDED(hr)) { PROPVARIANT appIdPropVar; hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); if (SUCCEEDED(hr)) { std::array AUMI; hr = PropVariantToString(appIdPropVar, AUMI.data(), AUMI.size()); if (FAILED(hr) || currentAumid != std::wstring(AUMI.data())) { // AUMI Changed for the same app, let's update the current value! =) wasChanged = true; PropVariantClear(&appIdPropVar); hr = InitPropVariantFromString(currentAumid.c_str(), &appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); if (SUCCEEDED(hr)) { hr = propertyStore->Commit(); if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { hr = persistFile->Save(shortcut_path.c_str(), TRUE); } } } } PropVariantClear(&appIdPropVar); } } } } } return hr; } bool ImplEnsureAumiddedShortcutExists(const char *const aumid) { auto waumid = sview_to_wstr(aumid); if (waumid.empty()) { return false; } auto exePath = GetCurrentModulePath(); auto shortcutPath = GetShortcutPath(); auto path = shortcutPath + LR"(\Microsoft\Windows\Start Menu\Programs\Dino.lnk)"; if (!std::filesystem::exists(path)) { return SUCCEEDED(InstallShortcut(exePath, waumid, path)); } else { return SUCCEEDED(ValidateShortcut(path, waumid)); } } extern "C" { gboolean EnsureAumiddedShortcutExists(const gchar* aumid) noexcept { return g_try_invoke(ImplEnsureAumiddedShortcutExists, aumid); } }