1
0

platform-windows.c 24 KB

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