Signed-off-by: Daniel Lehman dlehman25@gmail.com
--- v4: - move to advapi32 v3: - fix subject line v2: - return MUI strings (borrows from kernel32:TIME_GetSpecificTimeZoneInfo) - current test bots with languages are on Vista, which doesn't support EnumDynamicTimeZoneInformation tested manually with Spanish locale on Windows 10 1809
--- dlls/advapi32/advapi32.spec | 2 +- dlls/advapi32/registry.c | 64 ++++++++ dlls/advapi32/tests/registry.c | 154 ++++++++++++++++++ .../api-ms-win-core-timezone-l1-1-0.spec | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- include/timezoneapi.h | 32 ++++ 6 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 include/timezoneapi.h
diff --git a/dlls/advapi32/advapi32.spec b/dlls/advapi32/advapi32.spec index 91d6750658..a97161fa27 100644 --- a/dlls/advapi32/advapi32.spec +++ b/dlls/advapi32/advapi32.spec @@ -272,7 +272,7 @@ # @ stub EncryptionDisable @ stdcall EnumDependentServicesA(long long ptr long ptr ptr) @ stdcall EnumDependentServicesW(long long ptr long ptr ptr) -# @ stub EnumDynamicTimeZoneInformation +@ stdcall EnumDynamicTimeZoneInformation(long ptr) EnumDynamicTimeZoneInformation @ stub EnumServiceGroupA @ stub EnumServiceGroupW @ stdcall EnumServicesStatusA (long long long ptr long ptr ptr ptr) diff --git a/dlls/advapi32/registry.c b/dlls/advapi32/registry.c index c9b207daf3..9269898c6b 100644 --- a/dlls/advapi32/registry.c +++ b/dlls/advapi32/registry.c @@ -3699,3 +3699,67 @@ LSTATUS WINAPI RegLoadAppKeyW(const WCHAR *file, HKEY *result, REGSAM sam, DWORD *result = (HKEY)0xdeadbeef; return ERROR_SUCCESS; } + +/****************************************************************************** + * EnumDynamicTimeZoneInformation (ADVAPI32.@) + */ +DWORD WINAPI EnumDynamicTimeZoneInformation(const DWORD index, + DYNAMIC_TIME_ZONE_INFORMATION *dtzi) +{ + static const WCHAR mui_stdW[] = { 'M','U','I','_','S','t','d',0 }; + static const WCHAR mui_dltW[] = { 'M','U','I','_','D','l','t',0 }; + WCHAR keyname[ARRAY_SIZE(dtzi->TimeZoneKeyName)]; + HKEY time_zones_key, sub_key; + WCHAR sysdir[MAX_PATH]; + LSTATUS ret; + DWORD size; + struct tz_reg_data + { + LONG bias; + LONG std_bias; + LONG dlt_bias; + SYSTEMTIME std_date; + SYSTEMTIME dlt_date; + } tz_data; + + if (!dtzi) + return ERROR_INVALID_PARAMETER; + + ret = RegOpenKeyExA( HKEY_LOCAL_MACHINE, + "Software\Microsoft\Windows NT\CurrentVersion\Time Zones", 0, + KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &time_zones_key ); + if (ret) return ret; + + sub_key = NULL; + ret = RegEnumKeyW( time_zones_key, index, keyname, ARRAY_SIZE(keyname) ); + if (ret) goto cleanup; + + ret = RegOpenKeyExW( time_zones_key, keyname, 0, KEY_QUERY_VALUE, &sub_key ); + if (ret) goto cleanup; + + GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir)); + size = sizeof(dtzi->StandardName); + ret = RegLoadMUIStringW( sub_key, mui_stdW, dtzi->StandardName, size, NULL, 0, sysdir ); + if (ret) goto cleanup; + + size = sizeof(dtzi->DaylightName); + ret = RegLoadMUIStringW( sub_key, mui_dltW, dtzi->DaylightName, size, NULL, 0, sysdir ); + if (ret) goto cleanup; + + size = sizeof(tz_data); + ret = RegQueryValueExA( sub_key, "TZI", NULL, NULL, (BYTE*)&tz_data, &size ); + if (ret) goto cleanup; + + dtzi->Bias = tz_data.bias; + dtzi->StandardBias = tz_data.std_bias; + dtzi->DaylightBias = tz_data.dlt_bias; + memcpy( &dtzi->StandardDate, &tz_data.std_date, sizeof(tz_data.std_date) ); + memcpy( &dtzi->DaylightDate, &tz_data.dlt_date, sizeof(tz_data.dlt_date) ); + lstrcpyW( dtzi->TimeZoneKeyName, keyname ); + dtzi->DynamicDaylightTimeDisabled = FALSE; + +cleanup: + if (sub_key) RegCloseKey( sub_key ); + RegCloseKey( time_zones_key ); + return ret; +} diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c index 8a2fb3b7a5..6b199bc4d3 100644 --- a/dlls/advapi32/tests/registry.c +++ b/dlls/advapi32/tests/registry.c @@ -42,6 +42,7 @@ static const char * sTestpath2 = "%FOO%\subdir1"; static const DWORD ptr_size = 8 * sizeof(void*);
static DWORD (WINAPI *pRegGetValueA)(HKEY,LPCSTR,LPCSTR,DWORD,LPDWORD,PVOID,LPDWORD); +static DWORD (WINAPI *pRegGetValueW)(HKEY,LPCWSTR,LPCWSTR,DWORD,LPDWORD,PVOID,LPDWORD); static LONG (WINAPI *pRegCopyTreeA)(HKEY,const char *,HKEY); static LONG (WINAPI *pRegDeleteTreeA)(HKEY,const char *); static DWORD (WINAPI *pRegDeleteKeyExA)(HKEY,LPCSTR,REGSAM,DWORD); @@ -53,6 +54,8 @@ static LONG (WINAPI *pRegDeleteKeyValueA)(HKEY,LPCSTR,LPCSTR); static LONG (WINAPI *pRegSetKeyValueW)(HKEY,LPCWSTR,LPCWSTR,DWORD,const void*,DWORD); static LONG (WINAPI *pRegLoadMUIStringA)(HKEY,LPCSTR,LPSTR,DWORD,LPDWORD,DWORD,LPCSTR); static LONG (WINAPI *pRegLoadMUIStringW)(HKEY,LPCWSTR,LPWSTR,DWORD,LPDWORD,DWORD,LPCWSTR); +static DWORD (WINAPI *pEnumDynamicTimeZoneInformation)(const DWORD, + DYNAMIC_TIME_ZONE_INFORMATION*);
static BOOL limited_user;
@@ -128,6 +131,18 @@ static const char *wine_debugstr_an( const char *str, int n ) return res; }
+static const char *dbgstr_SYSTEMTIME(const SYSTEMTIME *st) +{ + static int index; + static char buf[2][64]; + + index %= ARRAY_SIZE(buf); + sprintf(buf[index], "%02d-%02d-%04d %02d:%02d:%02d.%03d", + st->wMonth, st->wDay, st->wYear, + st->wHour, st->wMinute, st->wSecond, st->wMilliseconds); + return buf[index++]; +} + #define ADVAPI32_GET_PROC(func) \ p ## func = (void*)GetProcAddress(hadvapi32, #func)
@@ -139,6 +154,7 @@ static void InitFunctionPtrs(void)
/* This function was introduced with Windows 2003 SP1 */ ADVAPI32_GET_PROC(RegGetValueA); + ADVAPI32_GET_PROC(RegGetValueW); ADVAPI32_GET_PROC(RegCopyTreeA); ADVAPI32_GET_PROC(RegDeleteTreeA); ADVAPI32_GET_PROC(RegDeleteKeyExA); @@ -146,6 +162,7 @@ static void InitFunctionPtrs(void) ADVAPI32_GET_PROC(RegSetKeyValueW); ADVAPI32_GET_PROC(RegLoadMUIStringA); ADVAPI32_GET_PROC(RegLoadMUIStringW); + ADVAPI32_GET_PROC(EnumDynamicTimeZoneInformation);
pIsWow64Process = (void *)GetProcAddress( hkernel32, "IsWow64Process" ); pRtlFormatCurrentUserKeyPath = (void *)GetProcAddress( hntdll, "RtlFormatCurrentUserKeyPath" ); @@ -4033,6 +4050,142 @@ static void test_RegLoadMUIString(void) SetEnvironmentVariableA("WineMuiDat", NULL); }
+static void test_EnumDynamicTimeZoneInformation(void) +{ + LSTATUS status; + HKEY key, subkey; + WCHAR name[32]; + WCHAR keyname[128]; + WCHAR sysdir[MAX_PATH]; + DWORD index, ret, gle, size; + DYNAMIC_TIME_ZONE_INFORMATION bogus_dtzi, dtzi; + static const WCHAR stdW[] = {'S','t','d',0}; + static const WCHAR dltW[] = {'D','l','t',0}; + static const WCHAR tziW[] = {'T','Z','I',0}; + static const WCHAR mui_stdW[] = {'M','U','I','_','S','t','d',0}; + static const WCHAR mui_dltW[] = {'M','U','I','_','D','l','t',0}; + struct tz_reg_data + { + LONG bias; + LONG std_bias; + LONG dlt_bias; + SYSTEMTIME std_date; + SYSTEMTIME dlt_date; + } tz_data; + + if (!pEnumDynamicTimeZoneInformation) + { + win_skip("EnumDynamicTimeZoneInformation is not supported.\n"); + return; + } + + if (pRegLoadMUIStringW) + GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir)); + + SetLastError(0xdeadbeef); + ret = pEnumDynamicTimeZoneInformation(0, NULL); + gle = GetLastError(); + ok(gle == 0xdeadbeef, "got 0x%x\n", gle); + ok(ret == ERROR_INVALID_PARAMETER, "got %d\n", ret); + + memset(&bogus_dtzi, 0xcc, sizeof(bogus_dtzi)); + memset(&dtzi, 0xcc, sizeof(dtzi)); + SetLastError(0xdeadbeef); + ret = pEnumDynamicTimeZoneInformation(-1, &dtzi); + gle = GetLastError(); + ok(gle == 0xdeadbeef, "got 0x%x\n", gle); + ok(ret == ERROR_NO_MORE_ITEMS, "got %d\n", ret); + ok(!memcmp(&dtzi, &bogus_dtzi, sizeof(dtzi)), "mismatch\n"); + + status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "Software\Microsoft\Windows NT\CurrentVersion\Time Zones", 0, + KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &key); + ok(status == ERROR_SUCCESS, "got %d\n", status); + index = 0; + while (!(status = RegEnumKeyW(key, index, keyname, ARRAY_SIZE(keyname)))) + { + subkey = NULL; + status = RegOpenKeyExW(key, keyname, 0, KEY_QUERY_VALUE, &subkey); + ok(status == ERROR_SUCCESS, "got %d\n", status); + + memset(&dtzi, 0xcc, sizeof(dtzi)); + SetLastError(0xdeadbeef); + ret = pEnumDynamicTimeZoneInformation(index, &dtzi); + gle = GetLastError(); + /* recently added time zones may not have MUI strings */ + ok(gle == ERROR_SUCCESS || + gle == ERROR_RESOURCE_TYPE_NOT_FOUND /* Win10 1809 32-bit */ || + gle == ERROR_MUI_FILE_NOT_FOUND /* Win10 1809 64-bit */, + "got 0x%x\n", gle); + ok(ret == ERROR_SUCCESS, "got %d\n", ret); + ok(!lstrcmpW(dtzi.TimeZoneKeyName, keyname), "expected %s, got %s\n", + wine_dbgstr_w(keyname), wine_dbgstr_w(dtzi.TimeZoneKeyName)); + + if (gle == ERROR_SUCCESS) + { + size = sizeof(name); + memset(name, 0, sizeof(name)); + if (pRegLoadMUIStringW) + status = pRegLoadMUIStringW(subkey, mui_stdW, name, size, &size, 0, sysdir); + else + status = pRegGetValueW(subkey, NULL, stdW, RRF_RT_REG_SZ, NULL, name, &size); + ok(status == ERROR_SUCCESS, "status %d name %s\n", status, wine_dbgstr_w(name)); + ok(!memcmp(&dtzi.StandardName, name, size), + "expected %s, got %s\n", wine_dbgstr_w(name), wine_dbgstr_w(dtzi.StandardName)); + + size = sizeof(name); + memset(name, 0, sizeof(name)); + if (pRegLoadMUIStringW) + status = pRegLoadMUIStringW(subkey, mui_dltW, name, size, &size, 0, sysdir); + else + status = pRegGetValueW(subkey, NULL, dltW, RRF_RT_REG_SZ, NULL, name, &size); + ok(status == ERROR_SUCCESS, "status %d name %s\n", status, wine_dbgstr_w(name)); + ok(!memcmp(&dtzi.DaylightName, name, size), + "expected %s, got %s\n", wine_dbgstr_w(name), wine_dbgstr_w(dtzi.DaylightName)); + } + else + { + ok(!dtzi.StandardName[0], "expected empty StandardName\n"); + ok(!dtzi.DaylightName[0], "expected empty DaylightName\n"); + } + + ok(!dtzi.DynamicDaylightTimeDisabled, "got %d\n", dtzi.DynamicDaylightTimeDisabled); + + size = sizeof(tz_data); + status = pRegGetValueW(key, keyname, tziW, RRF_RT_REG_BINARY, NULL, &tz_data, &size); + ok(status == ERROR_SUCCESS, "got %d\n", status); + + ok(dtzi.Bias == tz_data.bias, "expected %d, got %d\n", + tz_data.bias, dtzi.Bias); + ok(dtzi.StandardBias == tz_data.std_bias, "expected %d, got %d\n", + tz_data.std_bias, dtzi.StandardBias); + ok(dtzi.DaylightBias == tz_data.dlt_bias, "expected %d, got %d\n", + tz_data.dlt_bias, dtzi.DaylightBias); + + ok(!memcmp(&dtzi.StandardDate, &tz_data.std_date, sizeof(dtzi.StandardDate)), + "expected %s, got %s\n", + dbgstr_SYSTEMTIME(&tz_data.std_date), dbgstr_SYSTEMTIME(&dtzi.StandardDate)); + + ok(!memcmp(&dtzi.DaylightDate, &tz_data.dlt_date, sizeof(dtzi.DaylightDate)), + "expected %s, got %s\n", + dbgstr_SYSTEMTIME(&tz_data.dlt_date), dbgstr_SYSTEMTIME(&dtzi.DaylightDate)); + + RegCloseKey(subkey); + index++; + } + ok(status == ERROR_NO_MORE_ITEMS, "got %d\n", status); + + memset(&dtzi, 0xcc, sizeof(dtzi)); + SetLastError(0xdeadbeef); + ret = pEnumDynamicTimeZoneInformation(index, &dtzi); + gle = GetLastError(); + ok(gle == 0xdeadbeef, "got 0x%x\n", gle); + ok(ret == ERROR_NO_MORE_ITEMS, "got %d\n", ret); + ok(!memcmp(&dtzi, &bogus_dtzi, sizeof(dtzi)), "mismatch\n"); + + RegCloseKey(key); +} + START_TEST(registry) { /* Load pointers for functions that are not available in all Windows versions */ @@ -4070,6 +4223,7 @@ START_TEST(registry) test_RegNotifyChangeKeyValue(); test_RegQueryValueExPerformanceData(); test_RegLoadMUIString(); + test_EnumDynamicTimeZoneInformation();
/* cleanup */ delete_key( hkey_main ); diff --git a/dlls/api-ms-win-core-timezone-l1-1-0/api-ms-win-core-timezone-l1-1-0.spec b/dlls/api-ms-win-core-timezone-l1-1-0/api-ms-win-core-timezone-l1-1-0.spec index 3e38d70596..b25d121ad1 100644 --- a/dlls/api-ms-win-core-timezone-l1-1-0/api-ms-win-core-timezone-l1-1-0.spec +++ b/dlls/api-ms-win-core-timezone-l1-1-0/api-ms-win-core-timezone-l1-1-0.spec @@ -1,4 +1,4 @@ -@ stub EnumDynamicTimeZoneInformation +@ stdcall EnumDynamicTimeZoneInformation(long ptr) advapi32.EnumDynamicTimeZoneInformation @ stdcall FileTimeToSystemTime(ptr ptr) kernel32.FileTimeToSystemTime @ stdcall GetDynamicTimeZoneInformation(ptr) kernel32.GetDynamicTimeZoneInformation @ stdcall GetDynamicTimeZoneInformationEffectiveYears(ptr ptr ptr) kernel32.GetDynamicTimeZoneInformationEffectiveYears diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index c22365624d..07a40419ed 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -297,7 +297,7 @@ @ stdcall EnumDateFormatsExW(ptr long long) kernel32.EnumDateFormatsExW @ stdcall EnumDateFormatsW(ptr long long) kernel32.EnumDateFormatsW # @ stub EnumDeviceDrivers -# @ stub EnumDynamicTimeZoneInformation +@ stdcall EnumDynamicTimeZoneInformation(long ptr) advapi32.EnumDynamicTimeZoneInformation @ stdcall EnumLanguageGroupLocalesW(ptr long long ptr) kernel32.EnumLanguageGroupLocalesW # @ stub EnumPageFilesA # @ stub EnumPageFilesW diff --git a/include/timezoneapi.h b/include/timezoneapi.h new file mode 100644 index 0000000000..0bba897718 --- /dev/null +++ b/include/timezoneapi.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Daniel Lehman + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef _APISETTIMEZONE_ +#define _APISETTIMEZONE_ + +#ifdef __cplusplus +extern "C" { +#endif + +WINBASEAPI DWORD WINAPI EnumDynamicTimeZoneInformation(const DWORD, DYNAMIC_TIME_ZONE_INFORMATION *); + +#ifdef __cplusplus +} +#endif + +#endif /* _APISETTIMEZONE_ */