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:
parent
d168df7c23
commit
9cf77efaa0
@ -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. */
|
||||
|
Loading…
Reference in New Issue
Block a user