github.com/knieriem/gointernal@v0.2.0-pre2/internal/syscall/windows/registry/value.go (about)

     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
     8  
     9  import (
    10  	"errors"
    11  	"syscall"
    12  	"unicode/utf16"
    13  	"unsafe"
    14  )
    15  
    16  const (
    17  	// Registry value types.
    18  	NONE                       = 0
    19  	SZ                         = 1
    20  	EXPAND_SZ                  = 2
    21  	BINARY                     = 3
    22  	DWORD                      = 4
    23  	DWORD_BIG_ENDIAN           = 5
    24  	LINK                       = 6
    25  	MULTI_SZ                   = 7
    26  	RESOURCE_LIST              = 8
    27  	FULL_RESOURCE_DESCRIPTOR   = 9
    28  	RESOURCE_REQUIREMENTS_LIST = 10
    29  	QWORD                      = 11
    30  )
    31  
    32  var (
    33  	// ErrShortBuffer is returned when the buffer was too short for the operation.
    34  	ErrShortBuffer = syscall.ERROR_MORE_DATA
    35  
    36  	// ErrNotExist is returned when a registry key or value does not exist.
    37  	ErrNotExist = syscall.ERROR_FILE_NOT_FOUND
    38  
    39  	// ErrUnexpectedType is returned by Get*Value when the value's type was unexpected.
    40  	ErrUnexpectedType = errors.New("unexpected key value type")
    41  )
    42  
    43  // GetValue retrieves the type and data for the specified value associated
    44  // with an open key k. It fills up buffer buf and returns the retrieved
    45  // byte count n. If buf is too small to fit the stored value it returns
    46  // ErrShortBuffer error along with the required buffer size n.
    47  // If no buffer is provided, it returns true and actual buffer size n.
    48  // If no buffer is provided, GetValue returns the value's type only.
    49  // If the value does not exist, the error returned is ErrNotExist.
    50  //
    51  // GetValue is a low level function. If value's type is known, use the appropriate
    52  // Get*Value function instead.
    53  func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
    54  	pname, err := syscall.UTF16PtrFromString(name)
    55  	if err != nil {
    56  		return 0, 0, err
    57  	}
    58  	var pbuf *byte
    59  	if len(buf) > 0 {
    60  		pbuf = (*byte)(unsafe.Pointer(&buf[0]))
    61  	}
    62  	l := uint32(len(buf))
    63  	err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l)
    64  	if err != nil {
    65  		return int(l), valtype, err
    66  	}
    67  	return int(l), valtype, nil
    68  }
    69  
    70  func (k Key) getValue(name string, buf []byte) (date []byte, valtype uint32, err error) {
    71  	p, err := syscall.UTF16PtrFromString(name)
    72  	if err != nil {
    73  		return nil, 0, err
    74  	}
    75  	var t uint32
    76  	n := uint32(len(buf))
    77  	for {
    78  		err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n)
    79  		if err == nil {
    80  			return buf[:n], t, nil
    81  		}
    82  		if err != syscall.ERROR_MORE_DATA {
    83  			return nil, 0, err
    84  		}
    85  		if n <= uint32(len(buf)) {
    86  			return nil, 0, err
    87  		}
    88  		buf = make([]byte, n)
    89  	}
    90  }
    91  
    92  // GetStringValue retrieves the string value for the specified
    93  // value name associated with an open key k. It also returns the value's type.
    94  // If value does not exist, GetStringValue returns ErrNotExist.
    95  // If value is not SZ or EXPAND_SZ, it will return the correct value
    96  // type and ErrUnexpectedType.
    97  func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
    98  	data, typ, err2 := k.getValue(name, make([]byte, 64))
    99  	if err2 != nil {
   100  		return "", typ, err2
   101  	}
   102  	switch typ {
   103  	case SZ, EXPAND_SZ:
   104  	default:
   105  		return "", typ, ErrUnexpectedType
   106  	}
   107  	if len(data) == 0 {
   108  		return "", typ, nil
   109  	}
   110  	u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2]
   111  	return syscall.UTF16ToString(u), typ, nil
   112  }
   113  
   114  // GetMUIStringValue retrieves the localized string value for
   115  // the specified value name associated with an open key k.
   116  // If the value name doesn't exist or the localized string value
   117  // can't be resolved, GetMUIStringValue returns ErrNotExist.
   118  // GetMUIStringValue panics if the system doesn't support
   119  // regLoadMUIString; use LoadRegLoadMUIString to check if
   120  // regLoadMUIString is supported before calling this function.
   121  func (k Key) GetMUIStringValue(name string) (string, error) {
   122  	pname, err := syscall.UTF16PtrFromString(name)
   123  	if err != nil {
   124  		return "", err
   125  	}
   126  
   127  	buf := make([]uint16, 1024)
   128  	var buflen uint32
   129  	var pdir *uint16
   130  
   131  	err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
   132  	if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path
   133  
   134  		// Try to resolve the string value using the system directory as
   135  		// a DLL search path; this assumes the string value is of the form
   136  		// @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320.
   137  
   138  		// This approach works with tzres.dll but may have to be revised
   139  		// in the future to allow callers to provide custom search paths.
   140  
   141  		var s string
   142  		s, err = ExpandString("%SystemRoot%\\system32\\")
   143  		if err != nil {
   144  			return "", err
   145  		}
   146  		pdir, err = syscall.UTF16PtrFromString(s)
   147  		if err != nil {
   148  			return "", err
   149  		}
   150  
   151  		err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
   152  	}
   153  
   154  	for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed
   155  		if buflen <= uint32(len(buf)) {
   156  			break // Buffer not growing, assume race; break
   157  		}
   158  		buf = make([]uint16, buflen)
   159  		err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
   160  	}
   161  
   162  	if err != nil {
   163  		return "", err
   164  	}
   165  
   166  	return syscall.UTF16ToString(buf), nil
   167  }
   168  
   169  // ExpandString expands environment-variable strings and replaces
   170  // them with the values defined for the current user.
   171  // Use ExpandString to expand EXPAND_SZ strings.
   172  func ExpandString(value string) (string, error) {
   173  	if value == "" {
   174  		return "", nil
   175  	}
   176  	p, err := syscall.UTF16PtrFromString(value)
   177  	if err != nil {
   178  		return "", err
   179  	}
   180  	r := make([]uint16, 100)
   181  	for {
   182  		n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r)))
   183  		if err != nil {
   184  			return "", err
   185  		}
   186  		if n <= uint32(len(r)) {
   187  			return syscall.UTF16ToString(r[:n]), nil
   188  		}
   189  		r = make([]uint16, n)
   190  	}
   191  }
   192  
   193  // GetStringsValue retrieves the []string value for the specified
   194  // value name associated with an open key k. It also returns the value's type.
   195  // If value does not exist, GetStringsValue returns ErrNotExist.
   196  // If value is not MULTI_SZ, it will return the correct value
   197  // type and ErrUnexpectedType.
   198  func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
   199  	data, typ, err2 := k.getValue(name, make([]byte, 64))
   200  	if err2 != nil {
   201  		return nil, typ, err2
   202  	}
   203  	if typ != MULTI_SZ {
   204  		return nil, typ, ErrUnexpectedType
   205  	}
   206  	if len(data) == 0 {
   207  		return nil, typ, nil
   208  	}
   209  	p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2]
   210  	if len(p) == 0 {
   211  		return nil, typ, nil
   212  	}
   213  	if p[len(p)-1] == 0 {
   214  		p = p[:len(p)-1] // remove terminating null
   215  	}
   216  	val = make([]string, 0, 5)
   217  	from := 0
   218  	for i, c := range p {
   219  		if c == 0 {
   220  			val = append(val, string(utf16.Decode(p[from:i])))
   221  			from = i + 1
   222  		}
   223  	}
   224  	return val, typ, nil
   225  }
   226  
   227  // GetIntegerValue retrieves the integer value for the specified
   228  // value name associated with an open key k. It also returns the value's type.
   229  // If value does not exist, GetIntegerValue returns ErrNotExist.
   230  // If value is not DWORD or QWORD, it will return the correct value
   231  // type and ErrUnexpectedType.
   232  func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
   233  	data, typ, err2 := k.getValue(name, make([]byte, 8))
   234  	if err2 != nil {
   235  		return 0, typ, err2
   236  	}
   237  	switch typ {
   238  	case DWORD:
   239  		if len(data) != 4 {
   240  			return 0, typ, errors.New("DWORD value is not 4 bytes long")
   241  		}
   242  		return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil
   243  	case QWORD:
   244  		if len(data) != 8 {
   245  			return 0, typ, errors.New("QWORD value is not 8 bytes long")
   246  		}
   247  		return uint64(*(*uint64)(unsafe.Pointer(&data[0]))), QWORD, nil
   248  	default:
   249  		return 0, typ, ErrUnexpectedType
   250  	}
   251  }
   252  
   253  // GetBinaryValue retrieves the binary value for the specified
   254  // value name associated with an open key k. It also returns the value's type.
   255  // If value does not exist, GetBinaryValue returns ErrNotExist.
   256  // If value is not BINARY, it will return the correct value
   257  // type and ErrUnexpectedType.
   258  func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
   259  	data, typ, err2 := k.getValue(name, make([]byte, 64))
   260  	if err2 != nil {
   261  		return nil, typ, err2
   262  	}
   263  	if typ != BINARY {
   264  		return nil, typ, ErrUnexpectedType
   265  	}
   266  	return data, typ, nil
   267  }
   268  
   269  func (k Key) setValue(name string, valtype uint32, data []byte) error {
   270  	p, err := syscall.UTF16PtrFromString(name)
   271  	if err != nil {
   272  		return err
   273  	}
   274  	if len(data) == 0 {
   275  		return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0)
   276  	}
   277  	return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data)))
   278  }
   279  
   280  // SetDWordValue sets the data and type of a name value
   281  // under key k to value and DWORD.
   282  func (k Key) SetDWordValue(name string, value uint32) error {
   283  	return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:])
   284  }
   285  
   286  // SetQWordValue sets the data and type of a name value
   287  // under key k to value and QWORD.
   288  func (k Key) SetQWordValue(name string, value uint64) error {
   289  	return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:])
   290  }
   291  
   292  func (k Key) setStringValue(name string, valtype uint32, value string) error {
   293  	v, err := syscall.UTF16FromString(value)
   294  	if err != nil {
   295  		return err
   296  	}
   297  	buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2]
   298  	return k.setValue(name, valtype, buf)
   299  }
   300  
   301  // SetStringValue sets the data and type of a name value
   302  // under key k to value and SZ. The value must not contain a zero byte.
   303  func (k Key) SetStringValue(name, value string) error {
   304  	return k.setStringValue(name, SZ, value)
   305  }
   306  
   307  // SetExpandStringValue sets the data and type of a name value
   308  // under key k to value and EXPAND_SZ. The value must not contain a zero byte.
   309  func (k Key) SetExpandStringValue(name, value string) error {
   310  	return k.setStringValue(name, EXPAND_SZ, value)
   311  }
   312  
   313  // SetStringsValue sets the data and type of a name value
   314  // under key k to value and MULTI_SZ. The value strings
   315  // must not contain a zero byte.
   316  func (k Key) SetStringsValue(name string, value []string) error {
   317  	ss := ""
   318  	for _, s := range value {
   319  		for i := 0; i < len(s); i++ {
   320  			if s[i] == 0 {
   321  				return errors.New("string cannot have 0 inside")
   322  			}
   323  		}
   324  		ss += s + "\x00"
   325  	}
   326  	v := utf16.Encode([]rune(ss + "\x00"))
   327  	buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2]
   328  	return k.setValue(name, MULTI_SZ, buf)
   329  }
   330  
   331  // SetBinaryValue sets the data and type of a name value
   332  // under key k to value and BINARY.
   333  func (k Key) SetBinaryValue(name string, value []byte) error {
   334  	return k.setValue(name, BINARY, value)
   335  }
   336  
   337  // DeleteValue removes a named value from the key k.
   338  func (k Key) DeleteValue(name string) error {
   339  	return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name))
   340  }
   341  
   342  // ReadValueNames returns the value names of key k.
   343  func (k Key) ReadValueNames() ([]string, error) {
   344  	ki, err := k.Stat()
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  	names := make([]string, 0, ki.ValueCount)
   349  	buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character
   350  loopItems:
   351  	for i := uint32(0); ; i++ {
   352  		l := uint32(len(buf))
   353  		for {
   354  			err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
   355  			if err == nil {
   356  				break
   357  			}
   358  			if err == syscall.ERROR_MORE_DATA {
   359  				// Double buffer size and try again.
   360  				l = uint32(2 * len(buf))
   361  				buf = make([]uint16, l)
   362  				continue
   363  			}
   364  			if err == _ERROR_NO_MORE_ITEMS {
   365  				break loopItems
   366  			}
   367  			return names, err
   368  		}
   369  		names = append(names, syscall.UTF16ToString(buf[:l]))
   370  	}
   371  	return names, nil
   372  }