github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/registry/value.go (about)

     1  //go:build windows
     2  // +build windows
     3  
     4  // Copyright (C) 2020 - 2023 iDigitalFlame
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU General Public License as published by
     8  // the Free Software Foundation, either version 3 of the License, or
     9  // any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU General Public License
    17  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    18  //
    19  
    20  package registry
    21  
    22  import (
    23  	"syscall"
    24  	"unsafe"
    25  
    26  	"github.com/iDigitalFlame/xmt/device/winapi"
    27  	"github.com/iDigitalFlame/xmt/util"
    28  )
    29  
    30  const (
    31  	// ErrNotExist is returned when a registry key or value does not exist.
    32  	ErrNotExist = syscall.ERROR_FILE_NOT_FOUND
    33  	// ErrShortBuffer is returned when the buffer was too short for the operation.
    34  	ErrShortBuffer = syscall.ERROR_MORE_DATA
    35  )
    36  
    37  // SetString sets the data and type of a named value under the key to the
    38  // supplied value and SZ.
    39  //
    40  // The value must not contain a zero byte.
    41  func (k Key) SetString(n, v string) error {
    42  	return k.setStringValue(n, TypeString, v)
    43  }
    44  
    45  // SetExpandString sets the data and type of a named value under the key
    46  // to the supplied value and EXPAND_SZ.
    47  //
    48  // The value must not contain a zero byte.
    49  func (k Key) SetExpandString(n, v string) error {
    50  	return k.setStringValue(n, TypeExpandString, v)
    51  }
    52  
    53  // SetDWord sets the data and type of a named value under the key to the
    54  // supplied value and DWORD.
    55  func (k Key) SetDWord(n string, v uint32) error {
    56  	return k.setValue(n, TypeDword, (*[4]byte)(unsafe.Pointer(&v))[:])
    57  }
    58  
    59  // SetQWord sets the data and type of a named value under the key to the
    60  // supplied value and QWORD.
    61  func (k Key) SetQWord(n string, v uint64) error {
    62  	return k.setValue(n, TypeQword, (*[8]byte)(unsafe.Pointer(&v))[:])
    63  }
    64  
    65  // SetBinary sets the data and type of a name value under key k to value and
    66  // BINARY.
    67  func (k Key) SetBinary(n string, v []byte) error {
    68  	return k.setValue(n, TypeBinary, v)
    69  }
    70  
    71  // SetStrings sets the data and type of a named value under the key to the
    72  // supplied value and MULTI_SZ.
    73  //
    74  // The value strings must not contain a zero byte.
    75  func (k Key) SetStrings(n string, v []string) error {
    76  	var b util.Builder
    77  	for i := range v {
    78  		for x := range v[i] {
    79  			if v[i][x] == 0 {
    80  				return syscall.EINVAL
    81  			}
    82  		}
    83  		b.WriteString(v[i] + "\x00")
    84  	}
    85  	var (
    86  		r = winapi.UTF16EncodeStd([]rune(b.Output() + "\x00"))
    87  		o = (*[1 << 29]byte)(unsafe.Pointer(&r[0]))[: len(r)*2 : len(r)*2]
    88  	)
    89  	return k.setValue(n, TypeStringList, o)
    90  }
    91  
    92  // String retrieves the string value for the specified value name
    93  // associated with the open key. It also returns the value's type.
    94  //
    95  // If value does not exist, String returns ErrNotExist.
    96  //
    97  // If value is not SZ or EXPAND_SZ, it will return the correct value type and
    98  // ErrUnexpectedType.
    99  func (k Key) String(s string) (string, uint32, error) {
   100  	d, t, err := k.getValue(s, make([]byte, 64))
   101  	if err != nil {
   102  		return "", t, err
   103  	}
   104  	switch t {
   105  	case TypeString, TypeExpandString:
   106  	default:
   107  		return "", t, ErrUnexpectedType
   108  	}
   109  	if len(d) == 0 {
   110  		return "", t, nil
   111  	}
   112  	u := (*[1 << 29]uint16)(unsafe.Pointer(&d[0]))[: len(d)/2 : len(d)/2]
   113  	return winapi.UTF16ToString(u), t, nil
   114  }
   115  
   116  // Binary retrieves the binary value for the specified value name
   117  // associated with the open key. It also returns the value's type.
   118  //
   119  // If value does not exist, Binary returns ErrNotExist.
   120  //
   121  // If value is not BINARY, it will return the correct value type and
   122  // ErrUnexpectedType.
   123  func (k Key) Binary(s string) ([]byte, uint32, error) {
   124  	d, t, err := k.getValue(s, make([]byte, 64))
   125  	if err != nil {
   126  		return nil, t, err
   127  	}
   128  	if t != TypeBinary {
   129  		return nil, t, ErrUnexpectedType
   130  	}
   131  	return d, t, nil
   132  }
   133  
   134  // Integer retrieves the integer value for the specified value name
   135  // associated with the open key. It also returns the value's type.
   136  //
   137  // If value does not exist, Integer returns ErrNotExist.
   138  //
   139  // If value is not DWORD or QWORD, it will return the correct value type and
   140  // ErrUnexpectedType.
   141  func (k Key) Integer(s string) (uint64, uint32, error) {
   142  	d, t, err := k.getValue(s, make([]byte, 8))
   143  	if err != nil {
   144  		return 0, t, err
   145  	}
   146  	switch t {
   147  	case TypeDword:
   148  		if len(d) != 4 {
   149  			return 0, t, ErrUnexpectedSize
   150  		}
   151  		_ = d[3]
   152  		return uint64(d[0]) | uint64(d[1])<<8 | uint64(d[2])<<16 | uint64(d[3])<<24, t, nil
   153  	case TypeQword:
   154  		if len(d) != 8 {
   155  			return 0, t, ErrUnexpectedSize
   156  		}
   157  		_ = d[7]
   158  		return uint64(d[0]) | uint64(d[1])<<8 | uint64(d[2])<<16 | uint64(d[3])<<24 |
   159  			uint64(d[4])<<32 | uint64(d[5])<<40 | uint64(d[6])<<48 | uint64(d[7])<<56, t, nil
   160  	default:
   161  	}
   162  	return 0, t, ErrUnexpectedType
   163  }
   164  
   165  // Strings retrieves the []string value for the specified value name
   166  // associated with an open key k. It also returns the value's type.
   167  //
   168  // If value does not exist, Strings returns ErrNotExist.
   169  //
   170  // If value is not MULTI_SZ, it will return the correct value type and
   171  // ErrUnexpectedType.
   172  func (k Key) Strings(s string) ([]string, uint32, error) {
   173  	d, t, err := k.getValue(s, make([]byte, 64))
   174  	if err != nil {
   175  		return nil, t, err
   176  	}
   177  	if t != TypeStringList {
   178  		return nil, t, ErrUnexpectedType
   179  	}
   180  	if len(d) == 0 {
   181  		return nil, t, nil
   182  	}
   183  	p := (*[1 << 29]uint16)(unsafe.Pointer(&d[0]))[: len(d)/2 : len(d)/2]
   184  	if len(p) == 0 {
   185  		return nil, t, nil
   186  	}
   187  	if p[len(p)-1] == 0 {
   188  		p = p[:len(p)-1]
   189  	}
   190  	r := make([]string, 0, 5)
   191  	for i, n := 0, 0; i < len(p); i++ {
   192  		if p[i] > 0 {
   193  			continue
   194  		}
   195  		r = append(r, string(winapi.UTF16Decode(p[n:i])))
   196  		n = i + 1
   197  	}
   198  	return r, t, nil
   199  }
   200  func (k Key) setValue(s string, t uint32, d []byte) error {
   201  	if len(d) == 0 {
   202  		return winapi.RegSetValueEx(uintptr(k), s, t, nil, 0)
   203  	}
   204  	return winapi.RegSetValueEx(uintptr(k), s, t, &d[0], uint32(len(d)))
   205  }
   206  
   207  // Value retrieves the type and data for the specified value associated with
   208  // the open key.
   209  //
   210  // It fills up buffer buf and returns the retrieved byte count n. If buf is too
   211  // small to fit the stored value it returns an ErrShortBuffer error along with
   212  // the required buffer size n.
   213  //
   214  // If no buffer is provided, it returns true and actual buffer size and the
   215  // value's type only.
   216  //
   217  // If the value does not exist, the error returned is ErrNotExist.
   218  //
   219  // GetValue is a low level function. If value's type is known, use the
   220  // appropriate "Value" function instead.
   221  func (k Key) Value(s string, b []byte) (int, uint32, error) {
   222  	n, err := winapi.UTF16PtrFromString(s)
   223  	if err != nil {
   224  		return 0, 0, err
   225  	}
   226  	var (
   227  		l = uint32(len(b))
   228  		t uint32
   229  		o *byte
   230  	)
   231  	if l > 0 {
   232  		o = (*byte)(&b[0])
   233  	}
   234  	if err = syscall.RegQueryValueEx(syscall.Handle(k), n, nil, &t, o, &l); err != nil {
   235  		return int(l), t, err
   236  	}
   237  	return int(l), t, nil
   238  }
   239  func (k Key) setStringValue(n string, t uint32, v string) error {
   240  	p, err := winapi.UTF16FromString(v)
   241  	if err != nil {
   242  		return err
   243  	}
   244  	return k.setValue(n, t, (*[1 << 29]byte)(unsafe.Pointer(&p[0]))[:len(p)*2:len(p)*2])
   245  }
   246  func (k Key) getValue(s string, b []byte) ([]byte, uint32, error) {
   247  	p, err := syscall.UTF16PtrFromString(s)
   248  	if err != nil {
   249  		return nil, 0, err
   250  	}
   251  	var (
   252  		n = uint32(len(b))
   253  		t uint32
   254  	)
   255  	for {
   256  		err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(&b[0]), &n)
   257  		if err == nil {
   258  			return b[:n], t, nil
   259  		}
   260  		if err != syscall.ERROR_MORE_DATA {
   261  			return nil, 0, err
   262  		}
   263  		if n <= uint32(len(b)) {
   264  			return nil, 0, err
   265  		}
   266  		b = make([]byte, n)
   267  	}
   268  }