Source file src/internal/syscall/windows/registry/registry_test.go

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

View as plain text