podman-build/vendor/github.com/containers/libhvee/pkg/wmiext/array.go
2025-10-11 12:30:35 +09:00

203 lines
4.9 KiB
Go

//go:build windows
// +build windows
package wmiext
import (
"errors"
"fmt"
"reflect"
"unsafe"
"github.com/go-ole/go-ole"
)
func CreateStringArrayVariant(array []string) (ole.VARIANT, error) {
safeByteArray, err := safeArrayFromStringSlice(array)
if err != nil {
return ole.VARIANT{}, err
}
arrayVariant := ole.NewVariant(ole.VT_ARRAY|ole.VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray))))
return arrayVariant, nil
}
func CreateNumericArrayVariant(array interface{}, itemType ole.VT) (ole.VARIANT, error) {
safeArray, err := safeArrayFromNumericSlice(array, itemType)
if err != nil {
return ole.VARIANT{}, err
}
arrayVariant := ole.NewVariant(ole.VT_ARRAY|itemType, int64(uintptr(unsafe.Pointer(safeArray))))
return arrayVariant, nil
}
// The following safearray routines are unfortunately not yet exported from go-ole,
// so replicate them for now
func safeArrayCreateVector(variantType ole.VT, lowerBound int32, length uint32) (safearray *ole.SafeArray, err error) {
ret, _, _ := procSafeArrayCreateVector.Call(
uintptr(variantType),
uintptr(lowerBound),
uintptr(length))
if ret == 0 { // NULL return value
err = fmt.Errorf("could not create safe array")
}
safearray = (*ole.SafeArray)(unsafe.Pointer(ret))
return
}
func safeArrayFromNumericSlice(slice interface{}, itemType ole.VT) (*ole.SafeArray, error) {
sliceType := reflect.TypeOf(slice)
if sliceType.Kind() != reflect.Slice {
return nil, errors.New("expected a slice converting to safe array")
}
val := reflect.ValueOf(slice)
array, err := safeArrayCreateVector(itemType, 0, uint32(val.Len()))
if err != nil {
return nil, err
}
// assignable holders used for conversion
var (
vui1 uint8
vui2 uint16
vui4 uint32
vui8 uint64
vi1 int8
vi2 int16
vi4 int32
vi8 int64
)
for i := 0; i < val.Len(); i++ {
var data uintptr
item := val.Index(i)
switch itemType {
case ole.VT_UI1:
data = convertToUnsafeAddr(item, &vui1)
case ole.VT_UI2:
data = convertToUnsafeAddr(item, &vui2)
case ole.VT_UI4:
data = convertToUnsafeAddr(item, &vui4)
case ole.VT_UI8:
data = convertToUnsafeAddr(item, &vui8)
case ole.VT_I1:
data = convertToUnsafeAddr(item, &vi1)
case ole.VT_I2:
data = convertToUnsafeAddr(item, &vi2)
case ole.VT_I4:
data = convertToUnsafeAddr(item, &vi4)
case ole.VT_I8:
data = convertToUnsafeAddr(item, &vi8)
}
err = safeArrayPutElement(array, int64(i), data)
if err != nil {
_ = safeArrayDestroy(array)
return nil, err
}
}
return array, nil
}
func convertToUnsafeAddr(src reflect.Value, target interface{}) uintptr {
val := reflect.ValueOf(target)
val = val.Elem()
val.Set(src.Convert(val.Type()))
return val.UnsafeAddr()
}
func safeArrayDestroy(safearray *ole.SafeArray) (err error) {
ret, _, _ := procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray)))
if ret != 0 {
return NewWmiError(ret)
}
return nil
}
func safeArrayPutElement(safearray *ole.SafeArray, index int64, element uintptr) (err error) {
ret, _, _ := procSafeArrayPutElement.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&index)),
element)
if ret != 0 {
return NewWmiError(ret)
}
return nil
}
func safeArrayGetElement(safearray *ole.SafeArray, index int64, element unsafe.Pointer) error {
ret, _, _ := procSafeArrayGetElement.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&index)),
uintptr(element))
if ret != 0 {
return NewWmiError(ret)
}
return nil
}
func isVariantValConvertible(variant ole.VARIANT) bool {
return !(variant.VT == ole.VT_RECORD || variant.VT == ole.VT_VARIANT)
}
func safeArrayGetAsVariantVal(safeArray *ole.SafeArray, index int64, variant ole.VARIANT) (int64, error) {
var block int64
if !isVariantValConvertible(variant) {
return 0, fmt.Errorf("numeric call on a non-numeric value: %d", variant.VT)
}
if err := safeArrayGetElement(safeArray, index, unsafe.Pointer(&block)); err != nil {
return 0, err
}
switch variant.VT {
case ole.VT_UI1:
return int64(uint64(*(*uint8)(unsafe.Pointer(&block)))), nil
case ole.VT_UI2:
return int64(uint64(*(*uint16)(unsafe.Pointer(&block)))), nil
case ole.VT_UI4:
return int64(uint64(*(*uint32)(unsafe.Pointer(&block)))), nil
case ole.VT_I1:
return int64(*(*int8)(unsafe.Pointer(&block))), nil
case ole.VT_I2:
return int64(*(*int16)(unsafe.Pointer(&block))), nil
case ole.VT_I4:
return int64(*(*int32)(unsafe.Pointer(&block))), nil
case ole.VT_UI8, ole.VT_I8:
fallthrough
case ole.VT_R4, ole.VT_R8:
fallthrough
default:
return block, nil
}
}
func safeArrayFromStringSlice(slice []string) (*ole.SafeArray, error) {
array, err := safeArrayCreateVector(ole.VT_BSTR, 0, uint32(len(slice)))
if err != nil {
return nil, err
}
for i, v := range slice {
err = safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(ole.SysAllocStringLen(v))))
if err != nil {
_ = safeArrayDestroy(array)
return nil, err
}
}
return array, nil
}