Signed-off-by: Daniel Lehman <dlehman25(a)gmail.com>
---
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 +-
.../api-ms-win-core-timezone-l1-1-0.spec | 2 +-
dlls/kernelbase/kernelbase.spec | 2 +-
dlls/kernelbase/main.c | 134 ++++++++++++
dlls/kernelbase/tests/Makefile.in | 3 +-
dlls/kernelbase/tests/time.c | 200 ++++++++++++++++++
include/timezoneapi.h | 32 +++
7 files changed, 371 insertions(+), 4 deletions(-)
create mode 100644 dlls/kernelbase/tests/time.c
create mode 100644 include/timezoneapi.h
diff --git a/dlls/advapi32/advapi32.spec b/dlls/advapi32/advapi32.spec
index 91d6750658..ee9aba4057 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) kernelbase.EnumDynamicTimeZoneInformation
@ stub EnumServiceGroupA
@ stub EnumServiceGroupW
@ stdcall EnumServicesStatusA (long long long ptr long ptr ptr ptr)
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..0f42eda5d8 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) kernelbase.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 46163150c1..c366153971 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)
@ stdcall EnumLanguageGroupLocalesW(ptr long long ptr) kernel32.EnumLanguageGroupLocalesW
# @ stub EnumPageFilesA
# @ stub EnumPageFilesW
diff --git a/dlls/kernelbase/main.c b/dlls/kernelbase/main.c
index 3fce23657b..231a817a8e 100644
--- a/dlls/kernelbase/main.c
+++ b/dlls/kernelbase/main.c
@@ -165,3 +165,137 @@ HRESULT WINAPI QISearch(void *base, const QITAB *table, REFIID riid, void **obj)
*obj = NULL;
return E_NOINTERFACE;
}
+
+static BOOL reg_load_mui_string(HKEY hkey, LPCWSTR value, LPWSTR buffer, DWORD size)
+{
+ static const WCHAR advapi32W[] = {'a','d','v','a','p','i','3','2','.','d','l','l',0};
+ static DWORD (WINAPI *pRegLoadMUIStringW)(HKEY, LPCWSTR, LPWSTR, DWORD, DWORD *,
+ DWORD, LPCWSTR);
+ static WCHAR sysdir[MAX_PATH];
+ static HMODULE hDll;
+
+ if (!hDll)
+ {
+ hDll = LoadLibraryExW(advapi32W, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (hDll)
+ {
+ pRegLoadMUIStringW = (void *)GetProcAddress(hDll, "RegLoadMUIStringW");
+ GetSystemDirectoryW(sysdir, ARRAY_SIZE(sysdir));
+ }
+ }
+
+ if (pRegLoadMUIStringW &&
+ !pRegLoadMUIStringW(hkey, value, buffer, size, NULL, 0, sysdir))
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL reg_query_value(HKEY hkey, const WCHAR *name, DWORD type, void *data, DWORD count)
+{
+ char buf[256];
+ UNICODE_STRING nameW;
+ KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buf;
+
+ RtlInitUnicodeString( &nameW, name );
+ if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, buf, sizeof(buf), &count ))
+ return FALSE;
+
+ if (info->Type != type)
+ return FALSE;
+
+ memcpy( data, info->Data, info->DataLength );
+ return TRUE;
+}
+
+/******************************************************************************
+ * EnumDynamicTimeZoneInformation (KERNELBASE.@)
+ */
+DWORD WINAPI EnumDynamicTimeZoneInformation(const DWORD index,
+ DYNAMIC_TIME_ZONE_INFORMATION *dtzi)
+{
+ static const WCHAR Time_ZonesW[] = { '\\','R','E','G','I','S','T','R','Y','\\',
+ 'M','a','c','h','i','n','e','\\',
+ 'S','o','f','t','w','a','r','e','\\',
+ 'M','i','c','r','o','s','o','f','t','\\',
+ 'W','i','n','d','o','w','s',' ','N','T','\\',
+ 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+ 'T','i','m','e',' ','Z','o','n','e','s',0 };
+ static const WCHAR tziW[] = { 'T','Z','I',0 };
+ static const WCHAR stdW[] = { 'S','t','d',0 };
+ static const WCHAR dltW[] = { 'D','l','t',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 };
+ char node_buf[sizeof(KEY_NODE_INFORMATION) + ARRAY_SIZE(dtzi->TimeZoneKeyName)];
+ KEY_NODE_INFORMATION *info = (KEY_NODE_INFORMATION *)node_buf;
+ HANDLE time_zones_key, sub_key;
+ OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING nameW;
+ DWORD size;
+ NTSTATUS status;
+ 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;
+
+ attr.Length = sizeof(attr);
+ attr.RootDirectory = 0;
+ attr.ObjectName = &nameW;
+ attr.Attributes = 0;
+ attr.SecurityDescriptor = NULL;
+ attr.SecurityQualityOfService = NULL;
+ RtlInitUnicodeString( &nameW, Time_ZonesW );
+ status = NtOpenKeyEx( &time_zones_key, KEY_READ, &attr, KEY_ENUMERATE_SUB_KEYS );
+ if (status) return RtlNtStatusToDosError( status );
+
+ status = NtEnumerateKey( time_zones_key, index, KeyNodeInformation,
+ node_buf, sizeof(node_buf), &size );
+ if (status)
+ {
+ NtClose( time_zones_key );
+ return RtlNtStatusToDosError( status );
+ }
+
+ attr.RootDirectory = time_zones_key;
+ info->Name[info->NameLength / sizeof(WCHAR)] = 0;
+ RtlInitUnicodeString( &nameW, info->Name );
+ status = NtOpenKeyEx( &sub_key, KEY_READ, &attr, KEY_QUERY_VALUE );
+ if (status)
+ {
+ NtClose( time_zones_key );
+ return RtlNtStatusToDosError( status );
+ }
+
+ if (!reg_load_mui_string( sub_key, mui_stdW, dtzi->StandardName, sizeof(dtzi->StandardName) ) &&
+ !reg_query_value( sub_key, stdW, REG_SZ, dtzi->StandardName, sizeof(dtzi->StandardName) ))
+ goto done;
+
+ if (!reg_load_mui_string( sub_key, mui_dltW, dtzi->DaylightName, sizeof(dtzi->DaylightName) ) &&
+ !reg_query_value( sub_key, dltW, REG_SZ, dtzi->DaylightName, sizeof(dtzi->DaylightName) ))
+ goto done;
+
+ if (!reg_query_value( sub_key, tziW, REG_BINARY, &tz_data, sizeof(tz_data) ))
+ goto done;
+
+ 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, info->Name );
+ dtzi->DynamicDaylightTimeDisabled = FALSE;
+
+ SetLastError( ERROR_SUCCESS );
+
+done:
+ NtClose( sub_key );
+ NtClose( time_zones_key );
+ return ERROR_SUCCESS;
+}
diff --git a/dlls/kernelbase/tests/Makefile.in b/dlls/kernelbase/tests/Makefile.in
index 22e4a17a58..2006f5275b 100644
--- a/dlls/kernelbase/tests/Makefile.in
+++ b/dlls/kernelbase/tests/Makefile.in
@@ -2,4 +2,5 @@ TESTDLL = kernelbase.dll
C_SRCS = \
path.c \
- sync.c
+ sync.c \
+ time.c
diff --git a/dlls/kernelbase/tests/time.c b/dlls/kernelbase/tests/time.c
new file mode 100644
index 0000000000..8a1f8702f6
--- /dev/null
+++ b/dlls/kernelbase/tests/time.c
@@ -0,0 +1,200 @@
+/*
+ * Time tests
+ *
+ * Copyright 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
+ */
+
+#include <stdarg.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winreg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <winerror.h>
+#include <winuser.h>
+#include <winternl.h>
+
+#include "wine/test.h"
+
+static DWORD (WINAPI *pRegGetValueW)(HKEY,LPCWSTR,LPCWSTR,DWORD,DWORD*,void*,DWORD*);
+static DWORD (WINAPI *pRegLoadMUIStringW)(HKEY,LPCWSTR,LPWSTR,DWORD,DWORD*,DWORD,LPCWSTR);
+
+static DWORD (WINAPI *pEnumDynamicTimeZoneInformation)(const DWORD,
+ DYNAMIC_TIME_ZONE_INFORMATION*);
+
+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++];
+}
+
+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, sizeof(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(time)
+{
+ HMODULE hmod;
+
+ hmod = LoadLibraryA("advapi32.dll");
+ pRegLoadMUIStringW = (void *)GetProcAddress(hmod, "RegLoadMUIStringW");
+ pRegGetValueW = (void *)GetProcAddress(hmod, "RegGetValueW");
+
+ hmod = LoadLibraryA("kernelbase.dll");
+ pEnumDynamicTimeZoneInformation =
+ (void *)GetProcAddress(hmod, "EnumDynamicTimeZoneInformation");
+
+ test_EnumDynamicTimeZoneInformation();
+}
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_ */
--
2.17.1