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
This commit is contained in:
Ray Molenkamp 2023-11-01 01:44:51 +01:00 committed by Ray molenkamp
parent 977c62c708
commit f6c52849b5
5 changed files with 152 additions and 0 deletions

View File

@ -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 */

View File

@ -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 <windows.h>
# undef NOMINMAX
#else
# include <windows.h>
#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

View File

@ -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

View File

@ -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];

View File

@ -0,0 +1,95 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#ifdef WIN32
# include <Windows.h>
# include <KnownFolders.h>
# include <filesystem>
# include <propkey.h>
# include <propvarutil.h>
# include <shlobj_core.h>
# include <wrl.h>
# 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<IShellLinkW> shell_link;
if (CoCreateInstance(__uuidof(ShellLink), NULL, CLSCTX_ALL, IID_PPV_ARGS(&shell_link)) != S_OK) {
return false;
}
Microsoft::WRL::ComPtr<IPersistFile> 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<IPropertyStore> 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