platform-windows.c 21 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  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 << 16) | 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. struct os_inhibit_info {
  701. bool active;
  702. };
  703. os_inhibit_t *os_inhibit_sleep_create(const char *reason)
  704. {
  705. UNUSED_PARAMETER(reason);
  706. return bzalloc(sizeof(struct os_inhibit_info));
  707. }
  708. bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active)
  709. {
  710. if (!info)
  711. return false;
  712. if (info->active == active)
  713. return false;
  714. if (active) {
  715. SetThreadExecutionState(
  716. ES_CONTINUOUS |
  717. ES_SYSTEM_REQUIRED |
  718. ES_AWAYMODE_REQUIRED |
  719. ES_DISPLAY_REQUIRED);
  720. } else {
  721. SetThreadExecutionState(ES_CONTINUOUS);
  722. }
  723. info->active = active;
  724. return true;
  725. }
  726. void os_inhibit_sleep_destroy(os_inhibit_t *info)
  727. {
  728. if (info) {
  729. os_inhibit_sleep_set_active(info, false);
  730. bfree(info);
  731. }
  732. }
  733. void os_breakpoint(void)
  734. {
  735. __debugbreak();
  736. }
  737. DWORD num_logical_cores(ULONG_PTR mask)
  738. {
  739. DWORD left_shift = sizeof(ULONG_PTR) * 8 - 1;
  740. DWORD bit_set_count = 0;
  741. ULONG_PTR bit_test = (ULONG_PTR)1 << left_shift;
  742. for (DWORD i = 0; i <= left_shift; ++i) {
  743. bit_set_count += ((mask & bit_test) ? 1 : 0);
  744. bit_test /= 2;
  745. }
  746. return bit_set_count;
  747. }
  748. static int physical_cores = 0;
  749. static int logical_cores = 0;
  750. static bool core_count_initialized = false;
  751. static void os_get_cores_internal(void)
  752. {
  753. PSYSTEM_LOGICAL_PROCESSOR_INFORMATION info = NULL, temp = NULL;
  754. DWORD len = 0;
  755. if (core_count_initialized)
  756. return;
  757. core_count_initialized = true;
  758. GetLogicalProcessorInformation(info, &len);
  759. if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  760. return;
  761. info = malloc(len);
  762. if (GetLogicalProcessorInformation(info, &len)) {
  763. DWORD num = len / sizeof(*info);
  764. temp = info;
  765. for (DWORD i = 0; i < num; i++) {
  766. if (temp->Relationship == RelationProcessorCore) {
  767. ULONG_PTR mask = temp->ProcessorMask;
  768. physical_cores++;
  769. logical_cores += num_logical_cores(mask);
  770. }
  771. temp++;
  772. }
  773. }
  774. free(info);
  775. }
  776. int os_get_physical_cores(void)
  777. {
  778. if (!core_count_initialized)
  779. os_get_cores_internal();
  780. return physical_cores;
  781. }
  782. int os_get_logical_cores(void)
  783. {
  784. if (!core_count_initialized)
  785. os_get_cores_internal();
  786. return logical_cores;
  787. }
  788. static inline bool os_get_sys_memory_usage_internal(MEMORYSTATUSEX *msex)
  789. {
  790. if (!GlobalMemoryStatusEx(msex))
  791. return false;
  792. return true;
  793. }
  794. uint64_t os_get_sys_free_size(void)
  795. {
  796. MEMORYSTATUSEX msex = {sizeof(MEMORYSTATUSEX)};
  797. if (!os_get_sys_memory_usage_internal(&msex))
  798. return 0;
  799. return msex.ullAvailPhys;
  800. }
  801. static inline bool os_get_proc_memory_usage_internal(PROCESS_MEMORY_COUNTERS *pmc)
  802. {
  803. if (!GetProcessMemoryInfo(GetCurrentProcess(), pmc, sizeof(*pmc)))
  804. return false;
  805. return true;
  806. }
  807. bool os_get_proc_memory_usage(os_proc_memory_usage_t *usage)
  808. {
  809. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  810. if (!os_get_proc_memory_usage_internal(&pmc))
  811. return false;
  812. usage->resident_size = pmc.WorkingSetSize;
  813. usage->virtual_size = pmc.PagefileUsage;
  814. return true;
  815. }
  816. uint64_t os_get_proc_resident_size(void)
  817. {
  818. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  819. if (!os_get_proc_memory_usage_internal(&pmc))
  820. return 0;
  821. return pmc.WorkingSetSize;
  822. }
  823. uint64_t os_get_proc_virtual_size(void)
  824. {
  825. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  826. if (!os_get_proc_memory_usage_internal(&pmc))
  827. return 0;
  828. return pmc.PagefileUsage;
  829. }
  830. uint64_t os_get_free_disk_space(const char *dir)
  831. {
  832. wchar_t *wdir = NULL;
  833. if (!os_utf8_to_wcs_ptr(dir, 0, &wdir))
  834. return 0;
  835. ULARGE_INTEGER free;
  836. bool success = !!GetDiskFreeSpaceExW(wdir, &free, NULL, NULL);
  837. bfree(wdir);
  838. return success ? free.QuadPart : 0;
  839. }