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
This commit is contained in:
Harley Acheson 2023-05-24 21:19:56 +02:00 committed by Harley Acheson
parent d168df7c23
commit 9cf77efaa0
12 changed files with 516 additions and 109 deletions

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

@ -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);
}

@ -23,6 +23,9 @@
#include <assert.h>
#include <math.h>
#include <propkey.h>
#include <propvarutil.h>
#include <shellapi.h>
#include <shellscalingapi.h>
#include <string.h>
#include <windowsx.h>
@ -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();
}
}

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

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

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

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

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

@ -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);
}

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

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

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