diff --git a/plugins/windows-notification/api/include/ComPtr.hpp b/plugins/windows-notification/api/include/ComPtr.hpp deleted file mode 100644 index 4850b34b..00000000 --- a/plugins/windows-notification/api/include/ComPtr.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -template -struct ComPtr -{ - T *p{}; - - ~ComPtr() { if (p != nullptr) p->Release(); } - - T &operator*() const { return *p; } - T **operator&() const { return &p; } - T **operator&() { return &p; } - T *operator->() const { return p; } - - template - HRESULT As( U **const pp ) const - { return p->QueryInterface(pp); } -}; \ No newline at end of file diff --git a/plugins/windows-notification/api/src/shortcutcreator.cpp b/plugins/windows-notification/api/src/shortcutcreator.cpp index cbbce8f4..e8c202a3 100644 --- a/plugins/windows-notification/api/src/shortcutcreator.cpp +++ b/plugins/windows-notification/api/src/shortcutcreator.cpp @@ -1,151 +1,107 @@ -#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 // At least one COM header must have been previously +// included, for `winrt::create_instance` to work with the `GUID` type. -#include "ComPtr.hpp" +#include -// Not available in MINGW headers for some reason -PSSTDAPI PropVariantToString(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); +namespace { -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); - } - } - } - } - } +#define checked(func, args) \ + if (const auto hr = ((func)args); FAILED(hr)) \ + { \ + g_warning("%s%s failed: hresult %#08" PRIX32, \ + #func, #args, static_cast(hr)); \ + winrt::throw_hresult(hr); \ } - return hr; -} -int32_t ValidateShortcut(const std::wstring& shortcut_path, const std::wstring& currentAumid) +struct property { - bool wasChanged = false; + property() noexcept : var{} {} - ComPtr shellLink; - auto hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - - if (SUCCEEDED(hr)) + explicit property(const std::wstring &value) { - 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); - } - } - } - } + checked(::InitPropVariantFromString,(value.c_str(), &var)); } - return hr; -} -static bool ImplEnsureAumiddedShortcutExists( - const std::string_view menu_rel_path, const std::string_view aumid) + ~property() + { + if (const auto hr = ::PropVariantClear(&var); FAILED(hr)) + g_critical("PropVariantClear failed: hresult %#08" PRIX32, + static_cast(hr)); + } + + auto str() const + { + wchar_t *str; + checked(::PropVariantToStringAlloc,(var, &str)); + return std::unique_ptr + + { str, &::CoTaskMemFree }; + } + + operator const PROPVARIANT &() const noexcept { return var; } + operator PROPVARIANT *() noexcept { return &var; } + +private: + PROPVARIANT var; +}; + +bool ImplEnsureAumiddedShortcutExists( + const std::string_view menu_rel_path, const std::string_view narrow_aumid) { - const auto waumid = sview_to_wstr(aumid); - if (waumid.empty()) + const auto aumid = sview_to_wstr(narrow_aumid); + if (aumid.empty()) { return false; } - const auto path = GetEnv(L"APPDATA") + LR"(\Microsoft\Windows\Start Menu\)" + const auto exe_path = GetExePath(); + const auto shortcut_path = GetEnv(L"APPDATA") + + LR"(\Microsoft\Windows\Start Menu\)" + sview_to_wstr(menu_rel_path) + L".lnk"; - if (!std::filesystem::exists(path)) + + const auto lnk = winrt::create_instance(CLSID_ShellLink); + const auto file = lnk.as(); + const auto store = lnk.as(); + + if (SUCCEEDED(file->Load(shortcut_path.c_str(), STGM_READWRITE))) { - return SUCCEEDED(InstallShortcut(GetExePath(), waumid, path)); + property aumid_prop; + checked(store->GetValue,(PKEY_AppUserModel_ID, aumid_prop)); + if (aumid_prop.str().get() != aumid) + checked(store->SetValue,(PKEY_AppUserModel_ID, property{aumid})); } else { - return SUCCEEDED(ValidateShortcut(path, waumid)); + checked(store->SetValue,(PKEY_AppUserModel_ID, property{aumid})); + checked(lnk->SetPath,(exe_path.c_str())); } + + checked(store->Commit,()); + + if (file->IsDirty() != S_FALSE) // not the same as `== S_OK` + { + constexpr auto set_file_as_current = TRUE; + checked(file->Save,(shortcut_path.c_str(), set_file_as_current)); + } + + return true; } +#undef checked + +} // nameless namespace + + extern "C" { gboolean EnsureAumiddedShortcutExists(const gchar *const aumid) noexcept