A few headers like BLI_math_constants.h and BLI_utildefines.h keep working for C code, for remaining makesdna and userdef defaults code in C. Pull Request: https://projects.blender.org/blender/blender/pulls/134406
523 lines
15 KiB
C++
523 lines
15 KiB
C++
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bli
|
|
* WIN32-POSIX compatibility layer, MS-Windows-specific functions.
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
|
|
# include <conio.h>
|
|
# include <shlwapi.h>
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
# define COBJMACROS /* Remove this when converting to C++ */
|
|
# include <dxgi.h>
|
|
|
|
# include "MEM_guardedalloc.h"
|
|
|
|
# define WIN32_SKIP_HKEY_PROTECTION /* Need to use HKEY. */
|
|
# include "BLI_fileops.h"
|
|
# include "BLI_path_utils.hh"
|
|
# include "BLI_string.h"
|
|
# include "BLI_utildefines.h"
|
|
# include "BLI_winstuff.h"
|
|
|
|
# include "utf_winfunc.hh"
|
|
# include "utfconv.hh"
|
|
|
|
/* FILE_MAXDIR + FILE_MAXFILE */
|
|
|
|
int BLI_windows_get_executable_dir(char r_dirpath[/*FILE_MAXDIR*/])
|
|
{
|
|
char filepath[FILE_MAX];
|
|
char dir[FILE_MAX];
|
|
int a;
|
|
/* Change to utf support. */
|
|
GetModuleFileName(nullptr, filepath, sizeof(filepath));
|
|
BLI_path_split_dir_part(filepath, dir, sizeof(dir)); /* shouldn't be relative */
|
|
a = strlen(dir);
|
|
if (dir[a - 1] == '\\') {
|
|
dir[a - 1] = 0;
|
|
}
|
|
|
|
BLI_strncpy(r_dirpath, dir, FILE_MAXDIR);
|
|
|
|
return 1;
|
|
}
|
|
|
|
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\\") != nullptr);
|
|
}
|
|
|
|
static void registry_error(HKEY root, const char *message)
|
|
{
|
|
if (root) {
|
|
RegCloseKey(root);
|
|
}
|
|
fprintf(stderr, "%s\n", message);
|
|
}
|
|
|
|
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 root = 0;
|
|
HKEY hkey_progid = 0;
|
|
char buffer[256];
|
|
DWORD dwd = 0;
|
|
|
|
if (!open_registry_hive(all_users, &root)) {
|
|
return false;
|
|
}
|
|
|
|
lresult = RegCreateKeyEx(root,
|
|
prog_id,
|
|
0,
|
|
nullptr,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
nullptr,
|
|
&hkey_progid,
|
|
&dwd);
|
|
|
|
if (lresult == ERROR_SUCCESS) {
|
|
lresult = RegSetValueEx(
|
|
hkey_progid, nullptr, 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) {
|
|
registry_error(root, "Unable to register Blender App Id");
|
|
return false;
|
|
}
|
|
|
|
SNPRINTF(buffer, "%s\\shell\\open", prog_id);
|
|
lresult = RegCreateKeyEx(root,
|
|
buffer,
|
|
0,
|
|
nullptr,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
nullptr,
|
|
&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,
|
|
nullptr,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
nullptr,
|
|
&hkey_progid,
|
|
&dwd);
|
|
|
|
if (lresult == ERROR_SUCCESS) {
|
|
SNPRINTF(buffer, "\"%s\" \"%%1\"", executable);
|
|
lresult = RegSetValueEx(hkey_progid, nullptr, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1);
|
|
RegCloseKey(hkey_progid);
|
|
}
|
|
if (lresult != ERROR_SUCCESS) {
|
|
registry_error(root, "Unable to register Blender App Id");
|
|
return false;
|
|
}
|
|
|
|
SNPRINTF(buffer, "%s\\DefaultIcon", prog_id);
|
|
lresult = RegCreateKeyEx(root,
|
|
buffer,
|
|
0,
|
|
nullptr,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
nullptr,
|
|
&hkey_progid,
|
|
&dwd);
|
|
|
|
if (lresult == ERROR_SUCCESS) {
|
|
SNPRINTF(buffer, "\"%s\", 1", executable);
|
|
lresult = RegSetValueEx(hkey_progid, nullptr, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1);
|
|
RegCloseKey(hkey_progid);
|
|
}
|
|
if (lresult != ERROR_SUCCESS) {
|
|
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, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &hkey, &dwd);
|
|
if (lresult == ERROR_SUCCESS) {
|
|
/* Set this instance the default. */
|
|
lresult = RegSetValueEx(hkey, nullptr, 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,
|
|
nullptr,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
nullptr,
|
|
&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, nullptr, 0);
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
if (lresult != ERROR_SUCCESS) {
|
|
registry_error(root, "Unable to register Blend document type");
|
|
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];
|
|
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);
|
|
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, nullptr, nullptr, RRF_RT_REG_SZ, nullptr, &buffer, &size);
|
|
if (lresult == ERROR_SUCCESS && STREQ(buffer, BLENDER_WIN_APPID)) {
|
|
RegSetValueEx(hkey, nullptr, 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\\%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;
|
|
}
|
|
|
|
/**
|
|
* Check the registry to see if there is an operation association to a file
|
|
* extension. Extension *should almost always contain a dot like `.txt`,
|
|
* but this does allow querying non - extensions *like "Directory", "Drive",
|
|
* "AllProtocols", etc - anything in Classes with a "shell" branch.
|
|
*/
|
|
static bool BLI_windows_file_operation_is_registered(const char *extension, const char *operation)
|
|
{
|
|
HKEY hKey;
|
|
HRESULT hr = AssocQueryKey(ASSOCF_INIT_IGNOREUNKNOWN,
|
|
ASSOCKEY_SHELLEXECCLASS,
|
|
(LPCTSTR)extension,
|
|
(LPCTSTR)operation,
|
|
&hKey);
|
|
if (SUCCEEDED(hr)) {
|
|
RegCloseKey(hKey);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BLI_windows_external_operation_supported(const char *filepath, const char *operation)
|
|
{
|
|
if (STREQ(operation, "open") || STREQ(operation, "properties")) {
|
|
return true;
|
|
}
|
|
|
|
if (BLI_is_dir(filepath)) {
|
|
return BLI_windows_file_operation_is_registered("Directory", operation);
|
|
}
|
|
|
|
const char *extension = BLI_path_extension(filepath);
|
|
return BLI_windows_file_operation_is_registered(extension, operation);
|
|
}
|
|
|
|
bool BLI_windows_external_operation_execute(const char *filepath, const char *operation)
|
|
{
|
|
WCHAR wpath[FILE_MAX];
|
|
if (conv_utf_8_to_16(filepath, wpath, ARRAY_SIZE(wpath)) != 0) {
|
|
return false;
|
|
}
|
|
|
|
WCHAR woperation[FILE_MAX];
|
|
if (conv_utf_8_to_16(operation, woperation, ARRAY_SIZE(woperation)) != 0) {
|
|
return false;
|
|
}
|
|
|
|
SHELLEXECUTEINFOW shellinfo = {0};
|
|
shellinfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
shellinfo.fMask = SEE_MASK_INVOKEIDLIST;
|
|
shellinfo.lpVerb = woperation;
|
|
shellinfo.lpFile = wpath;
|
|
shellinfo.nShow = SW_SHOW;
|
|
|
|
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 = nullptr;
|
|
shellinfo.lpVerb = elevated ? "runas" : nullptr;
|
|
shellinfo.lpFile = blender_path;
|
|
shellinfo.lpParameters = parameters;
|
|
shellinfo.lpDirectory = nullptr;
|
|
shellinfo.nShow = silent ? SW_HIDE : SW_SHOW;
|
|
shellinfo.hInstApp = nullptr;
|
|
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];
|
|
|
|
/* the default drive to resolve a directory without a specified drive
|
|
* should be the Windows installation drive, since this was what the OS
|
|
* assumes. */
|
|
if (GetWindowsDirectory(str, MAX_PATH + 1)) {
|
|
root[0] = str[0];
|
|
root[1] = ':';
|
|
root[2] = '\\';
|
|
root[3] = '\0';
|
|
}
|
|
else {
|
|
/* if GetWindowsDirectory fails, something has probably gone wrong,
|
|
* we are trying the blender install dir though */
|
|
if (GetModuleFileName(nullptr, str, MAX_PATH + 1)) {
|
|
printf(
|
|
"Error! Could not get the Windows Directory - "
|
|
"Defaulting to Blender installation Dir!\n");
|
|
root[0] = str[0];
|
|
root[1] = ':';
|
|
root[2] = '\\';
|
|
root[3] = '\0';
|
|
}
|
|
else {
|
|
DWORD tmp;
|
|
int i;
|
|
int rc = 0;
|
|
/* now something has gone really wrong - still trying our best guess */
|
|
printf(
|
|
"Error! Could not get the Windows Directory - "
|
|
"Defaulting to first valid drive! Path might be invalid!\n");
|
|
tmp = GetLogicalDrives();
|
|
for (i = 2; i < 26; i++) {
|
|
if ((tmp >> i) & 1) {
|
|
root[0] = 'a' + i;
|
|
root[1] = ':';
|
|
root[2] = '\\';
|
|
root[3] = '\0';
|
|
if (GetFileAttributes(root) != 0xFFFFFFFF) {
|
|
rc = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (0 == rc) {
|
|
printf("ERROR in 'BLI_windows_get_default_root_dir': can't find a valid drive!\n");
|
|
root[0] = 'C';
|
|
root[1] = ':';
|
|
root[2] = '\\';
|
|
root[3] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool BLI_windows_get_directx_driver_version(const wchar_t *deviceSubString,
|
|
long long *r_driverVersion)
|
|
{
|
|
IDXGIFactory *pFactory = nullptr;
|
|
IDXGIAdapter *pAdapter = nullptr;
|
|
if (CreateDXGIFactory(__uuidof(IDXGIFactory), (void **)&pFactory) == S_OK) {
|
|
for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i) {
|
|
LARGE_INTEGER version;
|
|
if (pAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &version) == S_OK) {
|
|
DXGI_ADAPTER_DESC desc;
|
|
if (pAdapter->GetDesc(&desc) == S_OK) {
|
|
if (wcsstr(desc.Description, deviceSubString)) {
|
|
*r_driverVersion = version.QuadPart;
|
|
|
|
pAdapter->Release();
|
|
pFactory->Release();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
pAdapter->Release();
|
|
}
|
|
pFactory->Release();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#else
|
|
|
|
/* intentionally empty for UNIX */
|
|
|
|
#endif
|