obs-windows.c 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  1. /******************************************************************************
  2. Copyright (C) 2013 by Hugh Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "util/windows/win-registry.h"
  15. #include "util/windows/win-version.h"
  16. #include "util/platform.h"
  17. #include "util/dstr.h"
  18. #include "obs.h"
  19. #include "obs-internal.h"
  20. #include <windows.h>
  21. #include <wscapi.h>
  22. #include <iwscapi.h>
  23. static uint32_t win_ver = 0;
  24. const char *get_module_extension(void)
  25. {
  26. return ".dll";
  27. }
  28. #ifdef _WIN64
  29. #define BIT_STRING "64bit"
  30. #else
  31. #define BIT_STRING "32bit"
  32. #endif
  33. static const char *module_bin[] = {
  34. "obs-plugins/" BIT_STRING,
  35. "../../obs-plugins/" BIT_STRING,
  36. };
  37. static const char *module_data[] = {"data/%module%",
  38. "../../data/obs-plugins/%module%"};
  39. static const int module_patterns_size =
  40. sizeof(module_bin) / sizeof(module_bin[0]);
  41. void add_default_module_paths(void)
  42. {
  43. for (int i = 0; i < module_patterns_size; i++)
  44. obs_add_module_path(module_bin[i], module_data[i]);
  45. }
  46. /* on windows, points to [base directory]/data/libobs */
  47. char *find_libobs_data_file(const char *file)
  48. {
  49. struct dstr path;
  50. dstr_init(&path);
  51. if (check_path(file, "data/libobs/", &path))
  52. return path.array;
  53. if (check_path(file, "../../data/libobs/", &path))
  54. return path.array;
  55. dstr_free(&path);
  56. return NULL;
  57. }
  58. static void log_processor_info(void)
  59. {
  60. HKEY key;
  61. wchar_t data[1024];
  62. char *str = NULL;
  63. DWORD size, speed;
  64. LSTATUS status;
  65. memset(data, 0, sizeof(data));
  66. status = RegOpenKeyW(
  67. HKEY_LOCAL_MACHINE,
  68. L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &key);
  69. if (status != ERROR_SUCCESS)
  70. return;
  71. size = sizeof(data);
  72. status = RegQueryValueExW(key, L"ProcessorNameString", NULL, NULL,
  73. (LPBYTE)data, &size);
  74. if (status == ERROR_SUCCESS) {
  75. os_wcs_to_utf8_ptr(data, 0, &str);
  76. blog(LOG_INFO, "CPU Name: %s", str);
  77. bfree(str);
  78. }
  79. size = sizeof(speed);
  80. status = RegQueryValueExW(key, L"~MHz", NULL, NULL, (LPBYTE)&speed,
  81. &size);
  82. if (status == ERROR_SUCCESS)
  83. blog(LOG_INFO, "CPU Speed: %ldMHz", speed);
  84. RegCloseKey(key);
  85. }
  86. static void log_processor_cores(void)
  87. {
  88. blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
  89. os_get_physical_cores(), os_get_logical_cores());
  90. }
  91. static void log_available_memory(void)
  92. {
  93. MEMORYSTATUSEX ms;
  94. ms.dwLength = sizeof(ms);
  95. GlobalMemoryStatusEx(&ms);
  96. #ifdef _WIN64
  97. const char *note = "";
  98. #else
  99. const char *note = " (NOTE: 32bit programs cannot use more than 3gb)";
  100. #endif
  101. blog(LOG_INFO, "Physical Memory: %luMB Total, %luMB Free%s",
  102. (DWORD)(ms.ullTotalPhys / 1048576),
  103. (DWORD)(ms.ullAvailPhys / 1048576), note);
  104. }
  105. extern const char *get_win_release_id();
  106. static void log_windows_version(void)
  107. {
  108. struct win_version_info ver;
  109. get_win_ver(&ver);
  110. const char *release_id = get_win_release_id();
  111. bool b64 = is_64_bit_windows();
  112. const char *windows_bitness = b64 ? "64" : "32";
  113. blog(LOG_INFO,
  114. "Windows Version: %d.%d Build %d (release: %s; revision: %d; %s-bit)",
  115. ver.major, ver.minor, ver.build, release_id, ver.revis,
  116. windows_bitness);
  117. }
  118. static void log_admin_status(void)
  119. {
  120. SID_IDENTIFIER_AUTHORITY auth = SECURITY_NT_AUTHORITY;
  121. PSID admin_group;
  122. BOOL success;
  123. success = AllocateAndInitializeSid(&auth, 2,
  124. SECURITY_BUILTIN_DOMAIN_RID,
  125. DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0,
  126. 0, 0, &admin_group);
  127. if (success) {
  128. if (!CheckTokenMembership(NULL, admin_group, &success))
  129. success = false;
  130. FreeSid(admin_group);
  131. }
  132. blog(LOG_INFO, "Running as administrator: %s",
  133. success ? "true" : "false");
  134. }
  135. typedef HRESULT(WINAPI *dwm_is_composition_enabled_t)(BOOL *);
  136. static void log_aero(void)
  137. {
  138. dwm_is_composition_enabled_t composition_enabled = NULL;
  139. const char *aeroMessage =
  140. win_ver >= 0x602
  141. ? " (Aero is always on for windows 8 and above)"
  142. : "";
  143. HMODULE dwm = LoadLibraryW(L"dwmapi");
  144. BOOL bComposition = true;
  145. if (!dwm) {
  146. return;
  147. }
  148. composition_enabled = (dwm_is_composition_enabled_t)GetProcAddress(
  149. dwm, "DwmIsCompositionEnabled");
  150. if (!composition_enabled) {
  151. FreeLibrary(dwm);
  152. return;
  153. }
  154. composition_enabled(&bComposition);
  155. blog(LOG_INFO, "Aero is %s%s", bComposition ? "Enabled" : "Disabled",
  156. aeroMessage);
  157. }
  158. #define WIN10_GAME_BAR_REG_KEY \
  159. L"Software\\Microsoft\\Windows\\CurrentVersion\\GameDVR"
  160. #define WIN10_GAME_DVR_POLICY_REG_KEY \
  161. L"SOFTWARE\\Policies\\Microsoft\\Windows\\GameDVR"
  162. #define WIN10_GAME_DVR_REG_KEY L"System\\GameConfigStore"
  163. #define WIN10_GAME_MODE_REG_KEY L"Software\\Microsoft\\GameBar"
  164. static void log_gaming_features(void)
  165. {
  166. if (win_ver < 0xA00)
  167. return;
  168. struct reg_dword game_bar_enabled;
  169. struct reg_dword game_dvr_allowed;
  170. struct reg_dword game_dvr_enabled;
  171. struct reg_dword game_dvr_bg_recording;
  172. struct reg_dword game_mode_enabled;
  173. get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_BAR_REG_KEY,
  174. L"AppCaptureEnabled", &game_bar_enabled);
  175. get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_DVR_POLICY_REG_KEY,
  176. L"AllowGameDVR", &game_dvr_allowed);
  177. get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_DVR_REG_KEY,
  178. L"GameDVR_Enabled", &game_dvr_enabled);
  179. get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_BAR_REG_KEY,
  180. L"HistoricalCaptureEnabled", &game_dvr_bg_recording);
  181. get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_MODE_REG_KEY,
  182. L"AllowAutoGameMode", &game_mode_enabled);
  183. if (game_mode_enabled.status != ERROR_SUCCESS) {
  184. get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_MODE_REG_KEY,
  185. L"AutoGameModeEnabled", &game_mode_enabled);
  186. }
  187. blog(LOG_INFO, "Windows 10 Gaming Features:");
  188. if (game_bar_enabled.status == ERROR_SUCCESS) {
  189. blog(LOG_INFO, "\tGame Bar: %s",
  190. (bool)game_bar_enabled.return_value ? "On" : "Off");
  191. }
  192. if (game_dvr_allowed.status == ERROR_SUCCESS) {
  193. blog(LOG_INFO, "\tGame DVR Allowed: %s",
  194. (bool)game_dvr_allowed.return_value ? "Yes" : "No");
  195. }
  196. if (game_dvr_enabled.status == ERROR_SUCCESS) {
  197. blog(LOG_INFO, "\tGame DVR: %s",
  198. (bool)game_dvr_enabled.return_value ? "On" : "Off");
  199. }
  200. if (game_dvr_bg_recording.status == ERROR_SUCCESS) {
  201. blog(LOG_INFO, "\tGame DVR Background Recording: %s",
  202. (bool)game_dvr_bg_recording.return_value ? "On" : "Off");
  203. }
  204. if (game_mode_enabled.status == ERROR_SUCCESS) {
  205. blog(LOG_INFO, "\tGame Mode: %s",
  206. (bool)game_mode_enabled.return_value ? "On" : "Off");
  207. }
  208. }
  209. static const char *get_str_for_state(int state)
  210. {
  211. switch (state) {
  212. case WSC_SECURITY_PRODUCT_STATE_ON:
  213. return "enabled";
  214. case WSC_SECURITY_PRODUCT_STATE_OFF:
  215. return "disabled";
  216. case WSC_SECURITY_PRODUCT_STATE_SNOOZED:
  217. return "temporarily disabled";
  218. case WSC_SECURITY_PRODUCT_STATE_EXPIRED:
  219. return "expired";
  220. default:
  221. return "unknown";
  222. }
  223. }
  224. static const char *get_str_for_type(int type)
  225. {
  226. switch (type) {
  227. case WSC_SECURITY_PROVIDER_ANTIVIRUS:
  228. return "AV";
  229. case WSC_SECURITY_PROVIDER_FIREWALL:
  230. return "FW";
  231. case WSC_SECURITY_PROVIDER_ANTISPYWARE:
  232. return "ASW";
  233. default:
  234. return "unknown";
  235. }
  236. }
  237. static void log_security_products_by_type(IWSCProductList *prod_list, int type)
  238. {
  239. HRESULT hr;
  240. LONG count = 0;
  241. IWscProduct *prod;
  242. BSTR name;
  243. WSC_SECURITY_PRODUCT_STATE prod_state;
  244. hr = prod_list->lpVtbl->Initialize(prod_list, type);
  245. if (FAILED(hr))
  246. return;
  247. hr = prod_list->lpVtbl->get_Count(prod_list, &count);
  248. if (FAILED(hr)) {
  249. prod_list->lpVtbl->Release(prod_list);
  250. return;
  251. }
  252. for (int i = 0; i < count; i++) {
  253. hr = prod_list->lpVtbl->get_Item(prod_list, i, &prod);
  254. if (FAILED(hr))
  255. continue;
  256. hr = prod->lpVtbl->get_ProductName(prod, &name);
  257. if (FAILED(hr))
  258. continue;
  259. hr = prod->lpVtbl->get_ProductState(prod, &prod_state);
  260. if (FAILED(hr)) {
  261. SysFreeString(name);
  262. continue;
  263. }
  264. blog(LOG_INFO, "\t%S: %s (%s)", name,
  265. get_str_for_state(prod_state), get_str_for_type(type));
  266. SysFreeString(name);
  267. prod->lpVtbl->Release(prod);
  268. }
  269. prod_list->lpVtbl->Release(prod_list);
  270. }
  271. static void log_security_products(void)
  272. {
  273. IWSCProductList *prod_list = NULL;
  274. HMODULE h_wsc;
  275. HRESULT hr;
  276. /* We load the DLL rather than import wcsapi.lib because the clsid /
  277. * iid only exists on Windows 8 or higher. */
  278. h_wsc = LoadLibraryW(L"wscapi.dll");
  279. if (!h_wsc)
  280. return;
  281. const CLSID *prod_list_clsid =
  282. (const CLSID *)GetProcAddress(h_wsc, "CLSID_WSCProductList");
  283. const IID *prod_list_iid =
  284. (const IID *)GetProcAddress(h_wsc, "IID_IWSCProductList");
  285. if (prod_list_clsid && prod_list_iid) {
  286. blog(LOG_INFO, "Sec. Software Status:");
  287. hr = CoCreateInstance(prod_list_clsid, NULL,
  288. CLSCTX_INPROC_SERVER, prod_list_iid,
  289. &prod_list);
  290. if (!FAILED(hr)) {
  291. log_security_products_by_type(
  292. prod_list, WSC_SECURITY_PROVIDER_ANTIVIRUS);
  293. }
  294. hr = CoCreateInstance(prod_list_clsid, NULL,
  295. CLSCTX_INPROC_SERVER, prod_list_iid,
  296. &prod_list);
  297. if (!FAILED(hr)) {
  298. log_security_products_by_type(
  299. prod_list, WSC_SECURITY_PROVIDER_FIREWALL);
  300. }
  301. hr = CoCreateInstance(prod_list_clsid, NULL,
  302. CLSCTX_INPROC_SERVER, prod_list_iid,
  303. &prod_list);
  304. if (!FAILED(hr)) {
  305. log_security_products_by_type(
  306. prod_list, WSC_SECURITY_PROVIDER_ANTISPYWARE);
  307. }
  308. }
  309. FreeLibrary(h_wsc);
  310. }
  311. void log_system_info(void)
  312. {
  313. struct win_version_info ver;
  314. get_win_ver(&ver);
  315. win_ver = (ver.major << 8) | ver.minor;
  316. log_processor_info();
  317. log_processor_cores();
  318. log_available_memory();
  319. log_windows_version();
  320. log_admin_status();
  321. log_aero();
  322. log_gaming_features();
  323. log_security_products();
  324. }
  325. struct obs_hotkeys_platform {
  326. int vk_codes[OBS_KEY_LAST_VALUE];
  327. };
  328. static int get_virtual_key(obs_key_t key)
  329. {
  330. switch (key) {
  331. case OBS_KEY_RETURN:
  332. return VK_RETURN;
  333. case OBS_KEY_ESCAPE:
  334. return VK_ESCAPE;
  335. case OBS_KEY_TAB:
  336. return VK_TAB;
  337. case OBS_KEY_BACKTAB:
  338. return VK_OEM_BACKTAB;
  339. case OBS_KEY_BACKSPACE:
  340. return VK_BACK;
  341. case OBS_KEY_INSERT:
  342. return VK_INSERT;
  343. case OBS_KEY_DELETE:
  344. return VK_DELETE;
  345. case OBS_KEY_PAUSE:
  346. return VK_PAUSE;
  347. case OBS_KEY_PRINT:
  348. return VK_SNAPSHOT;
  349. case OBS_KEY_CLEAR:
  350. return VK_CLEAR;
  351. case OBS_KEY_HOME:
  352. return VK_HOME;
  353. case OBS_KEY_END:
  354. return VK_END;
  355. case OBS_KEY_LEFT:
  356. return VK_LEFT;
  357. case OBS_KEY_UP:
  358. return VK_UP;
  359. case OBS_KEY_RIGHT:
  360. return VK_RIGHT;
  361. case OBS_KEY_DOWN:
  362. return VK_DOWN;
  363. case OBS_KEY_PAGEUP:
  364. return VK_PRIOR;
  365. case OBS_KEY_PAGEDOWN:
  366. return VK_NEXT;
  367. case OBS_KEY_SHIFT:
  368. return VK_SHIFT;
  369. case OBS_KEY_CONTROL:
  370. return VK_CONTROL;
  371. case OBS_KEY_ALT:
  372. return VK_MENU;
  373. case OBS_KEY_CAPSLOCK:
  374. return VK_CAPITAL;
  375. case OBS_KEY_NUMLOCK:
  376. return VK_NUMLOCK;
  377. case OBS_KEY_SCROLLLOCK:
  378. return VK_SCROLL;
  379. case OBS_KEY_F1:
  380. return VK_F1;
  381. case OBS_KEY_F2:
  382. return VK_F2;
  383. case OBS_KEY_F3:
  384. return VK_F3;
  385. case OBS_KEY_F4:
  386. return VK_F4;
  387. case OBS_KEY_F5:
  388. return VK_F5;
  389. case OBS_KEY_F6:
  390. return VK_F6;
  391. case OBS_KEY_F7:
  392. return VK_F7;
  393. case OBS_KEY_F8:
  394. return VK_F8;
  395. case OBS_KEY_F9:
  396. return VK_F9;
  397. case OBS_KEY_F10:
  398. return VK_F10;
  399. case OBS_KEY_F11:
  400. return VK_F11;
  401. case OBS_KEY_F12:
  402. return VK_F12;
  403. case OBS_KEY_F13:
  404. return VK_F13;
  405. case OBS_KEY_F14:
  406. return VK_F14;
  407. case OBS_KEY_F15:
  408. return VK_F15;
  409. case OBS_KEY_F16:
  410. return VK_F16;
  411. case OBS_KEY_F17:
  412. return VK_F17;
  413. case OBS_KEY_F18:
  414. return VK_F18;
  415. case OBS_KEY_F19:
  416. return VK_F19;
  417. case OBS_KEY_F20:
  418. return VK_F20;
  419. case OBS_KEY_F21:
  420. return VK_F21;
  421. case OBS_KEY_F22:
  422. return VK_F22;
  423. case OBS_KEY_F23:
  424. return VK_F23;
  425. case OBS_KEY_F24:
  426. return VK_F24;
  427. case OBS_KEY_SPACE:
  428. return VK_SPACE;
  429. case OBS_KEY_APOSTROPHE:
  430. return VK_OEM_7;
  431. case OBS_KEY_PLUS:
  432. return VK_OEM_PLUS;
  433. case OBS_KEY_COMMA:
  434. return VK_OEM_COMMA;
  435. case OBS_KEY_MINUS:
  436. return VK_OEM_MINUS;
  437. case OBS_KEY_PERIOD:
  438. return VK_OEM_PERIOD;
  439. case OBS_KEY_SLASH:
  440. return VK_OEM_2;
  441. case OBS_KEY_0:
  442. return '0';
  443. case OBS_KEY_1:
  444. return '1';
  445. case OBS_KEY_2:
  446. return '2';
  447. case OBS_KEY_3:
  448. return '3';
  449. case OBS_KEY_4:
  450. return '4';
  451. case OBS_KEY_5:
  452. return '5';
  453. case OBS_KEY_6:
  454. return '6';
  455. case OBS_KEY_7:
  456. return '7';
  457. case OBS_KEY_8:
  458. return '8';
  459. case OBS_KEY_9:
  460. return '9';
  461. case OBS_KEY_NUMASTERISK:
  462. return VK_MULTIPLY;
  463. case OBS_KEY_NUMPLUS:
  464. return VK_ADD;
  465. case OBS_KEY_NUMMINUS:
  466. return VK_SUBTRACT;
  467. case OBS_KEY_NUMPERIOD:
  468. return VK_DECIMAL;
  469. case OBS_KEY_NUMSLASH:
  470. return VK_DIVIDE;
  471. case OBS_KEY_NUM0:
  472. return VK_NUMPAD0;
  473. case OBS_KEY_NUM1:
  474. return VK_NUMPAD1;
  475. case OBS_KEY_NUM2:
  476. return VK_NUMPAD2;
  477. case OBS_KEY_NUM3:
  478. return VK_NUMPAD3;
  479. case OBS_KEY_NUM4:
  480. return VK_NUMPAD4;
  481. case OBS_KEY_NUM5:
  482. return VK_NUMPAD5;
  483. case OBS_KEY_NUM6:
  484. return VK_NUMPAD6;
  485. case OBS_KEY_NUM7:
  486. return VK_NUMPAD7;
  487. case OBS_KEY_NUM8:
  488. return VK_NUMPAD8;
  489. case OBS_KEY_NUM9:
  490. return VK_NUMPAD9;
  491. case OBS_KEY_SEMICOLON:
  492. return VK_OEM_1;
  493. case OBS_KEY_A:
  494. return 'A';
  495. case OBS_KEY_B:
  496. return 'B';
  497. case OBS_KEY_C:
  498. return 'C';
  499. case OBS_KEY_D:
  500. return 'D';
  501. case OBS_KEY_E:
  502. return 'E';
  503. case OBS_KEY_F:
  504. return 'F';
  505. case OBS_KEY_G:
  506. return 'G';
  507. case OBS_KEY_H:
  508. return 'H';
  509. case OBS_KEY_I:
  510. return 'I';
  511. case OBS_KEY_J:
  512. return 'J';
  513. case OBS_KEY_K:
  514. return 'K';
  515. case OBS_KEY_L:
  516. return 'L';
  517. case OBS_KEY_M:
  518. return 'M';
  519. case OBS_KEY_N:
  520. return 'N';
  521. case OBS_KEY_O:
  522. return 'O';
  523. case OBS_KEY_P:
  524. return 'P';
  525. case OBS_KEY_Q:
  526. return 'Q';
  527. case OBS_KEY_R:
  528. return 'R';
  529. case OBS_KEY_S:
  530. return 'S';
  531. case OBS_KEY_T:
  532. return 'T';
  533. case OBS_KEY_U:
  534. return 'U';
  535. case OBS_KEY_V:
  536. return 'V';
  537. case OBS_KEY_W:
  538. return 'W';
  539. case OBS_KEY_X:
  540. return 'X';
  541. case OBS_KEY_Y:
  542. return 'Y';
  543. case OBS_KEY_Z:
  544. return 'Z';
  545. case OBS_KEY_BRACKETLEFT:
  546. return VK_OEM_4;
  547. case OBS_KEY_BACKSLASH:
  548. return VK_OEM_5;
  549. case OBS_KEY_BRACKETRIGHT:
  550. return VK_OEM_6;
  551. case OBS_KEY_ASCIITILDE:
  552. return VK_OEM_3;
  553. case OBS_KEY_HENKAN:
  554. return VK_CONVERT;
  555. case OBS_KEY_MUHENKAN:
  556. return VK_NONCONVERT;
  557. case OBS_KEY_KANJI:
  558. return VK_KANJI;
  559. case OBS_KEY_TOUROKU:
  560. return VK_OEM_FJ_TOUROKU;
  561. case OBS_KEY_MASSYO:
  562. return VK_OEM_FJ_MASSHOU;
  563. case OBS_KEY_HANGUL:
  564. return VK_HANGUL;
  565. case OBS_KEY_BACKSLASH_RT102:
  566. return VK_OEM_102;
  567. case OBS_KEY_MOUSE1:
  568. return VK_LBUTTON;
  569. case OBS_KEY_MOUSE2:
  570. return VK_RBUTTON;
  571. case OBS_KEY_MOUSE3:
  572. return VK_MBUTTON;
  573. case OBS_KEY_MOUSE4:
  574. return VK_XBUTTON1;
  575. case OBS_KEY_MOUSE5:
  576. return VK_XBUTTON2;
  577. case OBS_KEY_VK_CANCEL:
  578. return VK_CANCEL;
  579. case OBS_KEY_0x07:
  580. return 0x07;
  581. case OBS_KEY_0x0A:
  582. return 0x0A;
  583. case OBS_KEY_0x0B:
  584. return 0x0B;
  585. case OBS_KEY_0x0E:
  586. return 0x0E;
  587. case OBS_KEY_0x0F:
  588. return 0x0F;
  589. case OBS_KEY_0x16:
  590. return 0x16;
  591. case OBS_KEY_VK_JUNJA:
  592. return VK_JUNJA;
  593. case OBS_KEY_VK_FINAL:
  594. return VK_FINAL;
  595. case OBS_KEY_0x1A:
  596. return 0x1A;
  597. case OBS_KEY_VK_ACCEPT:
  598. return VK_ACCEPT;
  599. case OBS_KEY_VK_MODECHANGE:
  600. return VK_MODECHANGE;
  601. case OBS_KEY_VK_SELECT:
  602. return VK_SELECT;
  603. case OBS_KEY_VK_PRINT:
  604. return VK_PRINT;
  605. case OBS_KEY_VK_EXECUTE:
  606. return VK_EXECUTE;
  607. case OBS_KEY_VK_HELP:
  608. return VK_HELP;
  609. case OBS_KEY_0x30:
  610. return 0x30;
  611. case OBS_KEY_0x31:
  612. return 0x31;
  613. case OBS_KEY_0x32:
  614. return 0x32;
  615. case OBS_KEY_0x33:
  616. return 0x33;
  617. case OBS_KEY_0x34:
  618. return 0x34;
  619. case OBS_KEY_0x35:
  620. return 0x35;
  621. case OBS_KEY_0x36:
  622. return 0x36;
  623. case OBS_KEY_0x37:
  624. return 0x37;
  625. case OBS_KEY_0x38:
  626. return 0x38;
  627. case OBS_KEY_0x39:
  628. return 0x39;
  629. case OBS_KEY_0x3A:
  630. return 0x3A;
  631. case OBS_KEY_0x3B:
  632. return 0x3B;
  633. case OBS_KEY_0x3C:
  634. return 0x3C;
  635. case OBS_KEY_0x3D:
  636. return 0x3D;
  637. case OBS_KEY_0x3E:
  638. return 0x3E;
  639. case OBS_KEY_0x3F:
  640. return 0x3F;
  641. case OBS_KEY_0x40:
  642. return 0x40;
  643. case OBS_KEY_0x41:
  644. return 0x41;
  645. case OBS_KEY_0x42:
  646. return 0x42;
  647. case OBS_KEY_0x43:
  648. return 0x43;
  649. case OBS_KEY_0x44:
  650. return 0x44;
  651. case OBS_KEY_0x45:
  652. return 0x45;
  653. case OBS_KEY_0x46:
  654. return 0x46;
  655. case OBS_KEY_0x47:
  656. return 0x47;
  657. case OBS_KEY_0x48:
  658. return 0x48;
  659. case OBS_KEY_0x49:
  660. return 0x49;
  661. case OBS_KEY_0x4A:
  662. return 0x4A;
  663. case OBS_KEY_0x4B:
  664. return 0x4B;
  665. case OBS_KEY_0x4C:
  666. return 0x4C;
  667. case OBS_KEY_0x4D:
  668. return 0x4D;
  669. case OBS_KEY_0x4E:
  670. return 0x4E;
  671. case OBS_KEY_0x4F:
  672. return 0x4F;
  673. case OBS_KEY_0x50:
  674. return 0x50;
  675. case OBS_KEY_0x51:
  676. return 0x51;
  677. case OBS_KEY_0x52:
  678. return 0x52;
  679. case OBS_KEY_0x53:
  680. return 0x53;
  681. case OBS_KEY_0x54:
  682. return 0x54;
  683. case OBS_KEY_0x55:
  684. return 0x55;
  685. case OBS_KEY_0x56:
  686. return 0x56;
  687. case OBS_KEY_0x57:
  688. return 0x57;
  689. case OBS_KEY_0x58:
  690. return 0x58;
  691. case OBS_KEY_0x59:
  692. return 0x59;
  693. case OBS_KEY_0x5A:
  694. return 0x5A;
  695. case OBS_KEY_VK_LWIN:
  696. return VK_LWIN;
  697. case OBS_KEY_VK_RWIN:
  698. return VK_RWIN;
  699. case OBS_KEY_VK_APPS:
  700. return VK_APPS;
  701. case OBS_KEY_0x5E:
  702. return 0x5E;
  703. case OBS_KEY_VK_SLEEP:
  704. return VK_SLEEP;
  705. case OBS_KEY_VK_SEPARATOR:
  706. return VK_SEPARATOR;
  707. case OBS_KEY_0x88:
  708. return 0x88;
  709. case OBS_KEY_0x89:
  710. return 0x89;
  711. case OBS_KEY_0x8A:
  712. return 0x8A;
  713. case OBS_KEY_0x8B:
  714. return 0x8B;
  715. case OBS_KEY_0x8C:
  716. return 0x8C;
  717. case OBS_KEY_0x8D:
  718. return 0x8D;
  719. case OBS_KEY_0x8E:
  720. return 0x8E;
  721. case OBS_KEY_0x8F:
  722. return 0x8F;
  723. case OBS_KEY_VK_OEM_FJ_JISHO:
  724. return VK_OEM_FJ_JISHO;
  725. case OBS_KEY_VK_OEM_FJ_LOYA:
  726. return VK_OEM_FJ_LOYA;
  727. case OBS_KEY_VK_OEM_FJ_ROYA:
  728. return VK_OEM_FJ_ROYA;
  729. case OBS_KEY_0x97:
  730. return 0x97;
  731. case OBS_KEY_0x98:
  732. return 0x98;
  733. case OBS_KEY_0x99:
  734. return 0x99;
  735. case OBS_KEY_0x9A:
  736. return 0x9A;
  737. case OBS_KEY_0x9B:
  738. return 0x9B;
  739. case OBS_KEY_0x9C:
  740. return 0x9C;
  741. case OBS_KEY_0x9D:
  742. return 0x9D;
  743. case OBS_KEY_0x9E:
  744. return 0x9E;
  745. case OBS_KEY_0x9F:
  746. return 0x9F;
  747. case OBS_KEY_VK_LSHIFT:
  748. return VK_LSHIFT;
  749. case OBS_KEY_VK_RSHIFT:
  750. return VK_RSHIFT;
  751. case OBS_KEY_VK_LCONTROL:
  752. return VK_LCONTROL;
  753. case OBS_KEY_VK_RCONTROL:
  754. return VK_RCONTROL;
  755. case OBS_KEY_VK_LMENU:
  756. return VK_LMENU;
  757. case OBS_KEY_VK_RMENU:
  758. return VK_RMENU;
  759. case OBS_KEY_VK_BROWSER_BACK:
  760. return VK_BROWSER_BACK;
  761. case OBS_KEY_VK_BROWSER_FORWARD:
  762. return VK_BROWSER_FORWARD;
  763. case OBS_KEY_VK_BROWSER_REFRESH:
  764. return VK_BROWSER_REFRESH;
  765. case OBS_KEY_VK_BROWSER_STOP:
  766. return VK_BROWSER_STOP;
  767. case OBS_KEY_VK_BROWSER_SEARCH:
  768. return VK_BROWSER_SEARCH;
  769. case OBS_KEY_VK_BROWSER_FAVORITES:
  770. return VK_BROWSER_FAVORITES;
  771. case OBS_KEY_VK_BROWSER_HOME:
  772. return VK_BROWSER_HOME;
  773. case OBS_KEY_VK_VOLUME_MUTE:
  774. return VK_VOLUME_MUTE;
  775. case OBS_KEY_VK_VOLUME_DOWN:
  776. return VK_VOLUME_DOWN;
  777. case OBS_KEY_VK_VOLUME_UP:
  778. return VK_VOLUME_UP;
  779. case OBS_KEY_VK_MEDIA_NEXT_TRACK:
  780. return VK_MEDIA_NEXT_TRACK;
  781. case OBS_KEY_VK_MEDIA_PREV_TRACK:
  782. return VK_MEDIA_PREV_TRACK;
  783. case OBS_KEY_VK_MEDIA_STOP:
  784. return VK_MEDIA_STOP;
  785. case OBS_KEY_VK_MEDIA_PLAY_PAUSE:
  786. return VK_MEDIA_PLAY_PAUSE;
  787. case OBS_KEY_VK_LAUNCH_MAIL:
  788. return VK_LAUNCH_MAIL;
  789. case OBS_KEY_VK_LAUNCH_MEDIA_SELECT:
  790. return VK_LAUNCH_MEDIA_SELECT;
  791. case OBS_KEY_VK_LAUNCH_APP1:
  792. return VK_LAUNCH_APP1;
  793. case OBS_KEY_VK_LAUNCH_APP2:
  794. return VK_LAUNCH_APP2;
  795. case OBS_KEY_0xB8:
  796. return 0xB8;
  797. case OBS_KEY_0xB9:
  798. return 0xB9;
  799. case OBS_KEY_0xC1:
  800. return 0xC1;
  801. case OBS_KEY_0xC2:
  802. return 0xC2;
  803. case OBS_KEY_0xC3:
  804. return 0xC3;
  805. case OBS_KEY_0xC4:
  806. return 0xC4;
  807. case OBS_KEY_0xC5:
  808. return 0xC5;
  809. case OBS_KEY_0xC6:
  810. return 0xC6;
  811. case OBS_KEY_0xC7:
  812. return 0xC7;
  813. case OBS_KEY_0xC8:
  814. return 0xC8;
  815. case OBS_KEY_0xC9:
  816. return 0xC9;
  817. case OBS_KEY_0xCA:
  818. return 0xCA;
  819. case OBS_KEY_0xCB:
  820. return 0xCB;
  821. case OBS_KEY_0xCC:
  822. return 0xCC;
  823. case OBS_KEY_0xCD:
  824. return 0xCD;
  825. case OBS_KEY_0xCE:
  826. return 0xCE;
  827. case OBS_KEY_0xCF:
  828. return 0xCF;
  829. case OBS_KEY_0xD0:
  830. return 0xD0;
  831. case OBS_KEY_0xD1:
  832. return 0xD1;
  833. case OBS_KEY_0xD2:
  834. return 0xD2;
  835. case OBS_KEY_0xD3:
  836. return 0xD3;
  837. case OBS_KEY_0xD4:
  838. return 0xD4;
  839. case OBS_KEY_0xD5:
  840. return 0xD5;
  841. case OBS_KEY_0xD6:
  842. return 0xD6;
  843. case OBS_KEY_0xD7:
  844. return 0xD7;
  845. case OBS_KEY_0xD8:
  846. return 0xD8;
  847. case OBS_KEY_0xD9:
  848. return 0xD9;
  849. case OBS_KEY_0xDA:
  850. return 0xDA;
  851. case OBS_KEY_VK_OEM_8:
  852. return VK_OEM_8;
  853. case OBS_KEY_0xE0:
  854. return 0xE0;
  855. case OBS_KEY_VK_OEM_AX:
  856. return VK_OEM_AX;
  857. case OBS_KEY_VK_ICO_HELP:
  858. return VK_ICO_HELP;
  859. case OBS_KEY_VK_ICO_00:
  860. return VK_ICO_00;
  861. case OBS_KEY_VK_PROCESSKEY:
  862. return VK_PROCESSKEY;
  863. case OBS_KEY_VK_ICO_CLEAR:
  864. return VK_ICO_CLEAR;
  865. case OBS_KEY_VK_PACKET:
  866. return VK_PACKET;
  867. case OBS_KEY_0xE8:
  868. return 0xE8;
  869. case OBS_KEY_VK_OEM_RESET:
  870. return VK_OEM_RESET;
  871. case OBS_KEY_VK_OEM_JUMP:
  872. return VK_OEM_JUMP;
  873. case OBS_KEY_VK_OEM_PA1:
  874. return VK_OEM_PA1;
  875. case OBS_KEY_VK_OEM_PA2:
  876. return VK_OEM_PA2;
  877. case OBS_KEY_VK_OEM_PA3:
  878. return VK_OEM_PA3;
  879. case OBS_KEY_VK_OEM_WSCTRL:
  880. return VK_OEM_WSCTRL;
  881. case OBS_KEY_VK_OEM_CUSEL:
  882. return VK_OEM_CUSEL;
  883. case OBS_KEY_VK_OEM_ATTN:
  884. return VK_OEM_ATTN;
  885. case OBS_KEY_VK_OEM_FINISH:
  886. return VK_OEM_FINISH;
  887. case OBS_KEY_VK_OEM_COPY:
  888. return VK_OEM_COPY;
  889. case OBS_KEY_VK_OEM_AUTO:
  890. return VK_OEM_AUTO;
  891. case OBS_KEY_VK_OEM_ENLW:
  892. return VK_OEM_ENLW;
  893. case OBS_KEY_VK_ATTN:
  894. return VK_ATTN;
  895. case OBS_KEY_VK_CRSEL:
  896. return VK_CRSEL;
  897. case OBS_KEY_VK_EXSEL:
  898. return VK_EXSEL;
  899. case OBS_KEY_VK_EREOF:
  900. return VK_EREOF;
  901. case OBS_KEY_VK_PLAY:
  902. return VK_PLAY;
  903. case OBS_KEY_VK_ZOOM:
  904. return VK_ZOOM;
  905. case OBS_KEY_VK_NONAME:
  906. return VK_NONAME;
  907. case OBS_KEY_VK_PA1:
  908. return VK_PA1;
  909. case OBS_KEY_VK_OEM_CLEAR:
  910. return VK_OEM_CLEAR;
  911. /* TODO: Implement keys for non-US keyboards */
  912. default:;
  913. }
  914. return 0;
  915. }
  916. bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
  917. {
  918. hotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t));
  919. for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)
  920. hotkeys->platform_context->vk_codes[i] = get_virtual_key(i);
  921. return true;
  922. }
  923. void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
  924. {
  925. bfree(hotkeys->platform_context);
  926. hotkeys->platform_context = NULL;
  927. }
  928. static bool vk_down(DWORD vk)
  929. {
  930. short state = GetAsyncKeyState(vk);
  931. bool down = (state & 0x8000) != 0;
  932. return down;
  933. }
  934. bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context,
  935. obs_key_t key)
  936. {
  937. if (key == OBS_KEY_META) {
  938. return vk_down(VK_LWIN) || vk_down(VK_RWIN);
  939. }
  940. UNUSED_PARAMETER(context);
  941. return vk_down(obs_key_to_virtual_key(key));
  942. }
  943. void obs_key_to_str(obs_key_t key, struct dstr *str)
  944. {
  945. wchar_t name[128] = L"";
  946. UINT scan_code;
  947. int vk;
  948. if (key == OBS_KEY_NONE) {
  949. return;
  950. } else if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {
  951. if (obs->hotkeys.translations[key]) {
  952. dstr_copy(str, obs->hotkeys.translations[key]);
  953. } else {
  954. dstr_printf(str, "Mouse %d",
  955. (int)(key - OBS_KEY_MOUSE1 + 1));
  956. }
  957. return;
  958. }
  959. if (key == OBS_KEY_PAUSE) {
  960. dstr_copy(str, obs_get_hotkey_translation(key, "Pause"));
  961. return;
  962. } else if (key == OBS_KEY_META) {
  963. dstr_copy(str, obs_get_hotkey_translation(key, "Windows"));
  964. return;
  965. }
  966. vk = obs_key_to_virtual_key(key);
  967. scan_code = MapVirtualKey(vk, 0) << 16;
  968. switch (vk) {
  969. case VK_HOME:
  970. case VK_END:
  971. case VK_LEFT:
  972. case VK_UP:
  973. case VK_RIGHT:
  974. case VK_DOWN:
  975. case VK_PRIOR:
  976. case VK_NEXT:
  977. case VK_INSERT:
  978. case VK_DELETE:
  979. case VK_NUMLOCK:
  980. scan_code |= 0x01000000;
  981. }
  982. if ((key < OBS_KEY_VK_CANCEL || key > OBS_KEY_VK_OEM_CLEAR) &&
  983. scan_code != 0 && GetKeyNameTextW(scan_code, name, 128) != 0) {
  984. dstr_from_wcs(str, name);
  985. } else if (key != OBS_KEY_NONE) {
  986. dstr_copy(str, obs_key_to_name(key));
  987. }
  988. }
  989. obs_key_t obs_key_from_virtual_key(int code)
  990. {
  991. obs_hotkeys_platform_t *platform = obs->hotkeys.platform_context;
  992. for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
  993. if (platform->vk_codes[i] == code) {
  994. return (obs_key_t)i;
  995. }
  996. }
  997. return OBS_KEY_NONE;
  998. }
  999. int obs_key_to_virtual_key(obs_key_t key)
  1000. {
  1001. if (key == OBS_KEY_META)
  1002. return VK_LWIN;
  1003. return obs->hotkeys.platform_context->vk_codes[(int)key];
  1004. }
  1005. static inline void add_combo_key(obs_key_t key, struct dstr *str)
  1006. {
  1007. struct dstr key_str = {0};
  1008. obs_key_to_str(key, &key_str);
  1009. if (!dstr_is_empty(&key_str)) {
  1010. if (!dstr_is_empty(str)) {
  1011. dstr_cat(str, " + ");
  1012. }
  1013. dstr_cat_dstr(str, &key_str);
  1014. }
  1015. dstr_free(&key_str);
  1016. }
  1017. void obs_key_combination_to_str(obs_key_combination_t combination,
  1018. struct dstr *str)
  1019. {
  1020. if ((combination.modifiers & INTERACT_CONTROL_KEY) != 0) {
  1021. add_combo_key(OBS_KEY_CONTROL, str);
  1022. }
  1023. if ((combination.modifiers & INTERACT_COMMAND_KEY) != 0) {
  1024. add_combo_key(OBS_KEY_META, str);
  1025. }
  1026. if ((combination.modifiers & INTERACT_ALT_KEY) != 0) {
  1027. add_combo_key(OBS_KEY_ALT, str);
  1028. }
  1029. if ((combination.modifiers & INTERACT_SHIFT_KEY) != 0) {
  1030. add_combo_key(OBS_KEY_SHIFT, str);
  1031. }
  1032. if (combination.key != OBS_KEY_NONE) {
  1033. add_combo_key(combination.key, str);
  1034. }
  1035. }
  1036. bool sym_initialize_called = false;
  1037. void reset_win32_symbol_paths(void)
  1038. {
  1039. static BOOL(WINAPI * sym_initialize_w)(HANDLE, const wchar_t *, BOOL);
  1040. static BOOL(WINAPI * sym_set_search_path_w)(HANDLE, const wchar_t *);
  1041. static bool funcs_initialized = false;
  1042. static bool initialize_success = false;
  1043. struct obs_module *module = obs->first_module;
  1044. struct dstr path_str = {0};
  1045. DARRAY(char *) paths;
  1046. wchar_t *path_str_w = NULL;
  1047. char *abspath;
  1048. da_init(paths);
  1049. if (!funcs_initialized) {
  1050. HMODULE mod;
  1051. funcs_initialized = true;
  1052. mod = LoadLibraryW(L"DbgHelp");
  1053. if (!mod)
  1054. return;
  1055. sym_initialize_w =
  1056. (void *)GetProcAddress(mod, "SymInitializeW");
  1057. sym_set_search_path_w =
  1058. (void *)GetProcAddress(mod, "SymSetSearchPathW");
  1059. if (!sym_initialize_w || !sym_set_search_path_w) {
  1060. FreeLibrary(mod);
  1061. return;
  1062. }
  1063. initialize_success = true;
  1064. // Leaks 'mod' once.
  1065. }
  1066. if (!initialize_success)
  1067. return;
  1068. abspath = os_get_abs_path_ptr(".");
  1069. if (abspath)
  1070. da_push_back(paths, &abspath);
  1071. while (module) {
  1072. bool found = false;
  1073. struct dstr path = {0};
  1074. char *path_end;
  1075. dstr_copy(&path, module->bin_path);
  1076. dstr_replace(&path, "/", "\\");
  1077. path_end = strrchr(path.array, '\\');
  1078. if (!path_end) {
  1079. module = module->next;
  1080. dstr_free(&path);
  1081. continue;
  1082. }
  1083. *path_end = 0;
  1084. for (size_t i = 0; i < paths.num; i++) {
  1085. const char *existing_path = paths.array[i];
  1086. if (astrcmpi(path.array, existing_path) == 0) {
  1087. found = true;
  1088. break;
  1089. }
  1090. }
  1091. if (!found) {
  1092. abspath = os_get_abs_path_ptr(path.array);
  1093. if (abspath)
  1094. da_push_back(paths, &abspath);
  1095. }
  1096. dstr_free(&path);
  1097. module = module->next;
  1098. }
  1099. for (size_t i = 0; i < paths.num; i++) {
  1100. const char *path = paths.array[i];
  1101. if (path && *path) {
  1102. if (i != 0)
  1103. dstr_cat(&path_str, ";");
  1104. dstr_cat(&path_str, paths.array[i]);
  1105. }
  1106. }
  1107. if (path_str.array) {
  1108. os_utf8_to_wcs_ptr(path_str.array, path_str.len, &path_str_w);
  1109. if (path_str_w) {
  1110. if (!sym_initialize_called) {
  1111. sym_initialize_w(GetCurrentProcess(),
  1112. path_str_w, false);
  1113. sym_initialize_called = true;
  1114. } else {
  1115. sym_set_search_path_w(GetCurrentProcess(),
  1116. path_str_w);
  1117. }
  1118. bfree(path_str_w);
  1119. }
  1120. }
  1121. for (size_t i = 0; i < paths.num; i++)
  1122. bfree(paths.array[i]);
  1123. dstr_free(&path_str);
  1124. da_free(paths);
  1125. }
  1126. extern void initialize_crash_handler(void);
  1127. void obs_init_win32_crash_handler(void)
  1128. {
  1129. initialize_crash_handler();
  1130. }
  1131. bool initialize_com(void)
  1132. {
  1133. const HRESULT hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
  1134. const bool success = SUCCEEDED(hr);
  1135. if (success)
  1136. blog(LOG_INFO, "CoInitializeEx succeeded: 0x%08X", hr);
  1137. else
  1138. blog(LOG_ERROR, "CoInitializeEx failed: 0x%08X", hr);
  1139. return success;
  1140. }
  1141. void uninitialize_com(void)
  1142. {
  1143. CoUninitialize();
  1144. }