From f6c52849b502a865e68447a8dfc69a4955184e1f Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Wed, 1 Nov 2023 01:44:51 +0100 Subject: [PATCH] Fix #112729: Update pinned blender shortcut Windows allows people to pin an application to their taskbar, when a user pins blender, the data we set in `GHOST_WindowWin32::registerWindowAppUserModelProperties` is used which includes the path to the `blender-launcher.exe`. Now once that shortcut is created on the taskbar, this will never be updated, if people remove blender and install it again to a different path (happens often when using nightly builds) this leads to the situation where the shortcut on the taskbar points to a no longer existing blender installation. Now you may think, just un-pin and re-pin that should clear that right up! It doesn't, it'll keep using the outdated path till the end of time and there's no window API call we can do to update this information. However this shortcut is stored in the user profile in a sub-foder we can easily query, from there, we can iterate over all files, look for the one that has our appid in it, and when we find it, update the path to the blender launcher to the current installation, bit of a hack, but Microsoft seemingly offers no other way to deal with this problem. Pull Request: https://projects.blender.org/blender/blender/pulls/113859 --- source/blender/blenlib/BLI_winstuff.h | 2 + source/blender/blenlib/BLI_winstuff_com.hh | 48 ++++++++++ source/blender/blenlib/CMakeLists.txt | 2 + source/blender/blenlib/intern/winstuff.c | 5 + .../blenlib/intern/winstuff_registration.cc | 95 +++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 source/blender/blenlib/BLI_winstuff_com.hh create mode 100644 source/blender/blenlib/intern/winstuff_registration.cc diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index dabfe9b9dec..4df7607d18d 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -86,6 +86,8 @@ const char *dirname(char *path); bool BLI_windows_is_store_install(void); bool BLI_windows_register_blend_extension(bool all_users); bool BLI_windows_unregister_blend_extension(bool all_users); +bool BLI_windows_update_pinned_launcher(const char *launcher_path); + /* Gets the version of the currently loaded DirectX driver for the first device that matches * deviceString. This is required for Qualcomm devices which use Mesa's Gallium D2D12 layer for * OpenGL functionality */ diff --git a/source/blender/blenlib/BLI_winstuff_com.hh b/source/blender/blenlib/BLI_winstuff_com.hh new file mode 100644 index 00000000000..e10cdf1b1bf --- /dev/null +++ b/source/blender/blenlib/BLI_winstuff_com.hh @@ -0,0 +1,48 @@ +/* SPDX-FileCopyrightText: 2022 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * \brief COM helper functions for windows + */ + +#ifndef _WIN32 +# error "This include is for Windows only!" +#endif + +#include "BLI_sys_types.h" + +#define WIN32_LEAN_AND_MEAN + +#ifndef NOMINMAX +# define NOMINMAX +# include +# undef NOMINMAX +#else +# include +#endif + +namespace blender { +class CoInitializeWrapper { + HRESULT _hr; + + public: + CoInitializeWrapper(DWORD flags) + { + _hr = CoInitializeEx(nullptr, flags); + } + ~CoInitializeWrapper() + { + if (SUCCEEDED(_hr)) { + CoUninitialize(); + } + } + operator HRESULT() + { + return _hr; + } +}; +} // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 9a246fdb9b5..e8305077bdd 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -162,6 +162,7 @@ set(SRC intern/voxel.c intern/winstuff.c intern/winstuff_dir.c + intern/winstuff_registration.cc # Private headers. intern/BLI_mempool_private.h @@ -382,6 +383,7 @@ set(SRC BLI_voronoi_2d.h BLI_voxel.h BLI_winstuff.h + BLI_winstuff_com.hh PIL_time.h PIL_time_utildefines.h diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index c08721e633a..022502a51d9 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -221,6 +221,11 @@ bool BLI_windows_register_blend_extension(const bool all_users) return false; } + if (!BLI_windows_update_pinned_launcher(blender_path)) { + fprintf(stderr, "Update of pinned launcher failed."); + return false; + } + # ifdef WITH_BLENDER_THUMBNAILER { char reg_cmd[MAX_PATH * 2]; diff --git a/source/blender/blenlib/intern/winstuff_registration.cc b/source/blender/blenlib/intern/winstuff_registration.cc new file mode 100644 index 00000000000..de215ef50eb --- /dev/null +++ b/source/blender/blenlib/intern/winstuff_registration.cc @@ -0,0 +1,95 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef WIN32 +# include + +# include +# include +# include +# include +# include +# include + +# include "BLI_path_util.h" +# include "BLI_winstuff.h" +# include "BLI_winstuff_com.hh" + +# include "utf_winfunc.h" +# include "utfconv.h" + +/* Pinning : Windows allows people to pin an application to their taskbar, when a user pins + * blender, the data we set in `GHOST_WindowWin32::registerWindowAppUserModelProperties` is used + * which includes the path to the `blender-launcher.exe`. Now once that shortcut is created on + * the taskbar, this will never be updated, if people remove blender and install it again to a + * different path (happens often when using nightly builds) this leads to the situation where the + * shortcut on the taskbar points to a no longer existing blender installation. Now you may think, + * just un-pin and re-pin that should clear that right up! It doesn't, it'll keep using the + * outdated path till the end of time and there's no window API call we can do to update this + * information. However this shortcut is stored in the user profile in a sub-foder we can easily + * query, from there, we can iterate over all files, look for the one that has our appid in it, + * and when we find it, update the path to the blender launcher to the current installation, bit + * of a hack, but Microsoft seemingly offers no other way to deal with this problem. + * + * this function returns true when it had no issues executing, it is NOT indicative of any changes + * or updates being made + */ +bool BLI_windows_update_pinned_launcher(const char *launcher_path) +{ + WCHAR launcher_path_w[FILE_MAX]; + + if (conv_utf_8_to_16(launcher_path, launcher_path_w, ARRAY_SIZE(launcher_path_w)) != 0) { + return false; + } + + blender::CoInitializeWrapper initialize(COINIT_APARTMENTTHREADED); + if (FAILED(initialize)) { + return false; + } + + LPWSTR quick_launch_folder_path; + if (SHGetKnownFolderPath( + FOLDERID_ImplicitAppShortcuts, KF_FLAG_DEFAULT, NULL, &quick_launch_folder_path) != S_OK) + { + return false; + } + + std::wstring search_path = quick_launch_folder_path; + CoTaskMemFree(quick_launch_folder_path); + + Microsoft::WRL::ComPtr shell_link; + if (CoCreateInstance(__uuidof(ShellLink), NULL, CLSCTX_ALL, IID_PPV_ARGS(&shell_link)) != S_OK) { + return false; + } + + Microsoft::WRL::ComPtr persist_file; + if (shell_link.As(&persist_file) != S_OK) { + return false; + } + + for (auto const &dir_entry : std::filesystem::recursive_directory_iterator(search_path)) { + if (persist_file->Load(dir_entry.path().c_str(), STGM_READWRITE) != S_OK) { + continue; + } + + Microsoft::WRL::ComPtr property_store; + if (shell_link.As(&property_store) != S_OK) { + continue; + } + + UTF16_ENCODE(BLENDER_WIN_APPID); + PROPVARIANT app_model; + PropVariantInit(&app_model); + if (property_store->GetValue(PKEY_AppUserModel_ID, &app_model) == S_OK) { + if (std::wstring(BLENDER_WIN_APPID_16) == app_model.bstrVal) { + shell_link->SetPath(launcher_path_w); + persist_file->Save(NULL, TRUE); + } + } + PropVariantClear(&app_model); + UTF16_UN_ENCODE(BLENDER_WIN_APPID); + } + return true; +} +#endif