platform-windows.c 24 KB

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