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