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 }