platform-windows.c 22 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. /*
  2. * Copyright (c) 2013 Hugh Bailey <[email protected]>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #define PSAPI_VERSION 1
  17. #include <windows.h>
  18. #include <mmsystem.h>
  19. #include <shellapi.h>
  20. #include <shlobj.h>
  21. #include <intrin.h>
  22. #include <psapi.h>
  23. #include "base.h"
  24. #include "platform.h"
  25. #include "darray.h"
  26. #include "dstr.h"
  27. #include "windows/win-registry.h"
  28. #include "windows/win-version.h"
  29. #include "../../deps/w32-pthreads/pthread.h"
  30. static bool have_clockfreq = false;
  31. static LARGE_INTEGER clock_freq;
  32. static uint32_t winver = 0;
  33. static inline uint64_t get_clockfreq(void)
  34. {
  35. if (!have_clockfreq) {
  36. QueryPerformanceFrequency(&clock_freq);
  37. have_clockfreq = true;
  38. }
  39. return clock_freq.QuadPart;
  40. }
  41. static inline uint32_t get_winver(void)
  42. {
  43. if (!winver) {
  44. struct win_version_info ver;
  45. get_win_ver(&ver);
  46. winver = (ver.major << 8) | ver.minor;
  47. }
  48. return winver;
  49. }
  50. void *os_dlopen(const char *path)
  51. {
  52. struct dstr dll_name;
  53. wchar_t *wpath;
  54. wchar_t *wpath_slash;
  55. HMODULE h_library = NULL;
  56. if (!path)
  57. return NULL;
  58. dstr_init_copy(&dll_name, path);
  59. dstr_replace(&dll_name, "\\", "/");
  60. if (!dstr_find(&dll_name, ".dll"))
  61. dstr_cat(&dll_name, ".dll");
  62. os_utf8_to_wcs_ptr(dll_name.array, 0, &wpath);
  63. /* to make module dependency issues easier to deal with, allow
  64. * dynamically loaded libraries on windows to search for dependent
  65. * libraries that are within the library's own directory */
  66. wpath_slash = wcsrchr(wpath, L'/');
  67. if (wpath_slash) {
  68. *wpath_slash = 0;
  69. SetDllDirectoryW(wpath);
  70. *wpath_slash = L'/';
  71. }
  72. h_library = LoadLibraryW(wpath);
  73. bfree(wpath);
  74. dstr_free(&dll_name);
  75. if (wpath_slash)
  76. SetDllDirectoryW(NULL);
  77. if (!h_library) {
  78. DWORD error = GetLastError();
  79. /* don't print error for libraries that aren't meant to be
  80. * dynamically linked */
  81. if (error == ERROR_PROC_NOT_FOUND)
  82. return NULL;
  83. char *message = NULL;
  84. FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
  85. FORMAT_MESSAGE_IGNORE_INSERTS |
  86. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  87. NULL, error,
  88. MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  89. (LPSTR)&message, 0, NULL);
  90. blog(LOG_INFO, "LoadLibrary failed for '%s': %s (%lu)",
  91. path, message, error);
  92. if (message)
  93. LocalFree(message);
  94. }
  95. return h_library;
  96. }
  97. void *os_dlsym(void *module, const char *func)
  98. {
  99. void *handle;
  100. handle = (void*)GetProcAddress(module, func);
  101. return handle;
  102. }
  103. void os_dlclose(void *module)
  104. {
  105. FreeLibrary(module);
  106. }
  107. union time_data {
  108. FILETIME ft;
  109. unsigned long long val;
  110. };
  111. struct os_cpu_usage_info {
  112. union time_data last_time, last_sys_time, last_user_time;
  113. DWORD core_count;
  114. };
  115. os_cpu_usage_info_t *os_cpu_usage_info_start(void)
  116. {
  117. struct os_cpu_usage_info *info = bzalloc(sizeof(*info));
  118. SYSTEM_INFO si;
  119. FILETIME dummy;
  120. GetSystemInfo(&si);
  121. GetSystemTimeAsFileTime(&info->last_time.ft);
  122. GetProcessTimes(GetCurrentProcess(), &dummy, &dummy,
  123. &info->last_sys_time.ft, &info->last_user_time.ft);
  124. info->core_count = si.dwNumberOfProcessors;
  125. return info;
  126. }
  127. double os_cpu_usage_info_query(os_cpu_usage_info_t *info)
  128. {
  129. union time_data cur_time, cur_sys_time, cur_user_time;
  130. FILETIME dummy;
  131. double percent;
  132. if (!info)
  133. return 0.0;
  134. GetSystemTimeAsFileTime(&cur_time.ft);
  135. GetProcessTimes(GetCurrentProcess(), &dummy, &dummy,
  136. &cur_sys_time.ft, &cur_user_time.ft);
  137. percent = (double)(cur_sys_time.val - info->last_sys_time.val +
  138. (cur_user_time.val - info->last_user_time.val));
  139. percent /= (double)(cur_time.val - info->last_time.val);
  140. percent /= (double)info->core_count;
  141. info->last_time.val = cur_time.val;
  142. info->last_sys_time.val = cur_sys_time.val;
  143. info->last_user_time.val = cur_user_time.val;
  144. return percent * 100.0;
  145. }
  146. void os_cpu_usage_info_destroy(os_cpu_usage_info_t *info)
  147. {
  148. if (info)
  149. bfree(info);
  150. }
  151. bool os_sleepto_ns(uint64_t time_target)
  152. {
  153. uint64_t t = os_gettime_ns();
  154. uint32_t milliseconds;
  155. if (t >= time_target)
  156. return false;
  157. milliseconds = (uint32_t)((time_target - t)/1000000);
  158. if (milliseconds > 1)
  159. Sleep(milliseconds-1);
  160. for (;;) {
  161. t = os_gettime_ns();
  162. if (t >= time_target)
  163. return true;
  164. #if 0
  165. Sleep(1);
  166. #else
  167. Sleep(0);
  168. #endif
  169. }
  170. }
  171. void os_sleep_ms(uint32_t duration)
  172. {
  173. /* windows 8+ appears to have decreased sleep precision */
  174. if (get_winver() >= 0x0602 && duration > 0)
  175. duration--;
  176. Sleep(duration);
  177. }
  178. uint64_t os_gettime_ns(void)
  179. {
  180. LARGE_INTEGER current_time;
  181. double time_val;
  182. QueryPerformanceCounter(&current_time);
  183. time_val = (double)current_time.QuadPart;
  184. time_val *= 1000000000.0;
  185. time_val /= (double)get_clockfreq();
  186. return (uint64_t)time_val;
  187. }
  188. /* returns [folder]\[name] on windows */
  189. static int os_get_path_internal(char *dst, size_t size, const char *name,
  190. int folder)
  191. {
  192. wchar_t path_utf16[MAX_PATH];
  193. SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT,
  194. path_utf16);
  195. if (os_wcs_to_utf8(path_utf16, 0, dst, size) != 0) {
  196. if (!name || !*name) {
  197. return (int)strlen(dst);
  198. }
  199. if (strcat_s(dst, size, "\\") == 0) {
  200. if (strcat_s(dst, size, name) == 0) {
  201. return (int)strlen(dst);
  202. }
  203. }
  204. }
  205. return -1;
  206. }
  207. static char *os_get_path_ptr_internal(const char *name, int folder)
  208. {
  209. char *ptr;
  210. wchar_t path_utf16[MAX_PATH];
  211. struct dstr path;
  212. SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT,
  213. path_utf16);
  214. os_wcs_to_utf8_ptr(path_utf16, 0, &ptr);
  215. dstr_init_move_array(&path, ptr);
  216. dstr_cat(&path, "\\");
  217. dstr_cat(&path, name);
  218. return path.array;
  219. }
  220. int os_get_config_path(char *dst, size_t size, const char *name)
  221. {
  222. return os_get_path_internal(dst, size, name, CSIDL_APPDATA);
  223. }
  224. char *os_get_config_path_ptr(const char *name)
  225. {
  226. return os_get_path_ptr_internal(name, CSIDL_APPDATA);
  227. }
  228. int os_get_program_data_path(char *dst, size_t size, const char *name)
  229. {
  230. return os_get_path_internal(dst, size, name, CSIDL_COMMON_APPDATA);
  231. }
  232. char *os_get_program_data_path_ptr(const char *name)
  233. {
  234. return os_get_path_ptr_internal(name, CSIDL_COMMON_APPDATA);
  235. }
  236. bool os_file_exists(const char *path)
  237. {
  238. WIN32_FIND_DATAW wfd;
  239. HANDLE hFind;
  240. wchar_t *path_utf16;
  241. if (!os_utf8_to_wcs_ptr(path, 0, &path_utf16))
  242. return false;
  243. hFind = FindFirstFileW(path_utf16, &wfd);
  244. if (hFind != INVALID_HANDLE_VALUE)
  245. FindClose(hFind);
  246. bfree(path_utf16);
  247. return hFind != INVALID_HANDLE_VALUE;
  248. }
  249. size_t os_get_abs_path(const char *path, char *abspath, size_t size)
  250. {
  251. wchar_t wpath[512];
  252. wchar_t wabspath[512];
  253. size_t out_len = 0;
  254. size_t len;
  255. if (!abspath)
  256. return 0;
  257. len = os_utf8_to_wcs(path, 0, wpath, 512);
  258. if (!len)
  259. return 0;
  260. if (_wfullpath(wabspath, wpath, 512) != NULL)
  261. out_len = os_wcs_to_utf8(wabspath, 0, abspath, size);
  262. return out_len;
  263. }
  264. char *os_get_abs_path_ptr(const char *path)
  265. {
  266. char *ptr = bmalloc(512);
  267. if (!os_get_abs_path(path, ptr, 512)) {
  268. bfree(ptr);
  269. ptr = NULL;
  270. }
  271. return ptr;
  272. }
  273. struct os_dir {
  274. HANDLE handle;
  275. WIN32_FIND_DATA wfd;
  276. bool first;
  277. struct os_dirent out;
  278. };
  279. os_dir_t *os_opendir(const char *path)
  280. {
  281. struct dstr path_str = {0};
  282. struct os_dir *dir = NULL;
  283. WIN32_FIND_DATA wfd;
  284. HANDLE handle;
  285. wchar_t *w_path;
  286. dstr_copy(&path_str, path);
  287. dstr_cat(&path_str, "/*.*");
  288. if (os_utf8_to_wcs_ptr(path_str.array, path_str.len, &w_path) > 0) {
  289. handle = FindFirstFileW(w_path, &wfd);
  290. if (handle != INVALID_HANDLE_VALUE) {
  291. dir = bzalloc(sizeof(struct os_dir));
  292. dir->handle = handle;
  293. dir->first = true;
  294. dir->wfd = wfd;
  295. }
  296. bfree(w_path);
  297. }
  298. dstr_free(&path_str);
  299. return dir;
  300. }
  301. static inline bool is_dir(WIN32_FIND_DATA *wfd)
  302. {
  303. return !!(wfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  304. }
  305. struct os_dirent *os_readdir(os_dir_t *dir)
  306. {
  307. if (!dir)
  308. return NULL;
  309. if (dir->first) {
  310. dir->first = false;
  311. } else {
  312. if (!FindNextFileW(dir->handle, &dir->wfd))
  313. return NULL;
  314. }
  315. os_wcs_to_utf8(dir->wfd.cFileName, 0, dir->out.d_name,
  316. sizeof(dir->out.d_name));
  317. dir->out.directory = is_dir(&dir->wfd);
  318. return &dir->out;
  319. }
  320. void os_closedir(os_dir_t *dir)
  321. {
  322. if (dir) {
  323. FindClose(dir->handle);
  324. bfree(dir);
  325. }
  326. }
  327. int64_t os_get_free_space(const char *path)
  328. {
  329. ULARGE_INTEGER remainingSpace;
  330. char abs_path[512];
  331. wchar_t w_abs_path[512];
  332. if (os_get_abs_path(path, abs_path, 512) > 0) {
  333. if (os_utf8_to_wcs(abs_path, 0, w_abs_path, 512) > 0) {
  334. BOOL success = GetDiskFreeSpaceExW(w_abs_path,
  335. (PULARGE_INTEGER)&remainingSpace,
  336. NULL, NULL);
  337. if (success)
  338. return (int64_t)remainingSpace.QuadPart;
  339. }
  340. }
  341. return -1;
  342. }
  343. static void make_globent(struct os_globent *ent, WIN32_FIND_DATA *wfd,
  344. const char *pattern)
  345. {
  346. struct dstr name = {0};
  347. struct dstr path = {0};
  348. char *slash;
  349. dstr_from_wcs(&name, wfd->cFileName);
  350. dstr_copy(&path, pattern);
  351. slash = strrchr(path.array, '/');
  352. if (slash)
  353. dstr_resize(&path, slash + 1 - path.array);
  354. else
  355. dstr_free(&path);
  356. dstr_cat_dstr(&path, &name);
  357. ent->path = path.array;
  358. ent->directory = is_dir(wfd);
  359. dstr_free(&name);
  360. }
  361. int os_glob(const char *pattern, int flags, os_glob_t **pglob)
  362. {
  363. DARRAY(struct os_globent) files;
  364. HANDLE handle;
  365. WIN32_FIND_DATA wfd;
  366. int ret = -1;
  367. wchar_t *w_path;
  368. da_init(files);
  369. if (os_utf8_to_wcs_ptr(pattern, 0, &w_path) > 0) {
  370. handle = FindFirstFileW(w_path, &wfd);
  371. if (handle != INVALID_HANDLE_VALUE) {
  372. do {
  373. struct os_globent ent = {0};
  374. make_globent(&ent, &wfd, pattern);
  375. if (ent.path)
  376. da_push_back(files, &ent);
  377. } while (FindNextFile(handle, &wfd));
  378. FindClose(handle);
  379. *pglob = bmalloc(sizeof(**pglob));
  380. (*pglob)->gl_pathc = files.num;
  381. (*pglob)->gl_pathv = files.array;
  382. ret = 0;
  383. }
  384. bfree(w_path);
  385. }
  386. if (ret != 0)
  387. *pglob = NULL;
  388. UNUSED_PARAMETER(flags);
  389. return ret;
  390. }
  391. void os_globfree(os_glob_t *pglob)
  392. {
  393. if (pglob) {
  394. for (size_t i = 0; i < pglob->gl_pathc; i++)
  395. bfree(pglob->gl_pathv[i].path);
  396. bfree(pglob->gl_pathv);
  397. bfree(pglob);
  398. }
  399. }
  400. int os_unlink(const char *path)
  401. {
  402. wchar_t *w_path;
  403. bool success;
  404. os_utf8_to_wcs_ptr(path, 0, &w_path);
  405. if (!w_path)
  406. return -1;
  407. success = !!DeleteFileW(w_path);
  408. bfree(w_path);
  409. return success ? 0 : -1;
  410. }
  411. int os_rmdir(const char *path)
  412. {
  413. wchar_t *w_path;
  414. bool success;
  415. os_utf8_to_wcs_ptr(path, 0, &w_path);
  416. if (!w_path)
  417. return -1;
  418. success = !!RemoveDirectoryW(w_path);
  419. bfree(w_path);
  420. return success ? 0 : -1;
  421. }
  422. int os_mkdir(const char *path)
  423. {
  424. wchar_t *path_utf16;
  425. BOOL success;
  426. if (!os_utf8_to_wcs_ptr(path, 0, &path_utf16))
  427. return MKDIR_ERROR;
  428. success = CreateDirectory(path_utf16, NULL);
  429. bfree(path_utf16);
  430. if (!success)
  431. return (GetLastError() == ERROR_ALREADY_EXISTS) ?
  432. MKDIR_EXISTS : MKDIR_ERROR;
  433. return MKDIR_SUCCESS;
  434. }
  435. int os_rename(const char *old_path, const char *new_path)
  436. {
  437. wchar_t *old_path_utf16 = NULL;
  438. wchar_t *new_path_utf16 = NULL;
  439. int code = -1;
  440. if (!os_utf8_to_wcs_ptr(old_path, 0, &old_path_utf16)) {
  441. return -1;
  442. }
  443. if (!os_utf8_to_wcs_ptr(new_path, 0, &new_path_utf16)) {
  444. goto error;
  445. }
  446. code = MoveFileExW(old_path_utf16, new_path_utf16,
  447. MOVEFILE_REPLACE_EXISTING) ? 0 : -1;
  448. error:
  449. bfree(old_path_utf16);
  450. bfree(new_path_utf16);
  451. return code;
  452. }
  453. int os_safe_replace(const char *target, const char *from, const char *backup)
  454. {
  455. wchar_t *wtarget = NULL;
  456. wchar_t *wfrom = NULL;
  457. wchar_t *wbackup = NULL;
  458. int code = -1;
  459. if (!target || !from)
  460. return -1;
  461. if (!os_utf8_to_wcs_ptr(target, 0, &wtarget))
  462. return -1;
  463. if (!os_utf8_to_wcs_ptr(from, 0, &wfrom))
  464. goto fail;
  465. if (backup && !os_utf8_to_wcs_ptr(backup, 0, &wbackup))
  466. goto fail;
  467. if (ReplaceFileW(wtarget, wfrom, wbackup, 0, NULL, NULL)) {
  468. code = 0;
  469. } else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
  470. code = MoveFileExW(wfrom, wtarget, MOVEFILE_REPLACE_EXISTING)
  471. ? 0 : -1;
  472. }
  473. fail:
  474. bfree(wtarget);
  475. bfree(wfrom);
  476. bfree(wbackup);
  477. return code;
  478. }
  479. BOOL WINAPI DllMain(HINSTANCE hinst_dll, DWORD reason, LPVOID reserved)
  480. {
  481. switch (reason) {
  482. case DLL_PROCESS_ATTACH:
  483. timeBeginPeriod(1);
  484. #ifdef PTW32_STATIC_LIB
  485. pthread_win32_process_attach_np();
  486. #endif
  487. break;
  488. case DLL_PROCESS_DETACH:
  489. timeEndPeriod(1);
  490. #ifdef PTW32_STATIC_LIB
  491. pthread_win32_process_detach_np();
  492. #endif
  493. break;
  494. case DLL_THREAD_ATTACH:
  495. #ifdef PTW32_STATIC_LIB
  496. pthread_win32_thread_attach_np();
  497. #endif
  498. break;
  499. case DLL_THREAD_DETACH:
  500. #ifdef PTW32_STATIC_LIB
  501. pthread_win32_thread_detach_np();
  502. #endif
  503. break;
  504. }
  505. UNUSED_PARAMETER(hinst_dll);
  506. UNUSED_PARAMETER(reserved);
  507. return true;
  508. }
  509. os_performance_token_t *os_request_high_performance(const char *reason)
  510. {
  511. UNUSED_PARAMETER(reason);
  512. return NULL;
  513. }
  514. void os_end_high_performance(os_performance_token_t *token)
  515. {
  516. UNUSED_PARAMETER(token);
  517. }
  518. int os_copyfile(const char *file_in, const char *file_out)
  519. {
  520. wchar_t *file_in_utf16 = NULL;
  521. wchar_t *file_out_utf16 = NULL;
  522. int code = -1;
  523. if (!os_utf8_to_wcs_ptr(file_in, 0, &file_in_utf16)) {
  524. return -1;
  525. }
  526. if (!os_utf8_to_wcs_ptr(file_out, 0, &file_out_utf16)) {
  527. goto error;
  528. }
  529. code = CopyFileW(file_in_utf16, file_out_utf16, true) ? 0 : -1;
  530. error:
  531. bfree(file_in_utf16);
  532. bfree(file_out_utf16);
  533. return code;
  534. }
  535. char *os_getcwd(char *path, size_t size)
  536. {
  537. wchar_t *path_w;
  538. DWORD len;
  539. len = GetCurrentDirectoryW(0, NULL);
  540. if (!len)
  541. return NULL;
  542. path_w = bmalloc((len + 1) * sizeof(wchar_t));
  543. GetCurrentDirectoryW(len + 1, path_w);
  544. os_wcs_to_utf8(path_w, (size_t)len, path, size);
  545. bfree(path_w);
  546. return path;
  547. }
  548. int os_chdir(const char *path)
  549. {
  550. wchar_t *path_w = NULL;
  551. size_t size;
  552. int ret;
  553. size = os_utf8_to_wcs_ptr(path, 0, &path_w);
  554. if (!path_w)
  555. return -1;
  556. ret = SetCurrentDirectoryW(path_w) ? 0 : -1;
  557. bfree(path_w);
  558. return ret;
  559. }
  560. typedef DWORD (WINAPI *get_file_version_info_size_w_t)(
  561. LPCWSTR module,
  562. LPDWORD unused);
  563. typedef BOOL (WINAPI *get_file_version_info_w_t)(
  564. LPCWSTR module,
  565. DWORD unused,
  566. DWORD len,
  567. LPVOID data);
  568. typedef BOOL (WINAPI *ver_query_value_w_t)(
  569. LPVOID data,
  570. LPCWSTR subblock,
  571. LPVOID *buf,
  572. PUINT sizeout);
  573. static get_file_version_info_size_w_t get_file_version_info_size = NULL;
  574. static get_file_version_info_w_t get_file_version_info = NULL;
  575. static ver_query_value_w_t ver_query_value = NULL;
  576. static bool ver_initialized = false;
  577. static bool ver_initialize_success = false;
  578. static bool initialize_version_functions(void)
  579. {
  580. HMODULE ver = GetModuleHandleW(L"version");
  581. ver_initialized = true;
  582. if (!ver) {
  583. ver = LoadLibraryW(L"version");
  584. if (!ver) {
  585. blog(LOG_ERROR, "Failed to load windows "
  586. "version library");
  587. return false;
  588. }
  589. }
  590. get_file_version_info_size = (get_file_version_info_size_w_t)
  591. GetProcAddress(ver, "GetFileVersionInfoSizeW");
  592. get_file_version_info = (get_file_version_info_w_t)
  593. GetProcAddress(ver, "GetFileVersionInfoW");
  594. ver_query_value = (ver_query_value_w_t)
  595. GetProcAddress(ver, "VerQueryValueW");
  596. if (!get_file_version_info_size ||
  597. !get_file_version_info ||
  598. !ver_query_value) {
  599. blog(LOG_ERROR, "Failed to load windows version "
  600. "functions");
  601. return false;
  602. }
  603. ver_initialize_success = true;
  604. return true;
  605. }
  606. bool get_dll_ver(const wchar_t *lib, struct win_version_info *ver_info)
  607. {
  608. VS_FIXEDFILEINFO *info = NULL;
  609. UINT len = 0;
  610. BOOL success;
  611. LPVOID data;
  612. DWORD size;
  613. char utf8_lib[512];
  614. if (!ver_initialized && !initialize_version_functions())
  615. return false;
  616. if (!ver_initialize_success)
  617. return false;
  618. os_wcs_to_utf8(lib, 0, utf8_lib, sizeof(utf8_lib));
  619. size = get_file_version_info_size(lib, NULL);
  620. if (!size) {
  621. blog(LOG_ERROR, "Failed to get %s version info size", utf8_lib);
  622. return false;
  623. }
  624. data = bmalloc(size);
  625. if (!get_file_version_info(lib, 0, size, data)) {
  626. blog(LOG_ERROR, "Failed to get %s version info", utf8_lib);
  627. bfree(data);
  628. return false;
  629. }
  630. success = ver_query_value(data, L"\\", (LPVOID*)&info, &len);
  631. if (!success || !info || !len) {
  632. blog(LOG_ERROR, "Failed to get %s version info value", utf8_lib);
  633. bfree(data);
  634. return false;
  635. }
  636. ver_info->major = (int)HIWORD(info->dwFileVersionMS);
  637. ver_info->minor = (int)LOWORD(info->dwFileVersionMS);
  638. ver_info->build = (int)HIWORD(info->dwFileVersionLS);
  639. ver_info->revis = (int)LOWORD(info->dwFileVersionLS);
  640. bfree(data);
  641. return true;
  642. }
  643. bool is_64_bit_windows(void)
  644. {
  645. #if defined(_WIN64)
  646. return true;
  647. #elif defined(_WIN32)
  648. BOOL b64 = false;
  649. return IsWow64Process(GetCurrentProcess(), &b64) && b64;
  650. #endif
  651. }
  652. void get_reg_dword(HKEY hkey, LPCWSTR sub_key, LPCWSTR value_name,
  653. struct reg_dword *info)
  654. {
  655. struct reg_dword reg = {0};
  656. HKEY key;
  657. LSTATUS status;
  658. status = RegOpenKeyEx(hkey, sub_key, 0, KEY_READ, &key);
  659. if (status != ERROR_SUCCESS) {
  660. info->status = status;
  661. info->size = 0;
  662. info->return_value = 0;
  663. return;
  664. }
  665. reg.size = sizeof(reg.return_value);
  666. reg.status = RegQueryValueExW(key, value_name, NULL, NULL,
  667. (LPBYTE)&reg.return_value, &reg.size);
  668. RegCloseKey(key);
  669. *info = reg;
  670. }
  671. #define WINVER_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
  672. void get_win_ver(struct win_version_info *info)
  673. {
  674. static struct win_version_info ver = {0};
  675. static bool got_version = false;
  676. if (!info)
  677. return;
  678. if (!got_version) {
  679. get_dll_ver(L"kernel32", &ver);
  680. got_version = true;
  681. if (ver.major == 10) {
  682. HKEY key;
  683. DWORD size, win10_revision;
  684. LSTATUS status;
  685. status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
  686. WINVER_REG_KEY, &key);
  687. if (status != ERROR_SUCCESS)
  688. return;
  689. size = sizeof(win10_revision);
  690. status = RegQueryValueExW(key, L"UBR", NULL, NULL,
  691. (LPBYTE)&win10_revision, &size);
  692. if (status == ERROR_SUCCESS)
  693. ver.revis = (int)win10_revision > ver.revis ?
  694. (int)win10_revision : ver.revis;
  695. RegCloseKey(key);
  696. }
  697. }
  698. *info = ver;
  699. }
  700. uint32_t get_win_ver_int(void)
  701. {
  702. return get_winver();
  703. }
  704. struct os_inhibit_info {
  705. bool active;
  706. };
  707. os_inhibit_t *os_inhibit_sleep_create(const char *reason)
  708. {
  709. UNUSED_PARAMETER(reason);
  710. return bzalloc(sizeof(struct os_inhibit_info));
  711. }
  712. bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active)
  713. {
  714. if (!info)
  715. return false;
  716. if (info->active == active)
  717. return false;
  718. if (active) {
  719. SetThreadExecutionState(
  720. ES_CONTINUOUS |
  721. ES_SYSTEM_REQUIRED |
  722. ES_AWAYMODE_REQUIRED |
  723. ES_DISPLAY_REQUIRED);
  724. } else {
  725. SetThreadExecutionState(ES_CONTINUOUS);
  726. }
  727. info->active = active;
  728. return true;
  729. }
  730. void os_inhibit_sleep_destroy(os_inhibit_t *info)
  731. {
  732. if (info) {
  733. os_inhibit_sleep_set_active(info, false);
  734. bfree(info);
  735. }
  736. }
  737. void os_breakpoint(void)
  738. {
  739. __debugbreak();
  740. }
  741. DWORD num_logical_cores(ULONG_PTR mask)
  742. {
  743. DWORD left_shift = sizeof(ULONG_PTR) * 8 - 1;
  744. DWORD bit_set_count = 0;
  745. ULONG_PTR bit_test = (ULONG_PTR)1 << left_shift;
  746. for (DWORD i = 0; i <= left_shift; ++i) {
  747. bit_set_count += ((mask & bit_test) ? 1 : 0);
  748. bit_test /= 2;
  749. }
  750. return bit_set_count;
  751. }
  752. static int physical_cores = 0;
  753. static int logical_cores = 0;
  754. static bool core_count_initialized = false;
  755. static void os_get_cores_internal(void)
  756. {
  757. PSYSTEM_LOGICAL_PROCESSOR_INFORMATION info = NULL, temp = NULL;
  758. DWORD len = 0;
  759. if (core_count_initialized)
  760. return;
  761. core_count_initialized = true;
  762. GetLogicalProcessorInformation(info, &len);
  763. if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  764. return;
  765. info = malloc(len);
  766. if (GetLogicalProcessorInformation(info, &len)) {
  767. DWORD num = len / sizeof(*info);
  768. temp = info;
  769. for (DWORD i = 0; i < num; i++) {
  770. if (temp->Relationship == RelationProcessorCore) {
  771. ULONG_PTR mask = temp->ProcessorMask;
  772. physical_cores++;
  773. logical_cores += num_logical_cores(mask);
  774. }
  775. temp++;
  776. }
  777. }
  778. free(info);
  779. }
  780. int os_get_physical_cores(void)
  781. {
  782. if (!core_count_initialized)
  783. os_get_cores_internal();
  784. return physical_cores;
  785. }
  786. int os_get_logical_cores(void)
  787. {
  788. if (!core_count_initialized)
  789. os_get_cores_internal();
  790. return logical_cores;
  791. }
  792. static inline bool os_get_sys_memory_usage_internal(MEMORYSTATUSEX *msex)
  793. {
  794. if (!GlobalMemoryStatusEx(msex))
  795. return false;
  796. return true;
  797. }
  798. uint64_t os_get_sys_free_size(void)
  799. {
  800. MEMORYSTATUSEX msex = {sizeof(MEMORYSTATUSEX)};
  801. if (!os_get_sys_memory_usage_internal(&msex))
  802. return 0;
  803. return msex.ullAvailPhys;
  804. }
  805. static inline bool os_get_proc_memory_usage_internal(PROCESS_MEMORY_COUNTERS *pmc)
  806. {
  807. if (!GetProcessMemoryInfo(GetCurrentProcess(), pmc, sizeof(*pmc)))
  808. return false;
  809. return true;
  810. }
  811. bool os_get_proc_memory_usage(os_proc_memory_usage_t *usage)
  812. {
  813. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  814. if (!os_get_proc_memory_usage_internal(&pmc))
  815. return false;
  816. usage->resident_size = pmc.WorkingSetSize;
  817. usage->virtual_size = pmc.PagefileUsage;
  818. return true;
  819. }
  820. uint64_t os_get_proc_resident_size(void)
  821. {
  822. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  823. if (!os_get_proc_memory_usage_internal(&pmc))
  824. return 0;
  825. return pmc.WorkingSetSize;
  826. }
  827. uint64_t os_get_proc_virtual_size(void)
  828. {
  829. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  830. if (!os_get_proc_memory_usage_internal(&pmc))
  831. return 0;
  832. return pmc.PagefileUsage;
  833. }
  834. uint64_t os_get_free_disk_space(const char *dir)
  835. {
  836. wchar_t *wdir = NULL;
  837. if (!os_utf8_to_wcs_ptr(dir, 0, &wdir))
  838. return 0;
  839. ULARGE_INTEGER free;
  840. bool success = !!GetDiskFreeSpaceExW(wdir, &free, NULL, NULL);
  841. bfree(wdir);
  842. return success ? free.QuadPart : 0;
  843. }