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