From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id D84715764A7; Wed, 9 Aug 2023 18:44:58 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org D84715764A7 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1691595898; bh=eJe8SwkOysD6XZkxRCpW7+sr/+yFAaGkALllgHVX+ls=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=sfDwPg0msWmVqSGtEDZbjCIpJGDazfJ2TnOrL+Thw6zkc1flVHfEVCVIno0c0hq0F UVy/FMJuxD+T9T0P6FCCZSvDLHVFU49L35bNA/QPmMiBGDLRgWoFnC+3uAgv01rSjf bVnLFNyGqTHcpC1hL8qp8NWNdiLC1kNIIFYbKMrU= Received: from smtp32.i.mail.ru (smtp32.i.mail.ru [95.163.41.73]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id DB16B57648C for ; Wed, 9 Aug 2023 18:41:05 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org DB16B57648C Received: by smtp32.i.mail.ru with esmtpa (envelope-from ) id 1qTlJ3-003Nbf-0O; Wed, 09 Aug 2023 18:41:05 +0300 To: Igor Munkin , Sergey Bronnikov Date: Wed, 9 Aug 2023 18:35:57 +0300 Message-ID: <86adbae13323277ea291b7e3a709fc166086fbd3.1691592488.git.skaplun@tarantool.org> X-Mailer: git-send-email 2.41.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Mailru-Src: smtp X-4EC0790: 10 X-7564579A: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD969E04B5EED670DC864E28ABE09E133B4A5846C191BD37C58182A05F5380850401673E9BACCCBDFFAE8F4881418B56D3E624F5A704C0C9E6E7ABAEA48DB38A585 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE71BDE6A359BD5B800EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637993F4D0874F024C68638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D8CE692872F996CF0D3EB2CB77F7B698FC117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCCF01C05423B8DB1BA471835C12D1D9774AD6D5ED66289B52BA9C0B312567BB23117882F4460429728776938767073520B1593CA6EC85F86DF04B652EEC242312D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EE437C869540D2AB0FC8623B8F170C382FD8FC6C240DEA76429C9F4D5AE37F343AA9539A8B242431040A6AB1C7CE11FEE3DA532D2019E286A76136E347CC761E07C4224003CC836476E2F48590F00D11D6E2021AF6380DFAD1A18204E546F3947CB11811A4A51E3B096D1867E19FE1407959CC434672EE6371089D37D7C0E48F6C8AA50765F7900637B8F435DEDE9E76EBEFF80C71ABB335746BA297DBC24807EABDAD6C7F3747799A X-B7AD71C0: 4965CFDFE0519134C1FE400A9E48C5401DD40DE57556AFB266D16FC5F53507A1816E0A2A8F779BBED8D40077074E805C66D16FC5F53507A117535B0CF9F6D0C3EE9D5CB6078CC77C0DB680660F69848C50B8A193DD8A991A X-C1DE0DAB: 0D63561A33F958A5B8A302BBC6C054DD2F6BEBBAB7CE8134A88BB39C071A5889F87CCE6106E1FC07E67D4AC08A07B9B0F254576263B31EA99C5DF10A05D560A950611B66E3DA6D700B0A020F03D25A0997E3FB2386030E77 X-C8649E89: 1C3962B70DF3F0AD75DCE07D45A749953FED46C3ACD6F73ED3581295AF09D3DF87807E0823442EA2ED31085941D9CD0AF7F820E7B07EA4CF28A8A6A8335E9F919BABF0A8C42975576AA88B04ED7113CB37F5DBABF3FDE5C2498A0925D89034501529AA6C8ABD04CA5D239940B87B16D1D3ED68D8A413A77EA74DFFEFA5DC0E7F02C26D483E81D6BE5EF9655DD6DEA7D65774BB76CC95456EEC5B5AD62611EEC62B5AFB4261A09AF0 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojUzxoxvtYX2rpvIITrZgrKg== X-Mailru-Sender: 11C2EC085EDE56FAC07928AF2646A769620852B510C55C74E8F4881418B56D3E7958A49548C749F6DEDBA653FF35249392D99EB8CC7091A70E183A470755BFD208F19895AA18418972D6B4FCE48DF648AE208404248635DF X-Mras: Ok Subject: [Tarantool-patches] [PATCH luajit 08/19] Windows: Add UWP support, part 1. X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Kaplun via Tarantool-patches Reply-To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" From: Mike Pall Contributed by Ben Pye. (cherry-picked from commit c3c54ce1aef782823936808a75460e6b53aada2c) This patch adds partial support for the Universal Windows Platform [1] in LuaJIT. This includes: * `LJ_TARGET_UWP` is introduced to mark that target is Universal Windows Platform. * `LJ_WIN_VALLOC()` macro is introduced to use instead of `VirtualAlloc()` [2] (`VirtualAllocFromApp()` [3] for UWP) * `LJ_WIN_VPROTECT()` macro is introduced to use instead of `VirtualProtect()` [4] (`VirtualProtectFromApp()` [5] for UWP) * `LJ_WIN_LOADLIBA()` macro is introduced to use instead of `LoadLibraryExA()` [6] (custom implementation using `LoadPackagedLibrary()` [7] for UWP). Note that the following features are not implemented for UWP: * `io.popen()`. * LuaJIT profiler's (`jit.p`) timer for Windows has not very high resolution since `timeBeginPeriod()` [8] and `timeEndPeriod()` [9] are not used, because the library isn't loaded. [1]: https://learn.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide [2]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc [3]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocfromapp [4]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect [5]: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotectfromapp [6]: https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexa [7]: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-loadpackagedlibrary [8]: https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod [9]: https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timeendperiod Sergey Kaplun: * added the description for the feature Part of tarantool/tarantool#8825 --- doc/ext_ffi_api.html | 2 ++ src/lib_ffi.c | 3 +++ src/lib_io.c | 4 ++-- src/lib_package.c | 24 +++++++++++++++++++++++- src/lj_alloc.c | 6 +++--- src/lj_arch.h | 19 +++++++++++++++++++ src/lj_ccallback.c | 4 ++-- src/lj_clib.c | 20 ++++++++++++++++---- src/lj_mcode.c | 8 ++++---- src/lj_profile_timer.c | 8 ++++---- 10 files changed, 78 insertions(+), 20 deletions(-) diff --git a/doc/ext_ffi_api.html b/doc/ext_ffi_api.html index 91af2e1d..c72191d1 100644 --- a/doc/ext_ffi_api.html +++ b/doc/ext_ffi_api.html @@ -469,6 +469,8 @@ otherwise. The following parameters are currently defined: winWindows variant of the standard ABI +uwpUniversal Windows Platform + gc6464 bit GC references diff --git a/src/lib_ffi.c b/src/lib_ffi.c index 136e98e8..d1fe1a14 100644 --- a/src/lib_ffi.c +++ b/src/lib_ffi.c @@ -746,6 +746,9 @@ LJLIB_CF(ffi_abi) LJLIB_REC(.) #endif #if LJ_ABI_WIN case H_(4ab624a8,4ab624a8): b = 1; break; /* win */ +#endif +#if LJ_TARGET_UWP + case H_(a40f0bcb,a40f0bcb): b = 1; break; /* uwp */ #endif case H_(3af93066,1f001464): b = 1; break; /* le/be */ #if LJ_GC64 diff --git a/src/lib_io.c b/src/lib_io.c index f0108227..db995ae6 100644 --- a/src/lib_io.c +++ b/src/lib_io.c @@ -99,7 +99,7 @@ static int io_file_close(lua_State *L, IOFileUD *iof) int stat = -1; #if LJ_TARGET_POSIX stat = pclose(iof->fp); -#elif LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE +#elif LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP stat = _pclose(iof->fp); #else lua_assert(0); @@ -414,7 +414,7 @@ LJLIB_CF(io_open) LJLIB_CF(io_popen) { -#if LJ_TARGET_POSIX || (LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE) +#if LJ_TARGET_POSIX || (LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP) const char *fname = strdata(lj_lib_checkstr(L, 1)); GCstr *s = lj_lib_optstr(L, 2); const char *mode = s ? strdata(s) : "r"; diff --git a/src/lib_package.c b/src/lib_package.c index 67959a10..b49f0209 100644 --- a/src/lib_package.c +++ b/src/lib_package.c @@ -76,6 +76,20 @@ static const char *ll_bcsym(void *lib, const char *sym) BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*); #endif +#if LJ_TARGET_UWP +void *LJ_WIN_LOADLIBA(const char *path) +{ + DWORD err = GetLastError(); + wchar_t wpath[256]; + HANDLE lib = NULL; + if (MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, 256) > 0) { + lib = LoadPackagedLibrary(wpath, 0); + } + SetLastError(err); + return lib; +} +#endif + #undef setprogdir static void setprogdir(lua_State *L) @@ -119,7 +133,7 @@ static void ll_unloadlib(void *lib) static void *ll_load(lua_State *L, const char *path, int gl) { - HINSTANCE lib = LoadLibraryExA(path, NULL, 0); + HINSTANCE lib = LJ_WIN_LOADLIBA(path); if (lib == NULL) pusherror(L); UNUSED(gl); return lib; @@ -132,17 +146,25 @@ static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) return f; } +#if LJ_TARGET_UWP +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +#endif + static const char *ll_bcsym(void *lib, const char *sym) { if (lib) { return (const char *)GetProcAddress((HINSTANCE)lib, sym); } else { +#if LJ_TARGET_UWP + return (const char *)GetProcAddress((HINSTANCE)&__ImageBase, sym); +#else HINSTANCE h = GetModuleHandleA(NULL); const char *p = (const char *)GetProcAddress(h, sym); if (p == NULL && GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const char *)ll_bcsym, &h)) p = (const char *)GetProcAddress(h, sym); return p; +#endif } } diff --git a/src/lj_alloc.c b/src/lj_alloc.c index f7039b5b..9e2fb1f6 100644 --- a/src/lj_alloc.c +++ b/src/lj_alloc.c @@ -167,7 +167,7 @@ static void *DIRECT_MMAP(size_t size) static void *CALL_MMAP(size_t size) { DWORD olderr = GetLastError(); - void *ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + void *ptr = LJ_WIN_VALLOC(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); SetLastError(olderr); return ptr ? ptr : MFAIL; } @@ -176,8 +176,8 @@ static void *CALL_MMAP(size_t size) static void *DIRECT_MMAP(size_t size) { DWORD olderr = GetLastError(); - void *ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, - PAGE_READWRITE); + void *ptr = LJ_WIN_VALLOC(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); SetLastError(olderr); return ptr ? ptr : MFAIL; } diff --git a/src/lj_arch.h b/src/lj_arch.h index 7397492e..0351e046 100644 --- a/src/lj_arch.h +++ b/src/lj_arch.h @@ -141,6 +141,13 @@ #define LJ_TARGET_GC64 1 #endif +#ifdef _UWP +#define LJ_TARGET_UWP 1 +#if LUAJIT_TARGET == LUAJIT_ARCH_X64 +#define LJ_TARGET_GC64 1 +#endif +#endif + #define LJ_NUMMODE_SINGLE 0 /* Single-number mode only. */ #define LJ_NUMMODE_SINGLE_DUAL 1 /* Default to single-number mode. */ #define LJ_NUMMODE_DUAL 2 /* Dual-number mode only. */ @@ -586,6 +593,18 @@ #define LJ_ABI_WIN 0 #endif +#if LJ_TARGET_WINDOWS +#if LJ_TARGET_UWP +#define LJ_WIN_VALLOC VirtualAllocFromApp +#define LJ_WIN_VPROTECT VirtualProtectFromApp +extern void *LJ_WIN_LOADLIBA(const char *path); +#else +#define LJ_WIN_VALLOC VirtualAlloc +#define LJ_WIN_VPROTECT VirtualProtect +#define LJ_WIN_LOADLIBA(path) LoadLibraryExA((path), NULL, 0) +#endif +#endif + #if defined(LUAJIT_NO_UNWIND) || __GNU_COMPACT_EH__ || defined(__symbian__) || LJ_TARGET_IOS || LJ_TARGET_PS3 || LJ_TARGET_PS4 #define LJ_NO_UNWIND 1 #endif diff --git a/src/lj_ccallback.c b/src/lj_ccallback.c index c33190d7..37edd00f 100644 --- a/src/lj_ccallback.c +++ b/src/lj_ccallback.c @@ -267,7 +267,7 @@ static void callback_mcode_new(CTState *cts) if (CALLBACK_MAX_SLOT == 0) lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); #if LJ_TARGET_WINDOWS - p = VirtualAlloc(NULL, sz, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + p = LJ_WIN_VALLOC(NULL, sz, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); if (!p) lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV); #elif LJ_TARGET_POSIX @@ -285,7 +285,7 @@ static void callback_mcode_new(CTState *cts) #if LJ_TARGET_WINDOWS { DWORD oprot; - VirtualProtect(p, sz, PAGE_EXECUTE_READ, &oprot); + LJ_WIN_VPROTECT(p, sz, PAGE_EXECUTE_READ, &oprot); } #elif LJ_TARGET_POSIX mprotect(p, sz, (PROT_READ|PROT_EXEC)); diff --git a/src/lj_clib.c b/src/lj_clib.c index c06c0915..a8672052 100644 --- a/src/lj_clib.c +++ b/src/lj_clib.c @@ -158,11 +158,13 @@ BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*); /* Default libraries. */ enum { CLIB_HANDLE_EXE, +#if !LJ_TARGET_UWP CLIB_HANDLE_DLL, CLIB_HANDLE_CRT, CLIB_HANDLE_KERNEL32, CLIB_HANDLE_USER32, CLIB_HANDLE_GDI32, +#endif CLIB_HANDLE_MAX }; @@ -208,7 +210,7 @@ static const char *clib_extname(lua_State *L, const char *name) static void *clib_loadlib(lua_State *L, const char *name, int global) { DWORD oldwerr = GetLastError(); - void *h = (void *)LoadLibraryExA(clib_extname(L, name), NULL, 0); + void *h = LJ_WIN_LOADLIBA(clib_extname(L, name)); if (!h) clib_error(L, "cannot load module " LUA_QS ": %s", name); SetLastError(oldwerr); UNUSED(global); @@ -218,6 +220,7 @@ static void *clib_loadlib(lua_State *L, const char *name, int global) static void clib_unloadlib(CLibrary *cl) { if (cl->handle == CLIB_DEFHANDLE) { +#if !LJ_TARGET_UWP MSize i; for (i = CLIB_HANDLE_KERNEL32; i < CLIB_HANDLE_MAX; i++) { void *h = clib_def_handle[i]; @@ -226,11 +229,16 @@ static void clib_unloadlib(CLibrary *cl) FreeLibrary((HINSTANCE)h); } } +#endif } else if (cl->handle) { FreeLibrary((HINSTANCE)cl->handle); } } +#if LJ_TARGET_UWP +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +#endif + static void *clib_getsym(CLibrary *cl, const char *name) { void *p = NULL; @@ -239,6 +247,9 @@ static void *clib_getsym(CLibrary *cl, const char *name) for (i = 0; i < CLIB_HANDLE_MAX; i++) { HINSTANCE h = (HINSTANCE)clib_def_handle[i]; if (!(void *)h) { /* Resolve default library handles (once). */ +#if LJ_TARGET_UWP + h = (HINSTANCE)&__ImageBase; +#else switch (i) { case CLIB_HANDLE_EXE: GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, NULL, &h); break; case CLIB_HANDLE_DLL: @@ -249,11 +260,12 @@ static void *clib_getsym(CLibrary *cl, const char *name) GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (const char *)&_fmode, &h); break; - case CLIB_HANDLE_KERNEL32: h = LoadLibraryExA("kernel32.dll", NULL, 0); break; - case CLIB_HANDLE_USER32: h = LoadLibraryExA("user32.dll", NULL, 0); break; - case CLIB_HANDLE_GDI32: h = LoadLibraryExA("gdi32.dll", NULL, 0); break; + case CLIB_HANDLE_KERNEL32: h = LJ_WIN_LOADLIBA("kernel32.dll"); break; + case CLIB_HANDLE_USER32: h = LJ_WIN_LOADLIBA("user32.dll"); break; + case CLIB_HANDLE_GDI32: h = LJ_WIN_LOADLIBA("gdi32.dll"); break; } if (!h) continue; +#endif clib_def_handle[i] = (void *)h; } p = (void *)GetProcAddress(h, name); diff --git a/src/lj_mcode.c b/src/lj_mcode.c index c6361018..10db4457 100644 --- a/src/lj_mcode.c +++ b/src/lj_mcode.c @@ -66,8 +66,8 @@ void lj_mcode_sync(void *start, void *end) static void *mcode_alloc_at(jit_State *J, uintptr_t hint, size_t sz, DWORD prot) { - void *p = VirtualAlloc((void *)hint, sz, - MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, prot); + void *p = LJ_WIN_VALLOC((void *)hint, sz, + MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, prot); if (!p && !hint) lj_trace_err(J, LJ_TRERR_MCODEAL); return p; @@ -82,7 +82,7 @@ static void mcode_free(jit_State *J, void *p, size_t sz) static int mcode_setprot(void *p, size_t sz, DWORD prot) { DWORD oprot; - return !VirtualProtect(p, sz, prot, &oprot); + return !LJ_WIN_VPROTECT(p, sz, prot, &oprot); } #elif LJ_TARGET_POSIX @@ -255,7 +255,7 @@ static void *mcode_alloc(jit_State *J, size_t sz) /* All memory addresses are reachable by relative jumps. */ static void *mcode_alloc(jit_State *J, size_t sz) { -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) || LJ_TARGET_UWP /* Allow better executable memory allocation for OpenBSD W^X mode. */ void *p = mcode_alloc_at(J, 0, sz, MCPROT_RUN); if (p && mcode_setprot(p, sz, MCPROT_GEN)) { diff --git a/src/lj_profile_timer.c b/src/lj_profile_timer.c index 056fd1f7..0b859457 100644 --- a/src/lj_profile_timer.c +++ b/src/lj_profile_timer.c @@ -84,7 +84,7 @@ static DWORD WINAPI timer_thread(void *timerx) { lj_profile_timer *timer = (lj_profile_timer *)timerx; int interval = timer->opt.interval_msec; -#if LJ_TARGET_WINDOWS +#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP timer->wmm_tbp(interval); #endif while (1) { @@ -92,7 +92,7 @@ static DWORD WINAPI timer_thread(void *timerx) if (timer->abort) break; timer->opt.handler(); } -#if LJ_TARGET_WINDOWS +#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP timer->wmm_tep(interval); #endif return 0; @@ -101,9 +101,9 @@ static DWORD WINAPI timer_thread(void *timerx) /* Start profiling timer thread. */ void lj_profile_timer_start(lj_profile_timer *timer) { -#if LJ_TARGET_WINDOWS +#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP if (!timer->wmm) { /* Load WinMM library on-demand. */ - timer->wmm = LoadLibraryExA("winmm.dll", NULL, 0); + timer->wmm = LJ_WIN_LOADLIBA("winmm.dll"); if (timer->wmm) { timer->wmm_tbp = (WMM_TPFUNC)GetProcAddress(timer->wmm, "timeBeginPeriod"); -- 2.41.0