1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build windows
6
7package registry_test
8
9import (
10	"bytes"
11	"crypto/rand"
12	"os"
13	"syscall"
14	"testing"
15	"time"
16	"unsafe"
17
18	"golang.org/x/sys/windows/registry"
19)
20
21func randKeyName(prefix string) string {
22	const numbers = "0123456789"
23	buf := make([]byte, 10)
24	rand.Read(buf)
25	for i, b := range buf {
26		buf[i] = numbers[b%byte(len(numbers))]
27	}
28	return prefix + string(buf)
29}
30
31func TestReadSubKeyNames(t *testing.T) {
32	k, err := registry.OpenKey(registry.CLASSES_ROOT, "TypeLib", registry.ENUMERATE_SUB_KEYS)
33	if err != nil {
34		t.Fatal(err)
35	}
36	defer k.Close()
37
38	names, err := k.ReadSubKeyNames(-1)
39	if err != nil {
40		t.Fatal(err)
41	}
42	var foundStdOle bool
43	for _, name := range names {
44		// Every PC has "stdole 2.0 OLE Automation" library installed.
45		if name == "{00020430-0000-0000-C000-000000000046}" {
46			foundStdOle = true
47		}
48	}
49	if !foundStdOle {
50		t.Fatal("could not find stdole 2.0 OLE Automation")
51	}
52}
53
54func TestCreateOpenDeleteKey(t *testing.T) {
55	k, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
56	if err != nil {
57		t.Fatal(err)
58	}
59	defer k.Close()
60
61	testKName := randKeyName("TestCreateOpenDeleteKey_")
62
63	testK, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
64	if err != nil {
65		t.Fatal(err)
66	}
67	defer testK.Close()
68
69	if exist {
70		t.Fatalf("key %q already exists", testKName)
71	}
72
73	testKAgain, exist, err := registry.CreateKey(k, testKName, registry.CREATE_SUB_KEY)
74	if err != nil {
75		t.Fatal(err)
76	}
77	defer testKAgain.Close()
78
79	if !exist {
80		t.Fatalf("key %q should already exist", testKName)
81	}
82
83	testKOpened, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
84	if err != nil {
85		t.Fatal(err)
86	}
87	defer testKOpened.Close()
88
89	err = registry.DeleteKey(k, testKName)
90	if err != nil {
91		t.Fatal(err)
92	}
93
94	testKOpenedAgain, err := registry.OpenKey(k, testKName, registry.ENUMERATE_SUB_KEYS)
95	if err == nil {
96		defer testKOpenedAgain.Close()
97		t.Fatalf("key %q should already been deleted", testKName)
98	}
99	if err != registry.ErrNotExist {
100		t.Fatalf(`unexpected error ("not exist" expected): %v`, err)
101	}
102}
103
104func equalStringSlice(a, b []string) bool {
105	if len(a) != len(b) {
106		return false
107	}
108	if a == nil {
109		return true
110	}
111	for i := range a {
112		if a[i] != b[i] {
113			return false
114		}
115	}
116	return true
117}
118
119type ValueTest struct {
120	Type     uint32
121	Name     string
122	Value    interface{}
123	WillFail bool
124}
125
126var ValueTests = []ValueTest{
127	{Type: registry.SZ, Name: "String1", Value: ""},
128	{Type: registry.SZ, Name: "String2", Value: "\000", WillFail: true},
129	{Type: registry.SZ, Name: "String3", Value: "Hello World"},
130	{Type: registry.SZ, Name: "String4", Value: "Hello World\000", WillFail: true},
131	{Type: registry.EXPAND_SZ, Name: "ExpString1", Value: ""},
132	{Type: registry.EXPAND_SZ, Name: "ExpString2", Value: "\000", WillFail: true},
133	{Type: registry.EXPAND_SZ, Name: "ExpString3", Value: "Hello World"},
134	{Type: registry.EXPAND_SZ, Name: "ExpString4", Value: "Hello\000World", WillFail: true},
135	{Type: registry.EXPAND_SZ, Name: "ExpString5", Value: "%PATH%"},
136	{Type: registry.EXPAND_SZ, Name: "ExpString6", Value: "%NO_SUCH_VARIABLE%"},
137	{Type: registry.EXPAND_SZ, Name: "ExpString7", Value: "%PATH%;."},
138	{Type: registry.BINARY, Name: "Binary1", Value: []byte{}},
139	{Type: registry.BINARY, Name: "Binary2", Value: []byte{1, 2, 3}},
140	{Type: registry.BINARY, Name: "Binary3", Value: []byte{3, 2, 1, 0, 1, 2, 3}},
141	{Type: registry.DWORD, Name: "Dword1", Value: uint64(0)},
142	{Type: registry.DWORD, Name: "Dword2", Value: uint64(1)},
143	{Type: registry.DWORD, Name: "Dword3", Value: uint64(0xff)},
144	{Type: registry.DWORD, Name: "Dword4", Value: uint64(0xffff)},
145	{Type: registry.QWORD, Name: "Qword1", Value: uint64(0)},
146	{Type: registry.QWORD, Name: "Qword2", Value: uint64(1)},
147	{Type: registry.QWORD, Name: "Qword3", Value: uint64(0xff)},
148	{Type: registry.QWORD, Name: "Qword4", Value: uint64(0xffff)},
149	{Type: registry.QWORD, Name: "Qword5", Value: uint64(0xffffff)},
150	{Type: registry.QWORD, Name: "Qword6", Value: uint64(0xffffffff)},
151	{Type: registry.MULTI_SZ, Name: "MultiString1", Value: []string{"a", "b", "c"}},
152	{Type: registry.MULTI_SZ, Name: "MultiString2", Value: []string{"abc", "", "cba"}},
153	{Type: registry.MULTI_SZ, Name: "MultiString3", Value: []string{""}},
154	{Type: registry.MULTI_SZ, Name: "MultiString4", Value: []string{"abcdef"}},
155	{Type: registry.MULTI_SZ, Name: "MultiString5", Value: []string{"\000"}, WillFail: true},
156	{Type: registry.MULTI_SZ, Name: "MultiString6", Value: []string{"a\000b"}, WillFail: true},
157	{Type: registry.MULTI_SZ, Name: "MultiString7", Value: []string{"ab", "\000", "cd"}, WillFail: true},
158	{Type: registry.MULTI_SZ, Name: "MultiString8", Value: []string{"\000", "cd"}, WillFail: true},
159	{Type: registry.MULTI_SZ, Name: "MultiString9", Value: []string{"ab", "\000"}, WillFail: true},
160}
161
162func setValues(t *testing.T, k registry.Key) {
163	for _, test := range ValueTests {
164		var err error
165		switch test.Type {
166		case registry.SZ:
167			err = k.SetStringValue(test.Name, test.Value.(string))
168		case registry.EXPAND_SZ:
169			err = k.SetExpandStringValue(test.Name, test.Value.(string))
170		case registry.MULTI_SZ:
171			err = k.SetStringsValue(test.Name, test.Value.([]string))
172		case registry.BINARY:
173			err = k.SetBinaryValue(test.Name, test.Value.([]byte))
174		case registry.DWORD:
175			err = k.SetDWordValue(test.Name, uint32(test.Value.(uint64)))
176		case registry.QWORD:
177			err = k.SetQWordValue(test.Name, test.Value.(uint64))
178		default:
179			t.Fatalf("unsupported type %d for %s value", test.Type, test.Name)
180		}
181		if test.WillFail {
182			if err == nil {
183				t.Fatalf("setting %s value %q should fail, but succeeded", test.Name, test.Value)
184			}
185		} else {
186			if err != nil {
187				t.Fatal(err)
188			}
189		}
190	}
191}
192
193func enumerateValues(t *testing.T, k registry.Key) {
194	names, err := k.ReadValueNames(-1)
195	if err != nil {
196		t.Error(err)
197		return
198	}
199	haveNames := make(map[string]bool)
200	for _, n := range names {
201		haveNames[n] = false
202	}
203	for _, test := range ValueTests {
204		wantFound := !test.WillFail
205		_, haveFound := haveNames[test.Name]
206		if wantFound && !haveFound {
207			t.Errorf("value %s is not found while enumerating", test.Name)
208		}
209		if haveFound && !wantFound {
210			t.Errorf("value %s is found while enumerating, but expected to fail", test.Name)
211		}
212		if haveFound {
213			delete(haveNames, test.Name)
214		}
215	}
216	for n, v := range haveNames {
217		t.Errorf("value %s (%v) is found while enumerating, but has not been cretaed", n, v)
218	}
219}
220
221func testErrNotExist(t *testing.T, name string, err error) {
222	if err == nil {
223		t.Errorf("%s value should not exist", name)
224		return
225	}
226	if err != registry.ErrNotExist {
227		t.Errorf("reading %s value should return 'not exist' error, but got: %s", name, err)
228		return
229	}
230}
231
232func testErrUnexpectedType(t *testing.T, test ValueTest, gottype uint32, err error) {
233	if err == nil {
234		t.Errorf("GetXValue(%q) should not succeed", test.Name)
235		return
236	}
237	if err != registry.ErrUnexpectedType {
238		t.Errorf("reading %s value should return 'unexpected key value type' error, but got: %s", test.Name, err)
239		return
240	}
241	if gottype != test.Type {
242		t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
243		return
244	}
245}
246
247func testGetStringValue(t *testing.T, k registry.Key, test ValueTest) {
248	got, gottype, err := k.GetStringValue(test.Name)
249	if err != nil {
250		t.Errorf("GetStringValue(%s) failed: %v", test.Name, err)
251		return
252	}
253	if got != test.Value {
254		t.Errorf("want %s value %q, got %q", test.Name, test.Value, got)
255		return
256	}
257	if gottype != test.Type {
258		t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
259		return
260	}
261	if gottype == registry.EXPAND_SZ {
262		_, err = registry.ExpandString(got)
263		if err != nil {
264			t.Errorf("ExpandString(%s) failed: %v", got, err)
265			return
266		}
267	}
268}
269
270func testGetIntegerValue(t *testing.T, k registry.Key, test ValueTest) {
271	got, gottype, err := k.GetIntegerValue(test.Name)
272	if err != nil {
273		t.Errorf("GetIntegerValue(%s) failed: %v", test.Name, err)
274		return
275	}
276	if got != test.Value.(uint64) {
277		t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
278		return
279	}
280	if gottype != test.Type {
281		t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
282		return
283	}
284}
285
286func testGetBinaryValue(t *testing.T, k registry.Key, test ValueTest) {
287	got, gottype, err := k.GetBinaryValue(test.Name)
288	if err != nil {
289		t.Errorf("GetBinaryValue(%s) failed: %v", test.Name, err)
290		return
291	}
292	if !bytes.Equal(got, test.Value.([]byte)) {
293		t.Errorf("want %s value %v, got %v", test.Name, test.Value, got)
294		return
295	}
296	if gottype != test.Type {
297		t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
298		return
299	}
300}
301
302func testGetStringsValue(t *testing.T, k registry.Key, test ValueTest) {
303	got, gottype, err := k.GetStringsValue(test.Name)
304	if err != nil {
305		t.Errorf("GetStringsValue(%s) failed: %v", test.Name, err)
306		return
307	}
308	if !equalStringSlice(got, test.Value.([]string)) {
309		t.Errorf("want %s value %#v, got %#v", test.Name, test.Value, got)
310		return
311	}
312	if gottype != test.Type {
313		t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
314		return
315	}
316}
317
318func testGetValue(t *testing.T, k registry.Key, test ValueTest, size int) {
319	if size <= 0 {
320		return
321	}
322	// read data with no buffer
323	gotsize, gottype, err := k.GetValue(test.Name, nil)
324	if err != nil {
325		t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
326		return
327	}
328	if gotsize != size {
329		t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
330		return
331	}
332	if gottype != test.Type {
333		t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
334		return
335	}
336	// read data with short buffer
337	gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size-1))
338	if err == nil {
339		t.Errorf("GetValue(%s, [%d]byte) should fail, but succeeded", test.Name, size-1)
340		return
341	}
342	if err != registry.ErrShortBuffer {
343		t.Errorf("reading %s value should return 'short buffer' error, but got: %s", test.Name, err)
344		return
345	}
346	if gotsize != size {
347		t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
348		return
349	}
350	if gottype != test.Type {
351		t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
352		return
353	}
354	// read full data
355	gotsize, gottype, err = k.GetValue(test.Name, make([]byte, size))
356	if err != nil {
357		t.Errorf("GetValue(%s, [%d]byte) failed: %v", test.Name, size, err)
358		return
359	}
360	if gotsize != size {
361		t.Errorf("want %s value size of %d, got %v", test.Name, size, gotsize)
362		return
363	}
364	if gottype != test.Type {
365		t.Errorf("want %s value type %v, got %v", test.Name, test.Type, gottype)
366		return
367	}
368	// check GetValue returns ErrNotExist as required
369	_, _, err = k.GetValue(test.Name+"_not_there", make([]byte, size))
370	if err == nil {
371		t.Errorf("GetValue(%q) should not succeed", test.Name)
372		return
373	}
374	if err != registry.ErrNotExist {
375		t.Errorf("GetValue(%q) should return 'not exist' error, but got: %s", test.Name, err)
376		return
377	}
378}
379
380func testValues(t *testing.T, k registry.Key) {
381	for _, test := range ValueTests {
382		switch test.Type {
383		case registry.SZ, registry.EXPAND_SZ:
384			if test.WillFail {
385				_, _, err := k.GetStringValue(test.Name)
386				testErrNotExist(t, test.Name, err)
387			} else {
388				testGetStringValue(t, k, test)
389				_, gottype, err := k.GetIntegerValue(test.Name)
390				testErrUnexpectedType(t, test, gottype, err)
391				// Size of utf16 string in bytes is not perfect,
392				// but correct for current test values.
393				// Size also includes terminating 0.
394				testGetValue(t, k, test, (len(test.Value.(string))+1)*2)
395			}
396			_, _, err := k.GetStringValue(test.Name + "_string_not_created")
397			testErrNotExist(t, test.Name+"_string_not_created", err)
398		case registry.DWORD, registry.QWORD:
399			testGetIntegerValue(t, k, test)
400			_, gottype, err := k.GetBinaryValue(test.Name)
401			testErrUnexpectedType(t, test, gottype, err)
402			_, _, err = k.GetIntegerValue(test.Name + "_int_not_created")
403			testErrNotExist(t, test.Name+"_int_not_created", err)
404			size := 8
405			if test.Type == registry.DWORD {
406				size = 4
407			}
408			testGetValue(t, k, test, size)
409		case registry.BINARY:
410			testGetBinaryValue(t, k, test)
411			_, gottype, err := k.GetStringsValue(test.Name)
412			testErrUnexpectedType(t, test, gottype, err)
413			_, _, err = k.GetBinaryValue(test.Name + "_byte_not_created")
414			testErrNotExist(t, test.Name+"_byte_not_created", err)
415			testGetValue(t, k, test, len(test.Value.([]byte)))
416		case registry.MULTI_SZ:
417			if test.WillFail {
418				_, _, err := k.GetStringsValue(test.Name)
419				testErrNotExist(t, test.Name, err)
420			} else {
421				testGetStringsValue(t, k, test)
422				_, gottype, err := k.GetStringValue(test.Name)
423				testErrUnexpectedType(t, test, gottype, err)
424				size := 0
425				for _, s := range test.Value.([]string) {
426					size += len(s) + 1 // nil terminated
427				}
428				size += 1 // extra nil at the end
429				size *= 2 // count bytes, not uint16
430				testGetValue(t, k, test, size)
431			}
432			_, _, err := k.GetStringsValue(test.Name + "_strings_not_created")
433			testErrNotExist(t, test.Name+"_strings_not_created", err)
434		default:
435			t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
436			continue
437		}
438	}
439}
440
441func testStat(t *testing.T, k registry.Key) {
442	subk, _, err := registry.CreateKey(k, "subkey", registry.CREATE_SUB_KEY)
443	if err != nil {
444		t.Error(err)
445		return
446	}
447	defer subk.Close()
448
449	defer registry.DeleteKey(k, "subkey")
450
451	ki, err := k.Stat()
452	if err != nil {
453		t.Error(err)
454		return
455	}
456	if ki.SubKeyCount != 1 {
457		t.Error("key must have 1 subkey")
458	}
459	if ki.MaxSubKeyLen != 6 {
460		t.Error("key max subkey name length must be 6")
461	}
462	if ki.ValueCount != 24 {
463		t.Errorf("key must have 24 values, but is %d", ki.ValueCount)
464	}
465	if ki.MaxValueNameLen != 12 {
466		t.Errorf("key max value name length must be 10, but is %d", ki.MaxValueNameLen)
467	}
468	if ki.MaxValueLen != 38 {
469		t.Errorf("key max value length must be 38, but is %d", ki.MaxValueLen)
470	}
471	if mt, ct := ki.ModTime(), time.Now(); ct.Sub(mt) > 100*time.Millisecond {
472		t.Errorf("key mod time is not close to current time: mtime=%v current=%v delta=%v", mt, ct, ct.Sub(mt))
473	}
474}
475
476func deleteValues(t *testing.T, k registry.Key) {
477	for _, test := range ValueTests {
478		if test.WillFail {
479			continue
480		}
481		err := k.DeleteValue(test.Name)
482		if err != nil {
483			t.Error(err)
484			continue
485		}
486	}
487	names, err := k.ReadValueNames(-1)
488	if err != nil {
489		t.Error(err)
490		return
491	}
492	if len(names) != 0 {
493		t.Errorf("some values remain after deletion: %v", names)
494	}
495}
496
497func TestValues(t *testing.T) {
498	softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
499	if err != nil {
500		t.Fatal(err)
501	}
502	defer softwareK.Close()
503
504	testKName := randKeyName("TestValues_")
505
506	k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
507	if err != nil {
508		t.Fatal(err)
509	}
510	defer k.Close()
511
512	if exist {
513		t.Fatalf("key %q already exists", testKName)
514	}
515
516	defer registry.DeleteKey(softwareK, testKName)
517
518	setValues(t, k)
519
520	enumerateValues(t, k)
521
522	testValues(t, k)
523
524	testStat(t, k)
525
526	deleteValues(t, k)
527}
528
529func TestExpandString(t *testing.T) {
530	got, err := registry.ExpandString("%PATH%")
531	if err != nil {
532		t.Fatal(err)
533	}
534	want := os.Getenv("PATH")
535	if got != want {
536		t.Errorf("want %q string expanded, got %q", want, got)
537	}
538}
539
540func TestInvalidValues(t *testing.T) {
541	softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
542	if err != nil {
543		t.Fatal(err)
544	}
545	defer softwareK.Close()
546
547	testKName := randKeyName("TestInvalidValues_")
548
549	k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
550	if err != nil {
551		t.Fatal(err)
552	}
553	defer k.Close()
554
555	if exist {
556		t.Fatalf("key %q already exists", testKName)
557	}
558
559	defer registry.DeleteKey(softwareK, testKName)
560
561	var tests = []struct {
562		Type uint32
563		Name string
564		Data []byte
565	}{
566		{registry.DWORD, "Dword1", nil},
567		{registry.DWORD, "Dword2", []byte{1, 2, 3}},
568		{registry.QWORD, "Qword1", nil},
569		{registry.QWORD, "Qword2", []byte{1, 2, 3}},
570		{registry.QWORD, "Qword3", []byte{1, 2, 3, 4, 5, 6, 7}},
571		{registry.MULTI_SZ, "MultiString1", nil},
572		{registry.MULTI_SZ, "MultiString2", []byte{0}},
573		{registry.MULTI_SZ, "MultiString3", []byte{'a', 'b', 0}},
574		{registry.MULTI_SZ, "MultiString4", []byte{'a', 0, 0, 'b', 0}},
575		{registry.MULTI_SZ, "MultiString5", []byte{'a', 0, 0}},
576	}
577
578	for _, test := range tests {
579		err := k.SetValue(test.Name, test.Type, test.Data)
580		if err != nil {
581			t.Fatalf("SetValue for %q failed: %v", test.Name, err)
582		}
583	}
584
585	for _, test := range tests {
586		switch test.Type {
587		case registry.DWORD, registry.QWORD:
588			value, valType, err := k.GetIntegerValue(test.Name)
589			if err == nil {
590				t.Errorf("GetIntegerValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
591			}
592		case registry.MULTI_SZ:
593			value, valType, err := k.GetStringsValue(test.Name)
594			if err == nil {
595				if len(value) != 0 {
596					t.Errorf("GetStringsValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
597				}
598			}
599		default:
600			t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
601		}
602	}
603}
604
605func TestGetMUIStringValue(t *testing.T) {
606	if err := registry.LoadRegLoadMUIString(); err != nil {
607		t.Skip("regLoadMUIString not supported; skipping")
608	}
609	if err := procGetDynamicTimeZoneInformation.Find(); err != nil {
610		t.Skipf("%s not supported; skipping", procGetDynamicTimeZoneInformation.Name)
611	}
612	var dtzi DynamicTimezoneinformation
613	if _, err := GetDynamicTimeZoneInformation(&dtzi); err != nil {
614		t.Fatal(err)
615	}
616	tzKeyName := syscall.UTF16ToString(dtzi.TimeZoneKeyName[:])
617	timezoneK, err := registry.OpenKey(registry.LOCAL_MACHINE,
618		`SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\`+tzKeyName, registry.READ)
619	if err != nil {
620		t.Fatal(err)
621	}
622	defer timezoneK.Close()
623
624	type testType struct {
625		name string
626		want string
627	}
628	var tests = []testType{
629		{"MUI_Std", syscall.UTF16ToString(dtzi.StandardName[:])},
630	}
631	if dtzi.DynamicDaylightTimeDisabled == 0 {
632		tests = append(tests, testType{"MUI_Dlt", syscall.UTF16ToString(dtzi.DaylightName[:])})
633	}
634
635	for _, test := range tests {
636		got, err := timezoneK.GetMUIStringValue(test.name)
637		if err != nil {
638			t.Error("GetMUIStringValue:", err)
639		}
640
641		if got != test.want {
642			t.Errorf("GetMUIStringValue: %s: Got %q, want %q", test.name, got, test.want)
643		}
644	}
645}
646
647type DynamicTimezoneinformation struct {
648	Bias                        int32
649	StandardName                [32]uint16
650	StandardDate                syscall.Systemtime
651	StandardBias                int32
652	DaylightName                [32]uint16
653	DaylightDate                syscall.Systemtime
654	DaylightBias                int32
655	TimeZoneKeyName             [128]uint16
656	DynamicDaylightTimeDisabled uint8
657}
658
659var (
660	kernel32DLL = syscall.NewLazyDLL("kernel32")
661
662	procGetDynamicTimeZoneInformation = kernel32DLL.NewProc("GetDynamicTimeZoneInformation")
663)
664
665func GetDynamicTimeZoneInformation(dtzi *DynamicTimezoneinformation) (rc uint32, err error) {
666	r0, _, e1 := syscall.Syscall(procGetDynamicTimeZoneInformation.Addr(), 1, uintptr(unsafe.Pointer(dtzi)), 0, 0)
667	rc = uint32(r0)
668	if rc == 0xffffffff {
669		if e1 != 0 {
670			err = error(e1)
671		} else {
672			err = syscall.EINVAL
673		}
674	}
675	return
676}
677