From 9cf77efaa06823d2403120701f5c591d5b151d59 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Wed, 24 May 2023 21:19:56 +0200 Subject: [PATCH] UI: Updated Windows File Registration Windows file associations using ProgID, needed because of the launcher. This fixes "pin to taskbar" and Recent Documents lists, allow per- version jump lists and an "Open with" list with multiple versions. Pull Request: https://projects.blender.org/blender/blender/pulls/107013 --- CMakeLists.txt | 9 + intern/ghost/intern/GHOST_SystemPathsWin32.cc | 19 +- intern/ghost/intern/GHOST_WindowWin32.cc | 61 ++++ intern/ghost/intern/GHOST_WindowWin32.hh | 4 + scripts/startup/bl_ui/space_userpref.py | 15 +- source/blender/blenlib/BLI_winstuff.h | 18 +- source/blender/blenlib/intern/winstuff.c | 321 +++++++++++++----- .../blenloader/intern/versioning_userdef.c | 2 +- .../editors/space_userpref/userpref_ops.c | 71 +++- source/blender/makesdna/DNA_userdef_types.h | 2 +- source/blender/makesrna/intern/rna_userdef.c | 29 ++ source/creator/creator_args.c | 74 +++- 12 files changed, 516 insertions(+), 109 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1861d941c20..4b38740e26d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,15 @@ endif() get_blender_version() +if(WIN32) + add_definitions( + # This is the app ID used for file registration, given it's used from several modules + # there really is no nice way to get this information consistent without a global define. + -DBLENDER_WIN_APPID="blender.${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}" + # This is the name that will be shown in the taskbar and OpenWith windows UI + -DBLENDER_WIN_APPID_FRIENDLY_NAME="Blender ${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}" + ) +endif() # ----------------------------------------------------------------------------- # Declare Options diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.cc b/intern/ghost/intern/GHOST_SystemPathsWin32.cc index 5ce121aa7a7..ab6284a025a 100644 --- a/intern/ghost/intern/GHOST_SystemPathsWin32.cc +++ b/intern/ghost/intern/GHOST_SystemPathsWin32.cc @@ -119,8 +119,23 @@ const char *GHOST_SystemPathsWin32::getBinaryDir() const void GHOST_SystemPathsWin32::addToSystemRecentFiles(const char *filepath) const { - /* SHARD_PATH resolves to SHARD_PATHA for non-UNICODE build */ UTF16_ENCODE(filepath); - SHAddToRecentDocs(SHARD_PATHW, filepath_16); + UTF16_ENCODE(BLENDER_WIN_APPID); + SHARDAPPIDINFO info; + IShellItem *shell_item; + + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if (!SUCCEEDED(hr)) + return; + + hr = SHCreateItemFromParsingName(filepath_16, NULL, IID_PPV_ARGS(&shell_item)); + if (SUCCEEDED(hr)) { + info.psi = shell_item; + info.pszAppID = BLENDER_WIN_APPID_16; + SHAddToRecentDocs(SHARD_APPIDINFO, &info); + } + + CoUninitialize(); + UTF16_UN_ENCODE(BLENDER_WIN_APPID); UTF16_UN_ENCODE(filepath); } diff --git a/intern/ghost/intern/GHOST_WindowWin32.cc b/intern/ghost/intern/GHOST_WindowWin32.cc index c0c26557f8b..d4054e2cbaf 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cc +++ b/intern/ghost/intern/GHOST_WindowWin32.cc @@ -23,6 +23,9 @@ #include #include +#include +#include +#include #include #include #include @@ -114,6 +117,8 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, return; } + registerWindowAppUserModelProperties(); + /* Store the device context. */ m_hDC = ::GetDC(m_hWnd); @@ -248,6 +253,10 @@ GHOST_TTrackpadInfo GHOST_WindowWin32::getTrackpadInfo() GHOST_WindowWin32::~GHOST_WindowWin32() { + if (m_hWnd) { + unregisterWindowAppUserModelProperties(); + } + if (m_Bar) { m_Bar->SetProgressState(m_hWnd, TBPF_NOPROGRESS); m_Bar->Release(); @@ -1192,3 +1201,55 @@ void GHOST_WindowWin32::endIME() m_imeInput.EndIME(m_hWnd); } #endif /* WITH_INPUT_IME */ + +void GHOST_WindowWin32::registerWindowAppUserModelProperties() +{ + IPropertyStore *pstore; + char blender_path[MAX_PATH]; + wchar_t shell_command[MAX_PATH]; + + /* Find the current executable, and see if it's blender.exe if not bail out. */ + GetModuleFileName(0, blender_path, sizeof(blender_path)); + char *blender_app = strstr(blender_path, "blender.exe"); + if (!blender_app) { + return; + } + + HRESULT hr = SHGetPropertyStoreForWindow(m_hWnd, IID_PPV_ARGS(&pstore)); + if (!SUCCEEDED(hr)) { + return; + } + + /* Set the launcher as the shell command so the console window will not flash. + * when people pin blender to the taskbar. */ + strcpy(blender_app, "blender-launcher.exe"); + wsprintfW(shell_command, L"\"%S\"", blender_path); + UTF16_ENCODE(BLENDER_WIN_APPID); + UTF16_ENCODE(BLENDER_WIN_APPID_FRIENDLY_NAME); + PROPVARIANT propvar; + hr = InitPropVariantFromString(BLENDER_WIN_APPID_16, &propvar); + hr = pstore->SetValue(PKEY_AppUserModel_ID, propvar); + hr = InitPropVariantFromString(shell_command, &propvar); + hr = pstore->SetValue(PKEY_AppUserModel_RelaunchCommand, propvar); + hr = InitPropVariantFromString(BLENDER_WIN_APPID_FRIENDLY_NAME_16, &propvar); + hr = pstore->SetValue(PKEY_AppUserModel_RelaunchDisplayNameResource, propvar); + pstore->Release(); + UTF16_UN_ENCODE(BLENDER_WIN_APPID_FRIENDLY_NAME); + UTF16_UN_ENCODE(BLENDER_WIN_APPID); +} + +/* as per MSDN: Any property not cleared before closing the window, will be leaked and NOT be + * returned to the OS. */ +void GHOST_WindowWin32::unregisterWindowAppUserModelProperties() +{ + IPropertyStore *pstore; + HRESULT hr = SHGetPropertyStoreForWindow(m_hWnd, IID_PPV_ARGS(&pstore)); + if (SUCCEEDED(hr)) { + PROPVARIANT value; + PropVariantInit(&value); + pstore->SetValue(PKEY_AppUserModel_ID, value); + pstore->SetValue(PKEY_AppUserModel_RelaunchCommand, value); + pstore->SetValue(PKEY_AppUserModel_RelaunchDisplayNameResource, value); + pstore->Release(); + } +} diff --git a/intern/ghost/intern/GHOST_WindowWin32.hh b/intern/ghost/intern/GHOST_WindowWin32.hh index cb6a11691d5..642d1d404d3 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.hh +++ b/intern/ghost/intern/GHOST_WindowWin32.hh @@ -363,6 +363,10 @@ class GHOST_WindowWin32 : public GHOST_Window { int hotY, bool canInvertColor); + /* Registration of the AppModel Properties that govern the taskbar button and jump lists. */ + void registerWindowAppUserModelProperties(); + void unregisterWindowAppUserModelProperties(); + /** Pointer to system. */ GHOST_SystemWin32 *m_system; /** Pointer to COM #IDropTarget implementer. */ diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index fb8fe32fdbd..0bd6a099cb7 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -638,11 +638,16 @@ class USERPREF_PT_system_os_settings(SystemPanel, CenterAlignMixIn, Panel): return sys.platform[:3] == "win" def draw_centered(self, _context, layout): - layout.label(text="Make this installation your default Blender") - split = layout.split(factor=0.4) - split.alignment = 'RIGHT' - split.label(text="") - split.operator("preferences.associate_blend", text="Make Default") + if _context.preferences.system.is_microsoft_store_install: + layout.label(text="Microsoft Store installation.") + layout.label(text="Use Windows 'Default Apps' to associate with blend files.") + else: + layout.label(text="Open blend files with this Blender version") + split = layout.split(factor=0.5) + split.alignment = 'LEFT' + split.operator("preferences.associate_blend", text="Register") + split.operator("preferences.unassociate_blend", text="Unregister") + layout.prop(bpy.context.preferences.system, "register_all_users", text="For All Users") class USERPREF_PT_system_memory(SystemPanel, CenterAlignMixIn, Panel): diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index 976c27e2018..e75e2458c00 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -85,7 +85,10 @@ const char *dirname(char *path); /* Windows utility functions. */ -bool BLI_windows_register_blend_extension(bool background); +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); + /** * Set the `root_dir` to the default root directory on MS-Windows, * The string is guaranteed to be set with a length of 3 & null terminated, @@ -99,6 +102,19 @@ int BLI_windows_get_executable_dir(char *str); bool BLI_windows_external_operation_supported(const char *filepath, const char *operation); bool BLI_windows_external_operation_execute(const char *filepath, const char *operation); +/** + * Launch our own executable. + * + * \param parameters: application parameters separated by spaces. + * \param wait: whether to wait for the instance to exit. + * \param elevated: run as administrator. Will do UAC prompt. + * \param silent: Not show the launched program. + */ +bool BLI_windows_execute_self(const char *parameters, + const bool wait, + const bool elevated, + const bool silent); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index 0e006c8493d..055769aeab7 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -44,136 +44,255 @@ int BLI_windows_get_executable_dir(char *str) return 1; } -static void register_blend_extension_failed(HKEY root, const bool background) +bool BLI_windows_is_store_install(void) { + char install_dir[FILE_MAXDIR]; + BLI_windows_get_executable_dir(install_dir); + return (BLI_strcasestr(install_dir, "\\WindowsApps\\") != NULL); +} + +static void registry_error(HKEY root, const char *message) { - printf("failed\n"); if (root) { RegCloseKey(root); } - if (!background) { - MessageBox(0, "Could not register file extension.", "Blender error", MB_OK | MB_ICONERROR); - } + fprintf(stderr, "%s\n", message); } -bool BLI_windows_register_blend_extension(const bool background) +static bool open_registry_hive(bool all_users, HKEY *r_root) +{ + if (RegOpenKeyEx(all_users ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, + "Software\\Classes", + 0, + KEY_ALL_ACCESS, + r_root) != ERROR_SUCCESS) + { + registry_error(*r_root, "Unable to open the registry with the required permissions"); + return false; + } + return true; +} + +static bool register_blender_prog_id(const char *prog_id, + const char *executable, + const char *friendly_name, + bool all_users) { LONG lresult; - HKEY hkey = 0; HKEY root = 0; - BOOL usr_mode = false; - DWORD dwd = 0; + HKEY hkey_progid = 0; char buffer[256]; + DWORD dwd = 0; - char BlPath[MAX_PATH]; - char MBox[256]; - - printf("Registering file extension..."); - GetModuleFileName(0, BlPath, MAX_PATH); - - /* Replace the actual app name with the wrapper. */ - { - char *blender_app = strstr(BlPath, "blender.exe"); - if (blender_app != NULL) { - strcpy(blender_app, "blender-launcher.exe"); - } - } - - /* root is HKLM by default */ - lresult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Classes", 0, KEY_ALL_ACCESS, &root); - if (lresult != ERROR_SUCCESS) { - /* try HKCU on failure */ - usr_mode = true; - lresult = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Classes", 0, KEY_ALL_ACCESS, &root); - if (lresult != ERROR_SUCCESS) { - register_blend_extension_failed(0, background); - return false; - } + if (!open_registry_hive(all_users, &root)) { + return false; } lresult = RegCreateKeyEx( - root, "blendfile", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &dwd); + root, prog_id, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey_progid, &dwd); + if (lresult == ERROR_SUCCESS) { - strcpy(buffer, "Blender File"); - lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1); - RegCloseKey(hkey); + lresult = RegSetValueEx( + hkey_progid, NULL, 0, REG_SZ, (BYTE *)friendly_name, strlen(friendly_name) + 1); + } + if (lresult == ERROR_SUCCESS) { + lresult = RegSetValueEx( + hkey_progid, "AppUserModelId", 0, REG_SZ, (BYTE *)prog_id, strlen(prog_id) + 1); } if (lresult != ERROR_SUCCESS) { - register_blend_extension_failed(root, background); + registry_error(root, "Unable to register Blender App Id"); return false; } - lresult = RegCreateKeyEx(root, - "blendfile\\shell\\open\\command", - 0, - NULL, - REG_OPTION_NON_VOLATILE, - KEY_ALL_ACCESS, - NULL, - &hkey, - &dwd); + SNPRINTF(buffer, "%s\\shell\\open", prog_id); + lresult = RegCreateKeyEx( + root, buffer, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey_progid, &dwd); + + lresult = RegSetValueEx( + hkey_progid, "FriendlyAppName", 0, REG_SZ, (BYTE *)friendly_name, strlen(friendly_name) + 1); + + SNPRINTF(buffer, "%s\\shell\\open\\command", prog_id); + + lresult = RegCreateKeyEx( + root, buffer, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey_progid, &dwd); + if (lresult == ERROR_SUCCESS) { - SNPRINTF(buffer, "\"%s\" \"%%1\"", BlPath); - lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1); - RegCloseKey(hkey); + SNPRINTF(buffer, "\"%s\" \"%%1\"", executable); + lresult = RegSetValueEx(hkey_progid, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1); + RegCloseKey(hkey_progid); } if (lresult != ERROR_SUCCESS) { - register_blend_extension_failed(root, background); + registry_error(root, "Unable to register Blender App Id"); return false; } - lresult = RegCreateKeyEx(root, - "blendfile\\DefaultIcon", - 0, - NULL, - REG_OPTION_NON_VOLATILE, - KEY_ALL_ACCESS, - NULL, - &hkey, - &dwd); + SNPRINTF(buffer, "%s\\DefaultIcon", prog_id); + lresult = RegCreateKeyEx( + root, buffer, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey_progid, &dwd); + if (lresult == ERROR_SUCCESS) { - SNPRINTF(buffer, "\"%s\", 1", BlPath); - lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1); - RegCloseKey(hkey); + SNPRINTF(buffer, "\"%s\", 1", executable); + lresult = RegSetValueEx(hkey_progid, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1); + RegCloseKey(hkey_progid); } if (lresult != ERROR_SUCCESS) { - register_blend_extension_failed(root, background); + registry_error(root, "Unable to register Blender App Id"); + return false; + } + return true; +} + +bool BLI_windows_register_blend_extension(const bool all_users) +{ + if (BLI_windows_is_store_install()) { + fprintf(stderr, "Registration not possible from Microsoft Store installation."); + return false; + } + + HKEY root = 0; + char blender_path[MAX_PATH]; + char *blender_app; + HKEY hkey = 0; + LONG lresult; + DWORD dwd = 0; + const char *prog_id = BLENDER_WIN_APPID; + const char *friendly_name = BLENDER_WIN_APPID_FRIENDLY_NAME; + + GetModuleFileName(0, blender_path, sizeof(blender_path)); + + /* Prevent overflow when we add -launcher to the executable name. */ + if (strlen(blender_path) > (sizeof(blender_path) - 10)) + return false; + + /* Replace the actual app name with the wrapper. */ + blender_app = strstr(blender_path, "blender.exe"); + if (!blender_app) { + return false; + } + strcpy(blender_app, "blender-launcher.exe"); + + if (!open_registry_hive(all_users, &root)) { + return false; + } + + if (!register_blender_prog_id(prog_id, blender_path, friendly_name, all_users)) { + registry_error(root, "Unable to register Blend document type"); return false; } lresult = RegCreateKeyEx( root, ".blend", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, &dwd); if (lresult == ERROR_SUCCESS) { - strcpy(buffer, "blendfile"); - lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1); + /* Set this instance the default. */ + lresult = RegSetValueEx(hkey, NULL, 0, REG_SZ, (BYTE *)prog_id, strlen(prog_id) + 1); + + if (lresult != ERROR_SUCCESS) { + registry_error(root, "Unable to register Blend document type"); + RegCloseKey(hkey); + return false; + } + RegCloseKey(hkey); + + lresult = RegCreateKeyEx(root, + ".blend\\OpenWithProgids", + 0, + NULL, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + NULL, + &hkey, + &dwd); + + if (lresult != ERROR_SUCCESS) { + registry_error(root, "Unable to register Blend document type"); + RegCloseKey(hkey); + return false; + } + lresult = RegSetValueEx(hkey, prog_id, 0, REG_NONE, NULL, 0); RegCloseKey(hkey); } + if (lresult != ERROR_SUCCESS) { - register_blend_extension_failed(root, background); + registry_error(root, "Unable to register Blend document type"); return false; } # ifdef WITH_BLENDER_THUMBNAILER { - char RegCmd[MAX_PATH * 2]; - char InstallDir[FILE_MAXDIR]; - char SysDir[FILE_MAXDIR]; - BLI_windows_get_executable_dir(InstallDir); - GetSystemDirectory(SysDir, FILE_MAXDIR); - const char *ThumbHandlerDLL = "BlendThumb.dll"; - SNPRINTF(RegCmd, "%s\\regsvr32 /s \"%s\\%s\"", SysDir, InstallDir, ThumbHandlerDLL); - system(RegCmd); + char reg_cmd[MAX_PATH * 2]; + char install_dir[FILE_MAXDIR]; + char system_dir[FILE_MAXDIR]; + BLI_windows_get_executable_dir(install_dir); + GetSystemDirectory(system_dir, sizeof(system_dir)); + const char *thumbnail_handler = "BlendThumb.dll"; + SNPRINTF(reg_cmd, "%s\\regsvr32 /s \"%s\\%s\"", system_dir, install_dir, thumbnail_handler); + system(reg_cmd); } # endif RegCloseKey(root); - printf("success (%s)\n", usr_mode ? "user" : "system"); - if (!background) { - SNPRINTF(MBox, - "File extension registered for %s.", - usr_mode ? "the current user. To register for all users, run as an administrator" : - "all users"); - MessageBox(0, MBox, "Blender", MB_OK | MB_ICONINFORMATION); + char message[256]; + SNPRINTF(message, + "Blend file extension registered for %s.", + all_users ? "all users" : "the current user"); + printf("%s\n", message); + + return true; +} + +bool BLI_windows_unregister_blend_extension(const bool all_users) +{ + if (BLI_windows_is_store_install()) { + fprintf(stderr, "Unregistration not possible from Microsoft Store installation."); + return false; } + + HKEY root = 0; + HKEY hkey = 0; + LONG lresult; + + if (!open_registry_hive(all_users, &root)) { + return false; + } + + /* Don't stop on failure. We want to allow unregister after unregister. */ + + RegDeleteTree(root, BLENDER_WIN_APPID); + + lresult = RegOpenKeyEx(root, ".blend", 0, KEY_ALL_ACCESS, &hkey); + if (lresult == ERROR_SUCCESS) { + char buffer[256] = {0}; + DWORD size = sizeof(buffer); + lresult = RegGetValueA(hkey, NULL, NULL, RRF_RT_REG_SZ, NULL, &buffer, &size); + if (lresult == ERROR_SUCCESS && STREQ(buffer, BLENDER_WIN_APPID)) { + RegSetValueEx(hkey, NULL, 0, REG_SZ, 0, 0); + } + } + +# ifdef WITH_BLENDER_THUMBNAILER + { + char reg_cmd[MAX_PATH * 2]; + char install_dir[FILE_MAXDIR]; + char system_dir[FILE_MAXDIR]; + BLI_windows_get_executable_dir(install_dir); + GetSystemDirectory(system_dir, sizeof(system_dir)); + const char *thumbnail_handler = "BlendThumb.dll"; + SNPRINTF(reg_cmd, "%s\\regsvr32 /u \"%s\\%s\"", system_dir, install_dir, thumbnail_handler); + system(reg_cmd); + } +# endif + + lresult = RegOpenKeyEx(hkey, "OpenWithProgids", 0, KEY_ALL_ACCESS, &hkey); + if (lresult == ERROR_SUCCESS) { + RegDeleteValue(hkey, BLENDER_WIN_APPID); + } + + RegCloseKey(root); + char message[256]; + SNPRINTF(message, + "Blend file extension unregistered for %s.", + all_users ? "all users" : "the current user"); + printf("%s\n", message); + return true; } @@ -234,6 +353,44 @@ bool BLI_windows_external_operation_execute(const char *filepath, const char *op return ShellExecuteExW(&shellinfo); } +bool BLI_windows_execute_self(const char *parameters, + const bool wait, + const bool elevated, + const bool silent) +{ + char blender_path[MAX_PATH]; + GetModuleFileName(0, blender_path, MAX_PATH); + + SHELLEXECUTEINFOA shellinfo = {0}; + shellinfo.cbSize = sizeof(SHELLEXECUTEINFO); + shellinfo.fMask = wait ? SEE_MASK_NOCLOSEPROCESS : SEE_MASK_DEFAULT; + shellinfo.hwnd = NULL; + shellinfo.lpVerb = elevated ? "runas" : NULL; + shellinfo.lpFile = blender_path; + shellinfo.lpParameters = parameters; + shellinfo.lpDirectory = NULL; + shellinfo.nShow = silent ? SW_HIDE : SW_SHOW; + shellinfo.hInstApp = NULL; + shellinfo.hProcess = 0; + + DWORD exitCode = 0; + if (!ShellExecuteExA(&shellinfo)) { + return false; + } + if (!wait) { + return true; + } + + if (shellinfo.hProcess != 0) { + WaitForSingleObject(shellinfo.hProcess, INFINITE); + GetExitCodeProcess(shellinfo.hProcess, &exitCode); + CloseHandle(shellinfo.hProcess); + return (exitCode == 0); + } + + return false; +} + void BLI_windows_get_default_root_dir(char root[4]) { char str[MAX_PATH + 1]; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index d59b990414f..7016fc537e7 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -540,7 +540,7 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->flag &= ~(USER_FLAG_UNUSED_4); - userdef->uiflag &= ~(USER_HEADER_FROM_PREF | USER_UIFLAG_UNUSED_12 | USER_UIFLAG_UNUSED_22); + userdef->uiflag &= ~(USER_HEADER_FROM_PREF | USER_UIFLAG_UNUSED_12 | USER_REGISTER_ALL_USERS); } if (!USER_VERSION_ATLEAST(280, 41)) { diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c index b863bf9044c..81bd09866ac 100644 --- a/source/blender/editors/space_userpref/userpref_ops.c +++ b/source/blender/editors/space_userpref/userpref_ops.c @@ -237,7 +237,11 @@ static void PREFERENCES_OT_asset_library_remove(wmOperatorType *ot) static bool associate_blend_poll(bContext *C) { #ifdef WIN32 - UNUSED_VARS(C); + if (BLI_windows_is_store_install()) + { + CTX_wm_operator_poll_msg_set(C, "Not available for Microsoft Store installations"); + return false; + } return true; #else CTX_wm_operator_poll_msg_set(C, "Windows-only operator"); @@ -248,8 +252,21 @@ static bool associate_blend_poll(bContext *C) static int associate_blend_exec(bContext *UNUSED(C), wmOperator *op) { #ifdef WIN32 + if (BLI_windows_is_store_install()) { + BKE_report(op->reports, RPT_ERROR, "Registration not possible from Microsoft Store installations"); + return OPERATOR_CANCELLED; + } + + const bool all_users = (U.uiflag & USER_REGISTER_ALL_USERS); + WM_cursor_wait(true); - if (BLI_windows_register_blend_extension(true)) { + + if (all_users && BLI_windows_execute_self("--register-allusers", true, true, true)) { + BKE_report(op->reports, RPT_INFO, "File association registered"); + WM_cursor_wait(false); + return OPERATOR_FINISHED; + } + else if (!all_users && BLI_windows_register_blend_extension(false)) { BKE_report(op->reports, RPT_INFO, "File association registered"); WM_cursor_wait(false); return OPERATOR_FINISHED; @@ -257,6 +274,7 @@ static int associate_blend_exec(bContext *UNUSED(C), wmOperator *op) else { BKE_report(op->reports, RPT_ERROR, "Unable to register file association"); WM_cursor_wait(false); + MessageBox(0, "Unable to register file association", "Blender", MB_OK | MB_ICONERROR); return OPERATOR_CANCELLED; } #else @@ -278,6 +296,54 @@ static void PREFERENCES_OT_associate_blend(struct wmOperatorType *ot) ot->poll = associate_blend_poll; } +static int unassociate_blend_exec(bContext *UNUSED(C), wmOperator *op) +{ +#ifdef WIN32 + if (BLI_windows_is_store_install()) { + BKE_report( + op->reports, RPT_ERROR, "Unregistration not possible from Microsoft Store installations"); + return OPERATOR_CANCELLED; + } + + const bool all_users = (U.uiflag & USER_REGISTER_ALL_USERS); + + WM_cursor_wait(true); + + if (all_users && BLI_windows_execute_self("--unregister-allusers", true, true, true)) { + BKE_report(op->reports, RPT_INFO, "File association unregistered"); + WM_cursor_wait(false); + return OPERATOR_FINISHED; + } + else if (!all_users && BLI_windows_unregister_blend_extension(false)) { + BKE_report(op->reports, RPT_INFO, "File association unregistered"); + WM_cursor_wait(false); + return OPERATOR_FINISHED; + } + else { + BKE_report(op->reports, RPT_ERROR, "Unable to unregister file association"); + WM_cursor_wait(false); + MessageBox(0, "Unable to unregister file association", "Blender", MB_OK | MB_ICONERROR); + return OPERATOR_CANCELLED; + } +#else + UNUSED_VARS(op); + BLI_assert_unreachable(); + return OPERATOR_CANCELLED; +#endif +} + +static void PREFERENCES_OT_unassociate_blend(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove File Association"; + ot->description = "Remove this installation's associations with .blend files"; + ot->idname = "PREFERENCES_OT_unassociate_blend"; + + /* api callbacks */ + ot->exec = unassociate_blend_exec; + ot->poll = associate_blend_poll; +} + /** \} */ void ED_operatortypes_userpref(void) @@ -291,4 +357,5 @@ void ED_operatortypes_userpref(void) WM_operatortype_append(PREFERENCES_OT_asset_library_remove); WM_operatortype_append(PREFERENCES_OT_associate_blend); + WM_operatortype_append(PREFERENCES_OT_unassociate_blend); } diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e01eceddec3..4e1f90a4667 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1143,7 +1143,7 @@ typedef enum eUserpref_UI_Flag { USER_UIFLAG_UNUSED_3 = (1 << 19), /* Cleared. */ USER_ZOOM_TO_MOUSEPOS = (1 << 20), USER_SHOW_FPS = (1 << 21), - USER_UIFLAG_UNUSED_22 = (1 << 22), /* cleared */ + USER_REGISTER_ALL_USERS = (1 << 22), USER_MENUFIXEDORDER = (1 << 23), USER_CONTINUOUS_MOUSE = (1 << 24), USER_ZOOM_INVERT = (1 << 25), diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 15b47b24dcc..465fd3c44ad 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -17,6 +17,9 @@ #include "BLI_math_base.h" #include "BLI_math_rotation.h" #include "BLI_utildefines.h" +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif #include "BLT_translation.h" @@ -665,6 +668,14 @@ static void rna_UserDef_viewport_lights_update(Main *bmain, Scene *scene, Pointe rna_userdef_update(bmain, scene, ptr); } +static bool rna_userdef_is_microsoft_store_install_get(PointerRNA *UNUSED(ptr)) +{ +# ifdef WIN32 + return BLI_windows_is_store_install(); +# endif + return false; +} + static void rna_userdef_autosave_update(Main *bmain, Scene *scene, PointerRNA *ptr) { wmWindowManager *wm = bmain->wm.first; @@ -5816,6 +5827,24 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_HIDDEN); RNA_def_property_ui_text(prop, "Legacy Compute Device Type", "For backwards compatibility only"); # endif + + /* Registration and Unregistration */ + + prop = RNA_def_property(srna, "register_all_users", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_REGISTER_ALL_USERS); + RNA_def_property_ui_text( + prop, + "Register for All Users", + "Make this Blender version open blend files for all users. Requires elevated privileges."); + + prop = RNA_def_boolean( + srna, + "is_microsoft_store_install", + false, + "Is Microsoft Store Install", + "Whether this blender installation is a sandboxed Microsoft Store version."); + RNA_def_property_boolean_funcs(prop, "rna_userdef_is_microsoft_store_install_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); } static void rna_def_userdef_input(BlenderRNA *brna) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 4d60f0515fc..a332657d195 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -627,8 +627,10 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo BLI_args_print_arg_doc(ba, "/?"); /* WIN32 only (ignored for non-win32) */ - BLI_args_print_arg_doc(ba, "-R"); - BLI_args_print_arg_doc(ba, "-r"); + BLI_args_print_arg_doc(ba, "--register"); + BLI_args_print_arg_doc(ba, "--register-allusers"); + BLI_args_print_arg_doc(ba, "--unregister"); + BLI_args_print_arg_doc(ba, "--unregister-allusers"); BLI_args_print_arg_doc(ba, "--version"); @@ -1399,20 +1401,60 @@ static int arg_handle_start_with_console(int UNUSED(argc), static const char arg_handle_register_extension_doc[] = "\n\t" - "Register blend-file extension, then exit (Windows only)."; -static const char arg_handle_register_extension_doc_silent[] = - "\n\t" - "Silently register blend-file extension, then exit (Windows only)."; -static int arg_handle_register_extension(int UNUSED(argc), const char **UNUSED(argv), void *data) + "Register blend-file extension for current user, then exit (Windows only)."; +static int arg_handle_register_extension(int UNUSED(argc), + const char **UNUSED(argv), + void *UNUSED(data)) { # ifdef WIN32 - if (data) { - G.background = 1; - } - BLI_windows_register_blend_extension(G.background); + G.background = 1; + BLI_windows_register_blend_extension(false); + TerminateProcess(GetCurrentProcess(), 0); +# endif + return 0; +} + +static const char arg_handle_register_extension_all_doc[] = + "\n\t" + "Register blend-file extension for all users, then exit (Windows only)."; +static int arg_handle_register_extension_all(int UNUSED(argc), + const char **UNUSED(argv), + void *UNUSED(data)) +{ +# ifdef WIN32 + G.background = 1; + BLI_windows_register_blend_extension(true); + TerminateProcess(GetCurrentProcess(), 0); +# endif + return 0; +} + +static const char arg_handle_unregister_extension_doc[] = + "\n\t" + "Unregister blend-file extension for current user, then exit (Windows only)."; +static int arg_handle_unregister_extension(int UNUSED(argc), + const char **UNUSED(argv), + void *UNUSED(data)) +{ +# ifdef WIN32 + G.background = 1; + BLI_windows_unregister_blend_extension(false); + TerminateProcess(GetCurrentProcess(), 0); +# endif + return 0; +} + +static const char arg_handle_unregister_extension_all_doc[] = + "\n\t" + "Unregister blend-file extension for all users, then exit (Windows only)."; +static int arg_handle_unregister_extension_all(int UNUSED(argc), + const char **UNUSED(argv), + void *UNUSED(data)) +{ +# ifdef WIN32 + G.background = 1; + BLI_windows_unregister_blend_extension(true); TerminateProcess(GetCurrentProcess(), 0); -# else - (void)data; /* unused */ # endif return 0; } @@ -2335,8 +2377,10 @@ void main_args_setup(bContext *C, bArgs *ba) BLI_args_add(ba, "-M", "--window-maximized", CB(arg_handle_window_maximized), NULL); BLI_args_add(ba, NULL, "--no-window-focus", CB(arg_handle_no_window_focus), NULL); BLI_args_add(ba, "-con", "--start-console", CB(arg_handle_start_with_console), NULL); - BLI_args_add(ba, "-R", NULL, CB(arg_handle_register_extension), NULL); - BLI_args_add(ba, "-r", NULL, CB_EX(arg_handle_register_extension, silent), ba); + BLI_args_add(ba, "-r", "--register", CB(arg_handle_register_extension), NULL); + BLI_args_add(ba, NULL, "--register-allusers", CB(arg_handle_register_extension_all), NULL); + BLI_args_add(ba, NULL, "--unregister", CB(arg_handle_unregister_extension), NULL); + BLI_args_add(ba, NULL, "--unregister-allusers", CB(arg_handle_unregister_extension_all), NULL); BLI_args_add(ba, NULL, "--no-native-pixels", CB(arg_handle_native_pixels_set), ba); /* Pass: Disabling Things & Forcing Settings. */