summaryrefslogtreecommitdiffhomepage
path: root/setupapi/setupapi_windows.go
diff options
context:
space:
mode:
authorSimon Rozman <simon@rozman.si>2019-02-05 11:44:47 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2019-02-05 12:59:42 +0100
commit99a3b628e9f2405a4acaa84c7a8fb74cfa6bbe6e (patch)
treee92139a9bdc5d0d41b72e09e5a69628203cc9b90 /setupapi/setupapi_windows.go
parente7ffce0d21ee87f36a86d854fed5a6d18d9fa4bf (diff)
setupapi: Add support for SetupDi(Get|Set)DeviceRegistryProperty()
Signed-off-by: Simon Rozman <simon@rozman.si>
Diffstat (limited to 'setupapi/setupapi_windows.go')
-rw-r--r--setupapi/setupapi_windows.go107
1 files changed, 99 insertions, 8 deletions
diff --git a/setupapi/setupapi_windows.go b/setupapi/setupapi_windows.go
index d87520b..0a833ed 100644
--- a/setupapi/setupapi_windows.go
+++ b/setupapi/setupapi_windows.go
@@ -6,6 +6,8 @@
package setupapi
import (
+ "encoding/binary"
+ "fmt"
"syscall"
"unsafe"
@@ -144,6 +146,95 @@ func (DeviceInfoSet DevInfo) OpenDevRegKey(DeviceInfoData *SP_DEVINFO_DATA, Scop
return SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, Scope, HwProfile, KeyType, samDesired)
}
+//sys setupDiGetDeviceRegistryProperty(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, Property SPDRP, PropertyRegDataType *uint32, PropertyBuffer *byte, PropertyBufferSize uint32, RequiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceRegistryPropertyW
+
+// SetupDiGetDeviceRegistryProperty function retrieves a specified Plug and Play device property.
+func SetupDiGetDeviceRegistryProperty(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, Property SPDRP) (value interface{}, err error) {
+ buf := make([]byte, 0x100)
+ var dataType, bufLen uint32
+ err = setupDiGetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, Property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
+ if err == nil {
+ // The buffer was sufficiently big.
+ return getRegistryValue(buf[:bufLen], dataType)
+ }
+
+ if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
+ // The buffer was too small. Now that we got the required size, create another one big enough and retry.
+ buf = make([]byte, bufLen)
+ err = setupDiGetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, Property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
+ if err == nil {
+ return getRegistryValue(buf[:bufLen], dataType)
+ }
+ }
+
+ return
+}
+
+func getRegistryValue(buf []byte, dataType uint32) (interface{}, error) {
+ switch dataType {
+ case windows.REG_SZ:
+ return windows.UTF16ToString(toUTF16(buf)), nil
+ case windows.REG_EXPAND_SZ:
+ return registry.ExpandString(windows.UTF16ToString(toUTF16(buf)))
+ case windows.REG_BINARY:
+ return buf, nil
+ case windows.REG_DWORD_LITTLE_ENDIAN:
+ return binary.LittleEndian.Uint32(buf), nil
+ case windows.REG_DWORD_BIG_ENDIAN:
+ return binary.BigEndian.Uint32(buf), nil
+ case windows.REG_MULTI_SZ:
+ bufW := toUTF16(buf)
+ a := []string{}
+ for i := 0; i < len(bufW); {
+ j := i + wcslen(bufW[i:])
+ if i < j {
+ a = append(a, windows.UTF16ToString(bufW[i:j]))
+ }
+ i = j + 1
+ }
+ return a, nil
+ case windows.REG_QWORD_LITTLE_ENDIAN:
+ return binary.LittleEndian.Uint64(buf), nil
+ default:
+ return nil, fmt.Errorf("Unsupported registry value type: %v", dataType)
+ }
+}
+
+func toUTF16(buf []byte) []uint16 {
+ sl := struct {
+ addr *uint16
+ len int
+ cap int
+ }{(*uint16)(unsafe.Pointer(&buf[0])), len(buf) / 2, cap(buf) / 2}
+ return *(*[]uint16)(unsafe.Pointer(&sl))
+}
+
+func wcslen(str []uint16) int {
+ for i := 0; i < len(str); i++ {
+ if str[i] == 0 {
+ return i
+ }
+ }
+ return len(str)
+}
+
+// GetDeviceRegistryProperty method retrieves a specified Plug and Play device property.
+func (DeviceInfoSet DevInfo) GetDeviceRegistryProperty(DeviceInfoData *SP_DEVINFO_DATA, Property SPDRP) (value interface{}, err error) {
+ return SetupDiGetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, Property)
+}
+
+//sys setupDiSetDeviceRegistryProperty(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, Property SPDRP, PropertyBuffer *byte, PropertyBufferSize uint32) (err error) = setupapi.SetupDiSetDeviceRegistryPropertyW
+
+// SetupDiSetDeviceRegistryProperty function sets a Plug and Play device property for a device.
+func SetupDiSetDeviceRegistryProperty(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, Property SPDRP, PropertyBuffer []byte) (err error) {
+ return setupDiSetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, Property, &PropertyBuffer[0], uint32(len(PropertyBuffer)))
+}
+
+// SetDeviceRegistryProperty function sets a Plug and Play device property for a device.
+func (DeviceInfoSet DevInfo) SetDeviceRegistryProperty(DeviceInfoData *SP_DEVINFO_DATA, Property SPDRP, PropertyBuffer []byte) (err error) {
+ return SetupDiSetDeviceRegistryProperty(DeviceInfoSet, DeviceInfoData, Property, PropertyBuffer)
+}
+
//sys setupDiGetDeviceInstallParams(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DeviceInstallParams *_SP_DEVINSTALL_PARAMS) (err error) = setupapi.SetupDiGetDeviceInstallParamsW
// SetupDiGetDeviceInstallParams function retrieves device installation parameters for a device information set or a particular device information element.
@@ -248,9 +339,9 @@ func SetupDiClassGuidsFromNameEx(ClassName string, MachineName string) (ClassGui
return
}
- const bufLen = 4
- var buf [bufLen]windows.GUID
- var bufCount uint32
+ const bufCapacity = 4
+ var buf [bufCapacity]windows.GUID
+ var bufLen uint32
var machineNameUTF16 *uint16
if MachineName != "" {
@@ -260,18 +351,18 @@ func SetupDiClassGuidsFromNameEx(ClassName string, MachineName string) (ClassGui
}
}
- err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufLen, &bufCount, machineNameUTF16, 0)
+ err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufCapacity, &bufLen, machineNameUTF16, 0)
if err == nil {
// The GUID array was sufficiently big. Return its slice.
- return buf[:bufCount], nil
+ return buf[:bufLen], nil
}
if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
// The GUID array was too small. Now that we got the required size, create another one big enough and retry.
- buf := make([]windows.GUID, bufCount)
- err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufCount, &bufCount, machineNameUTF16, 0)
+ buf := make([]windows.GUID, bufLen)
+ err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufLen, &bufLen, machineNameUTF16, 0)
if err == nil {
- return buf[:bufCount], nil
+ return buf[:bufLen], nil
}
}