Blender V5.0
winstuff.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#ifdef WIN32
11
12# include <conio.h>
13# include <shlwapi.h>
14# include <stdio.h>
15# include <stdlib.h>
16# define COBJMACROS /* Remove this when converting to C++ */
17# include <dxgi.h>
18
19# include "MEM_guardedalloc.h"
20
21# define WIN32_SKIP_HKEY_PROTECTION /* Need to use HKEY. */
22# include "BLI_fileops.h"
23# include "BLI_path_utils.hh"
24# include "BLI_string.h"
25# include "BLI_utildefines.h"
26# include "BLI_winstuff.h"
27
28# include "utf_winfunc.hh"
29# include "utfconv.hh"
30
31/* FILE_MAXDIR + FILE_MAXFILE */
32
33int BLI_windows_get_executable_dir(char r_dirpath[/*FILE_MAXDIR*/])
34{
35 char filepath[FILE_MAX];
36 char dir[FILE_MAX];
37 int a;
38 /* Change to UTF support. */
39 GetModuleFileName(nullptr, filepath, sizeof(filepath));
40 BLI_path_split_dir_part(filepath, dir, sizeof(dir)); /* shouldn't be relative */
41 a = strlen(dir);
42 if (dir[a - 1] == '\\') {
43 dir[a - 1] = 0;
44 }
45
46 BLI_strncpy(r_dirpath, dir, FILE_MAXDIR);
47
48 return 1;
49}
50
52{
53 char install_dir[FILE_MAXDIR];
55 return (BLI_strcasestr(install_dir, "\\WindowsApps\\") != nullptr);
56}
57
58static void registry_error(HKEY root, const char *message)
59{
60 if (root) {
61 RegCloseKey(root);
62 }
63 fprintf(stderr, "%s\n", message);
64}
65
66static bool open_registry_hive(bool all_users, HKEY *r_root)
67{
68 if (RegOpenKeyEx(all_users ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
69 "Software\\Classes",
70 0,
71 KEY_ALL_ACCESS,
72 r_root) != ERROR_SUCCESS)
73 {
74 registry_error(*r_root, "Unable to open the registry with the required permissions");
75 return false;
76 }
77 return true;
78}
79
80static bool register_blender_prog_id(const char *prog_id,
81 const char *executable,
82 const char *friendly_name,
83 bool all_users)
84{
85 LONG lresult;
86 HKEY root = 0;
87 HKEY hkey_progid = 0;
88 char buffer[256];
89 DWORD dwd = 0;
90
91 if (!open_registry_hive(all_users, &root)) {
92 return false;
93 }
94
95 lresult = RegCreateKeyEx(root,
96 prog_id,
97 0,
98 nullptr,
99 REG_OPTION_NON_VOLATILE,
100 KEY_ALL_ACCESS,
101 nullptr,
102 &hkey_progid,
103 &dwd);
104
105 if (lresult == ERROR_SUCCESS) {
106 lresult = RegSetValueEx(
107 hkey_progid, nullptr, 0, REG_SZ, (BYTE *)friendly_name, strlen(friendly_name) + 1);
108 }
109 if (lresult == ERROR_SUCCESS) {
110 lresult = RegSetValueEx(
111 hkey_progid, "AppUserModelId", 0, REG_SZ, (BYTE *)prog_id, strlen(prog_id) + 1);
112 }
113 if (lresult != ERROR_SUCCESS) {
114 registry_error(root, "Unable to register Blender App Id");
115 return false;
116 }
117
118 SNPRINTF(buffer, "%s\\shell\\open", prog_id);
119 lresult = RegCreateKeyEx(root,
120 buffer,
121 0,
122 nullptr,
123 REG_OPTION_NON_VOLATILE,
124 KEY_ALL_ACCESS,
125 nullptr,
126 &hkey_progid,
127 &dwd);
128
129 lresult = RegSetValueEx(
130 hkey_progid, "FriendlyAppName", 0, REG_SZ, (BYTE *)friendly_name, strlen(friendly_name) + 1);
131
132 SNPRINTF(buffer, "%s\\shell\\open\\command", prog_id);
133
134 lresult = RegCreateKeyEx(root,
135 buffer,
136 0,
137 nullptr,
138 REG_OPTION_NON_VOLATILE,
139 KEY_ALL_ACCESS,
140 nullptr,
141 &hkey_progid,
142 &dwd);
143
144 if (lresult == ERROR_SUCCESS) {
145 SNPRINTF(buffer, "\"%s\" \"%%1\"", executable);
146 lresult = RegSetValueEx(hkey_progid, nullptr, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1);
147 RegCloseKey(hkey_progid);
148 }
149 if (lresult != ERROR_SUCCESS) {
150 registry_error(root, "Unable to register Blender App Id");
151 return false;
152 }
153
154 SNPRINTF(buffer, "%s\\DefaultIcon", prog_id);
155 lresult = RegCreateKeyEx(root,
156 buffer,
157 0,
158 nullptr,
159 REG_OPTION_NON_VOLATILE,
160 KEY_ALL_ACCESS,
161 nullptr,
162 &hkey_progid,
163 &dwd);
164
165 if (lresult == ERROR_SUCCESS) {
166 SNPRINTF(buffer, "\"%s\", 1", executable);
167 lresult = RegSetValueEx(hkey_progid, nullptr, 0, REG_SZ, (BYTE *)buffer, strlen(buffer) + 1);
168 RegCloseKey(hkey_progid);
169 }
170 if (lresult != ERROR_SUCCESS) {
171 registry_error(root, "Unable to register Blender App Id");
172 return false;
173 }
174 return true;
175}
176
177bool BLI_windows_register_blend_extension(const bool all_users)
178{
180 fprintf(stderr, "Registration not possible from Microsoft Store installation.");
181 return false;
182 }
183
184 HKEY root = 0;
185 char blender_path[MAX_PATH];
186 char *blender_app;
187 HKEY hkey = 0;
188 LONG lresult;
189 DWORD dwd = 0;
190 const char *prog_id = BLENDER_WIN_APPID;
191 const char *friendly_name = BLENDER_WIN_APPID_FRIENDLY_NAME;
192
193 GetModuleFileName(0, blender_path, sizeof(blender_path));
194
195 /* Prevent overflow when we add -launcher to the executable name. */
196 if (strlen(blender_path) > (sizeof(blender_path) - 10)) {
197 return false;
198 }
199
200 /* Replace the actual app name with the wrapper. */
201 blender_app = strstr(blender_path, "blender.exe");
202 if (!blender_app) {
203 return false;
204 }
205 strcpy(blender_app, "blender-launcher.exe");
206
207 if (!open_registry_hive(all_users, &root)) {
208 return false;
209 }
210
211 if (!register_blender_prog_id(prog_id, blender_path, friendly_name, all_users)) {
212 registry_error(root, "Unable to register Blend document type");
213 return false;
214 }
215
216 lresult = RegCreateKeyEx(
217 root, ".blend", 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &hkey, &dwd);
218 if (lresult == ERROR_SUCCESS) {
219 /* Set this instance the default. */
220 lresult = RegSetValueEx(hkey, nullptr, 0, REG_SZ, (BYTE *)prog_id, strlen(prog_id) + 1);
221
222 if (lresult != ERROR_SUCCESS) {
223 registry_error(root, "Unable to register Blend document type");
224 RegCloseKey(hkey);
225 return false;
226 }
227 RegCloseKey(hkey);
228
229 lresult = RegCreateKeyEx(root,
230 ".blend\\OpenWithProgids",
231 0,
232 nullptr,
233 REG_OPTION_NON_VOLATILE,
234 KEY_ALL_ACCESS,
235 nullptr,
236 &hkey,
237 &dwd);
238
239 if (lresult != ERROR_SUCCESS) {
240 registry_error(root, "Unable to register Blend document type");
241 RegCloseKey(hkey);
242 return false;
243 }
244 lresult = RegSetValueEx(hkey, prog_id, 0, REG_NONE, nullptr, 0);
245 RegCloseKey(hkey);
246 }
247
248 if (lresult != ERROR_SUCCESS) {
249 registry_error(root, "Unable to register Blend document type");
250 return false;
251 }
252
253 if (!BLI_windows_update_pinned_launcher(blender_path)) {
254 fprintf(stderr, "Update of pinned launcher failed.");
255 return false;
256 }
257
258# ifdef WITH_BLENDER_THUMBNAILER
259 {
260 char reg_cmd[MAX_PATH * 2];
261 char install_dir[FILE_MAXDIR];
262 char system_dir[FILE_MAXDIR];
264 GetSystemDirectory(system_dir, sizeof(system_dir));
265 const char *thumbnail_handler = "BlendThumb.dll";
266 SNPRINTF(reg_cmd, "%s\\regsvr32 /s \"%s\\%s\"", system_dir, install_dir, thumbnail_handler);
267 system(reg_cmd);
268 }
269# endif
270
271 RegCloseKey(root);
272 char message[256];
273 SNPRINTF(message,
274 "Blend file extension registered for %s.",
275 all_users ? "all users" : "the current user");
276 printf("%s\n", message);
277
278 return true;
279}
280
281bool BLI_windows_unregister_blend_extension(const bool all_users)
282{
284 fprintf(stderr, "Unregistration not possible from Microsoft Store installation.");
285 return false;
286 }
287
288 HKEY root = 0;
289 HKEY hkey = 0;
290 LONG lresult;
291
292 if (!open_registry_hive(all_users, &root)) {
293 return false;
294 }
295
296 /* Don't stop on failure. We want to allow unregister after unregister. */
297
298 RegDeleteTree(root, BLENDER_WIN_APPID);
299
300 lresult = RegOpenKeyEx(root, ".blend", 0, KEY_ALL_ACCESS, &hkey);
301 if (lresult == ERROR_SUCCESS) {
302 char buffer[256] = {0};
303 DWORD size = sizeof(buffer);
304 lresult = RegGetValueA(hkey, nullptr, nullptr, RRF_RT_REG_SZ, nullptr, &buffer, &size);
305 if (lresult == ERROR_SUCCESS && STREQ(buffer, BLENDER_WIN_APPID)) {
306 RegSetValueEx(hkey, nullptr, 0, REG_SZ, 0, 0);
307 }
308 }
309
310# ifdef WITH_BLENDER_THUMBNAILER
311 {
312 char reg_cmd[MAX_PATH * 2];
313 char install_dir[FILE_MAXDIR];
314 char system_dir[FILE_MAXDIR];
316 GetSystemDirectory(system_dir, sizeof(system_dir));
317 const char *thumbnail_handler = "BlendThumb.dll";
318 SNPRINTF(reg_cmd, "%s\\regsvr32 /u /s \"%s\\%s\"", system_dir, install_dir, thumbnail_handler);
319 system(reg_cmd);
320 }
321# endif
322
323 lresult = RegOpenKeyEx(hkey, "OpenWithProgids", 0, KEY_ALL_ACCESS, &hkey);
324 if (lresult == ERROR_SUCCESS) {
325 RegDeleteValue(hkey, BLENDER_WIN_APPID);
326 }
327
328 RegCloseKey(root);
329 char message[256];
330 SNPRINTF(message,
331 "Blend file extension unregistered for %s.",
332 all_users ? "all users" : "the current user");
333 printf("%s\n", message);
334
335 return true;
336}
337
344static bool BLI_windows_file_operation_is_registered(const char *extension, const char *operation)
345{
346 HKEY hKey;
347 HRESULT hr = AssocQueryKey(ASSOCF_INIT_IGNOREUNKNOWN,
348 ASSOCKEY_SHELLEXECCLASS,
349 (LPCTSTR)extension,
350 (LPCTSTR)operation,
351 &hKey);
352 if (SUCCEEDED(hr)) {
353 RegCloseKey(hKey);
354 return true;
355 }
356 return false;
357}
358
359bool BLI_windows_external_operation_supported(const char *filepath, const char *operation)
360{
361 if (STREQ(operation, "open") || STREQ(operation, "properties")) {
362 return true;
363 }
364
365 if (BLI_is_dir(filepath)) {
366 return BLI_windows_file_operation_is_registered("Directory", operation);
367 }
368
369 const char *extension = BLI_path_extension(filepath);
370 return BLI_windows_file_operation_is_registered(extension, operation);
371}
372
373bool BLI_windows_external_operation_execute(const char *filepath, const char *operation)
374{
375 WCHAR wpath[FILE_MAX];
376 if (conv_utf_8_to_16(filepath, wpath, ARRAY_SIZE(wpath)) != 0) {
377 return false;
378 }
379
380 WCHAR woperation[FILE_MAX];
381 if (conv_utf_8_to_16(operation, woperation, ARRAY_SIZE(woperation)) != 0) {
382 return false;
383 }
384
385 SHELLEXECUTEINFOW shellinfo = {0};
386 shellinfo.cbSize = sizeof(SHELLEXECUTEINFO);
387 shellinfo.fMask = SEE_MASK_INVOKEIDLIST;
388 shellinfo.lpVerb = woperation;
389 shellinfo.lpFile = wpath;
390 shellinfo.nShow = SW_SHOW;
391
392 return ShellExecuteExW(&shellinfo);
393}
394
395bool BLI_windows_execute_self(const char *parameters,
396 const bool wait,
397 const bool elevated,
398 const bool silent)
399{
400 char blender_path[MAX_PATH];
401 GetModuleFileName(0, blender_path, MAX_PATH);
402
403 SHELLEXECUTEINFOA shellinfo = {0};
404 shellinfo.cbSize = sizeof(SHELLEXECUTEINFO);
405 shellinfo.fMask = wait ? SEE_MASK_NOCLOSEPROCESS : SEE_MASK_DEFAULT;
406 shellinfo.hwnd = nullptr;
407 shellinfo.lpVerb = elevated ? "runas" : nullptr;
408 shellinfo.lpFile = blender_path;
409 shellinfo.lpParameters = parameters;
410 shellinfo.lpDirectory = nullptr;
411 shellinfo.nShow = silent ? SW_HIDE : SW_SHOW;
412 shellinfo.hInstApp = nullptr;
413 shellinfo.hProcess = 0;
414
415 DWORD exitCode = 0;
416 if (!ShellExecuteExA(&shellinfo)) {
417 return false;
418 }
419 if (!wait) {
420 return true;
421 }
422
423 if (shellinfo.hProcess != 0) {
424 WaitForSingleObject(shellinfo.hProcess, INFINITE);
425 GetExitCodeProcess(shellinfo.hProcess, &exitCode);
426 CloseHandle(shellinfo.hProcess);
427 return (exitCode == 0);
428 }
429
430 return false;
431}
432
433void BLI_windows_get_default_root_dir(char root[4])
434{
435 char str[MAX_PATH + 1];
436
437 /* the default drive to resolve a directory without a specified drive
438 * should be the Windows installation drive, since this was what the OS
439 * assumes. */
440 if (GetWindowsDirectory(str, MAX_PATH + 1)) {
441 root[0] = str[0];
442 root[1] = ':';
443 root[2] = '\\';
444 root[3] = '\0';
445 }
446 else {
447 /* if GetWindowsDirectory fails, something has probably gone wrong,
448 * we are trying the blender install dir though */
449 if (GetModuleFileName(nullptr, str, MAX_PATH + 1)) {
450 printf(
451 "Error! Could not get the Windows Directory - "
452 "Defaulting to Blender installation Dir!\n");
453 root[0] = str[0];
454 root[1] = ':';
455 root[2] = '\\';
456 root[3] = '\0';
457 }
458 else {
459 DWORD tmp;
460 int i;
461 int rc = 0;
462 /* now something has gone really wrong - still trying our best guess */
463 printf(
464 "Error! Could not get the Windows Directory - "
465 "Defaulting to first valid drive! Path might be invalid!\n");
466 tmp = GetLogicalDrives();
467 for (i = 2; i < 26; i++) {
468 if ((tmp >> i) & 1) {
469 root[0] = 'a' + i;
470 root[1] = ':';
471 root[2] = '\\';
472 root[3] = '\0';
473 if (GetFileAttributes(root) != 0xFFFFFFFF) {
474 rc = i;
475 break;
476 }
477 }
478 }
479 if (0 == rc) {
480 printf("ERROR in 'BLI_windows_get_default_root_dir': cannot find a valid drive!\n");
481 root[0] = 'C';
482 root[1] = ':';
483 root[2] = '\\';
484 root[3] = '\0';
485 }
486 }
487 }
488}
489
490bool BLI_windows_get_directx_driver_version(const wchar_t *deviceSubString,
491 long long *r_driverVersion)
492{
493 IDXGIFactory *pFactory = nullptr;
494 IDXGIAdapter *pAdapter = nullptr;
495 if (CreateDXGIFactory(__uuidof(IDXGIFactory), (void **)&pFactory) == S_OK) {
496 for (UINT i = 0; pFactory->EnumAdapters(i, &pAdapter) != DXGI_ERROR_NOT_FOUND; ++i) {
497 LARGE_INTEGER version;
498 if (pAdapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &version) == S_OK) {
499 DXGI_ADAPTER_DESC desc;
500 if (pAdapter->GetDesc(&desc) == S_OK) {
501 if (wcsstr(desc.Description, deviceSubString)) {
502 *r_driverVersion = version.QuadPart;
503
504 pAdapter->Release();
505 pFactory->Release();
506 return true;
507 }
508 }
509 }
510
511 pAdapter->Release();
512 }
513 pFactory->Release();
514 }
515
516 return false;
517}
518
520 DWORD minorVersion,
521 DWORD buildNumber)
522{
523 HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
524 if (hMod == 0) {
525 return false;
526 }
527
528 typedef NTSTATUS(WINAPI * RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
529 RtlGetVersionPtr rtl_get_version = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
530 if (rtl_get_version == nullptr) {
531 fprintf(stderr, "BLI_windows_is_build_version_greater_or_equal: RtlGetVersion not found.");
532 return false;
533 }
534
535 RTL_OSVERSIONINFOW osVersioninfo{};
536 osVersioninfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);
537 if (rtl_get_version(&osVersioninfo) != 0) {
538 fprintf(stderr, "BLI_windows_is_build_version_greater_or_equal: RtlGetVersion failed.");
539 return false;
540 }
541 if (majorVersion != osVersioninfo.dwMajorVersion) {
542 return osVersioninfo.dwMajorVersion > majorVersion;
543 }
544 if (minorVersion != osVersioninfo.dwMinorVersion) {
545 return osVersioninfo.dwMajorVersion > minorVersion;
546 }
547 return osVersioninfo.dwBuildNumber >= buildNumber;
548}
549
550void BLI_windows_process_set_qos(QoSMode qos_mode, QoSPrecedence qos_precedence)
551{
552 static QoSPrecedence qos_precedence_last = QoSPrecedence::JOB;
553 if (int(qos_precedence) < int(qos_precedence_last)) {
554 return;
555 }
556
557 /* Only supported on Windows build >= 10.0.22000, i.e., Windows 11 21H2:
558 * https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ne-processthreadsapi-process_information_class
559 */
561 return;
562 }
563
564 PROCESS_POWER_THROTTLING_STATE processPowerThrottlingState{};
565 processPowerThrottlingState.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
566 switch (qos_mode) {
567 case QoSMode::DEFAULT:
568 processPowerThrottlingState.ControlMask = 0;
569 processPowerThrottlingState.StateMask = 0;
570 break;
571 case QoSMode::HIGH:
572 processPowerThrottlingState.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
573 processPowerThrottlingState.StateMask = 0;
574 break;
575 case QoSMode::ECO:
576 processPowerThrottlingState.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
577 processPowerThrottlingState.StateMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
578 break;
579 }
580 HANDLE hProcess = GetCurrentProcess();
581 if (!SetProcessInformation(hProcess,
582 ProcessPowerThrottling,
583 &processPowerThrottlingState,
584 sizeof(PROCESS_POWER_THROTTLING_STATE)))
585 {
586 fprintf(
587 stderr, "BLI_windows_set_process_qos: SetProcessInformation failed: %d\n", GetLastError());
588 return;
589 }
590 qos_precedence_last = qos_precedence;
591}
592
593#else
594
595/* intentionally empty for UNIX */
596
597#endif
File and directory operations.
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:443
#define FILE_MAX
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
const char * BLI_path_extension(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAXDIR
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define ARRAY_SIZE(arr)
#define STREQ(a, b)
Compatibility-like things for windows.
bool BLI_windows_external_operation_supported(const char *filepath, const char *operation)
bool BLI_windows_unregister_blend_extension(bool all_users)
bool BLI_windows_execute_self(const char *parameters, const bool wait, const bool elevated, const bool silent)
bool BLI_windows_get_directx_driver_version(const wchar_t *deviceSubString, long long *r_driverVersion)
void BLI_windows_get_default_root_dir(char root_dir[4])
bool BLI_windows_update_pinned_launcher(const char *launcher_path)
bool BLI_windows_is_build_version_greater_or_equal(DWORD majorVersion, DWORD minorVersion, DWORD buildNumber)
bool BLI_windows_external_operation_execute(const char *filepath, const char *operation)
int BLI_windows_get_executable_dir(char r_dirpath[])
QoSMode
void BLI_windows_process_set_qos(QoSMode qos_mode, QoSPrecedence qos_precedence)
QoSPrecedence
bool BLI_windows_is_store_install(void)
bool BLI_windows_register_blend_extension(bool all_users)
Read Guarded memory(de)allocation.
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define str(s)
#define printf(...)
#define L
i
Definition text_draw.cc:230
int conv_utf_8_to_16(const char *in8, wchar_t *out16, size_t size16)
Definition utfconv.cc:182