github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/registry/key.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  	"io"
    24  	"runtime"
    25  	"syscall"
    26  	"time"
    27  
    28  	"github.com/iDigitalFlame/xmt/device/winapi"
    29  )
    30  
    31  // Windows defines some predefined root keys that are always open.
    32  //
    33  // An application can use these keys as entry points to the registry. Normally
    34  // these keys are used in OpenKey to open new keys, but they can also be used
    35  // anywhere a Key is required.
    36  const (
    37  	KeyClassesRoot     = Key(syscall.HKEY_CLASSES_ROOT)
    38  	KeyCurrentUser     = Key(syscall.HKEY_CURRENT_USER)
    39  	KeyLocalMachine    = Key(syscall.HKEY_LOCAL_MACHINE)
    40  	KeyUsers           = Key(syscall.HKEY_USERS)
    41  	KeyCurrentConfig   = Key(syscall.HKEY_CURRENT_CONFIG)
    42  	KeyPerformanceData = Key(syscall.HKEY_PERFORMANCE_DATA)
    43  )
    44  
    45  const errNoMoreItems syscall.Errno = 259
    46  
    47  // Key is a handle to an open Windows registry key.
    48  //
    49  // Keys can be obtained by calling OpenKey.
    50  type Key uintptr
    51  
    52  // KeyInfo describes the statistics of a key.
    53  //
    54  // It is returned by a call to Stat.
    55  type KeyInfo struct {
    56  	SubKeyCount     uint32
    57  	MaxSubKeyLen    uint32
    58  	ValueCount      uint32
    59  	MaxValueNameLen uint32
    60  	MaxValueLen     uint32
    61  	lastWriteTime   syscall.Filetime
    62  }
    63  
    64  // Close closes the open key.
    65  func (k Key) Close() error {
    66  	return syscall.RegCloseKey(syscall.Handle(k))
    67  }
    68  
    69  // Flush calls 'winapi.RegFlushKey' on this key to sync the data with the
    70  // underlying filesystem.
    71  func (k Key) Flush() error {
    72  	return winapi.RegFlushKey(uintptr(k))
    73  }
    74  
    75  // ModTime returns the key's last write time.
    76  func (i *KeyInfo) ModTime() time.Time {
    77  	return time.Unix(0, i.lastWriteTime.Nanoseconds())
    78  }
    79  
    80  // Stat retrieves information about the open key.
    81  func (k Key) Stat() (*KeyInfo, error) {
    82  	var (
    83  		i   KeyInfo
    84  		err = syscall.RegQueryInfoKey(
    85  			syscall.Handle(k), nil, nil, nil,
    86  			&i.SubKeyCount, &i.MaxSubKeyLen, nil, &i.ValueCount,
    87  			&i.MaxValueNameLen, &i.MaxValueLen, nil, &i.lastWriteTime,
    88  		)
    89  	)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	return &i, nil
    94  }
    95  
    96  // Values returns the value names in the key. This function is similar to
    97  // 'ValueNames' but returns ALL names instead.
    98  func (k Key) Values() ([]string, error) {
    99  	return k.ValueNames(-1)
   100  }
   101  
   102  // DeleteKey deletes the subkey path of the key and its values.
   103  //
   104  // Convince function added directly to the Key alias.
   105  //
   106  // This function fails with "invalid argument" if the key has subkeys or
   107  // values. Use 'DeleteTreeKey' instead to delete the full non-empty key.
   108  func (k Key) DeleteKey(s string) error {
   109  	return DeleteEx(k, s, 0)
   110  }
   111  
   112  // DeleteValue removes a named value from the key.
   113  func (k Key) DeleteValue(n string) error {
   114  	return winapi.RegDeleteValue(uintptr(k), n)
   115  }
   116  
   117  // SubKeys returns the names of subkeys of key. This function is similar to
   118  // 'SubKeyNames' but returns ALL names instead.
   119  func (k Key) SubKeys() ([]string, error) {
   120  	return k.SubKeyNames(-1)
   121  }
   122  
   123  // ValueNames returns the value names in the key.
   124  //
   125  // The parameter controls the number of returned names, analogous to the way
   126  // 'os.File.Readdirnames' works.
   127  func (k Key) ValueNames(n int) ([]string, error) {
   128  	x, err := k.Stat()
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	if x.ValueCount == 0 || x.MaxValueNameLen == 0 {
   133  		return nil, nil
   134  	}
   135  	var (
   136  		o = make([]string, 0, x.ValueCount)
   137  		b = make([]uint16, x.MaxValueNameLen+1)
   138  	)
   139  loop:
   140  	for i := uint32(0); i < x.ValueCount; i++ {
   141  		if n > 0 {
   142  			if len(o) == n {
   143  				return o, nil
   144  			}
   145  		}
   146  		l := uint32(len(b))
   147  		for {
   148  			if err = winapi.RegEnumValue(uintptr(k), i, &b[0], &l, nil, nil, nil); err == nil {
   149  				break
   150  			}
   151  			if err == syscall.ERROR_MORE_DATA {
   152  				l = uint32(2 * len(b))
   153  				b = make([]uint16, l)
   154  				continue
   155  			}
   156  			if err == errNoMoreItems {
   157  				break loop
   158  			}
   159  			return o, err
   160  		}
   161  		o = append(o, winapi.UTF16ToString(b[:l]))
   162  	}
   163  	if n > len(o) {
   164  		return o, io.EOF
   165  	}
   166  	return o, nil
   167  }
   168  
   169  // SubKeyNames returns the names of subkeys of key.
   170  //
   171  // The parameter controls the number of returned names, analogous to the way
   172  // 'os.File.Readdirnames' works.
   173  func (k Key) SubKeyNames(n int) ([]string, error) {
   174  	runtime.LockOSThread()
   175  	var (
   176  		o   = make([]string, 0)
   177  		b   = make([]uint16, 256)
   178  		err error
   179  	)
   180  loop:
   181  	for i := uint32(0); ; i++ {
   182  		if n > 0 {
   183  			if len(o) == n {
   184  				runtime.UnlockOSThread()
   185  				return o, nil
   186  			}
   187  		}
   188  		l := uint32(len(b))
   189  		for {
   190  			if err = syscall.RegEnumKeyEx(syscall.Handle(k), i, &b[0], &l, nil, nil, nil, nil); err == nil {
   191  				break
   192  			}
   193  			if err == syscall.ERROR_MORE_DATA {
   194  				l = uint32(2 * len(b))
   195  				b = make([]uint16, l)
   196  				continue
   197  			}
   198  			break loop
   199  		}
   200  		o = append(o, winapi.UTF16ToString(b[:l]))
   201  	}
   202  	if runtime.UnlockOSThread(); err == errNoMoreItems {
   203  		err = nil
   204  	}
   205  	if n > len(o) {
   206  		return o, io.EOF
   207  	}
   208  	return o, err
   209  }
   210  
   211  // Open opens a new key with path name relative to the key.
   212  //
   213  // Convince function added directly to the Key alias.
   214  //
   215  // It accepts any open root key, including CurrentUser for example, and returns
   216  // the new key and an any errors that may occur during opening.
   217  //
   218  // The access parameter specifies desired access rights to the key to be opened.
   219  func (k Key) Open(s string, a uint32) (Key, error) {
   220  	v, err := winapi.UTF16PtrFromString(s)
   221  	if err != nil {
   222  		return 0, err
   223  	}
   224  	var h syscall.Handle
   225  	if err = syscall.RegOpenKeyEx(syscall.Handle(k), v, 0, a, &h); err != nil {
   226  		return 0, err
   227  	}
   228  	return Key(h), nil
   229  }
   230  
   231  // DeleteKeyEx deletes the subkey path of the key and its values.
   232  //
   233  // Convince function added directly to the Key alias.
   234  //
   235  // This function fails with "invalid argument" if the key has subkeys or
   236  // values. Use 'DeleteTreeKey' instead to delete the full non-empty key.
   237  //
   238  // This function allows for specifying which WOW endpoint to delete from
   239  // leave the flags value as zero to use the current WOW/non-WOW registry point.
   240  //
   241  // NOTE(dij): WOW64_32 is 0x200 and WOW64_64 is 0x100
   242  func (k Key) DeleteKeyEx(s string, flags uint32) error {
   243  	return winapi.RegDeleteKeyEx(uintptr(k), s, flags)
   244  }
   245  
   246  // Create creates a key named path under the open key.
   247  //
   248  // Convince function added directly to the Key alias.
   249  //
   250  // CreateKey returns the new key and a boolean flag that reports whether the key
   251  // already existed.
   252  //
   253  // The access parameter specifies the access rights for the key to be created.
   254  func (k Key) Create(s string, a uint32) (Key, bool, error) {
   255  	var (
   256  		h   uintptr
   257  		d   uint32
   258  		err = winapi.RegCreateKeyEx(uintptr(k), s, "", 0, a, nil, &h, &d)
   259  	)
   260  	if err != nil {
   261  		return 0, false, err
   262  	}
   263  	return Key(h), d == 2, nil
   264  }