Merge pull request #17838 from baude/addhv1

basic hypverv machine implementation
This commit is contained in:
OpenShift Merge Robot
2023-03-20 10:40:30 -04:00
committed by GitHub
106 changed files with 9552 additions and 33 deletions

View File

@ -141,15 +141,15 @@ func hostInfo() (*entities.MachineHostInfo, error) {
}
}
host.VMType = provider.VMType()
host.VMType = provider.VMType().String()
dataDir, err := machine.GetDataDir(host.VMType)
dataDir, err := machine.GetDataDir(provider.VMType())
if err != nil {
return nil, fmt.Errorf("failed to get machine image dir")
}
host.MachineImageDir = dataDir
confDir, err := machine.GetConfDir(host.VMType)
confDir, err := machine.GetConfDir(provider.VMType())
if err != nil {
return nil, fmt.Errorf("failed to get machine config dir %w", err)
}

View File

@ -1,10 +1,19 @@
package machine
import (
"os"
"github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/hyperv"
"github.com/containers/podman/v4/pkg/machine/wsl"
)
func GetSystemDefaultProvider() machine.VirtProvider {
// This is a work-around for default provider on windows while
// hyperv is one developer.
// TODO this needs to be changed back
if _, exists := os.LookupEnv("HYPERV"); exists {
return hyperv.GetVirtualizationProvider()
}
return wsl.GetWSLProvider()
}

2
go.mod
View File

@ -15,6 +15,7 @@ require (
github.com/containers/common v0.51.1-0.20230316131336-0be880eaeb02
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.24.3-0.20230314083015-0c6d07e02a9a
github.com/containers/libhvee v0.0.1
github.com/containers/ocicrypt v1.1.7
github.com/containers/psgo v1.8.0
github.com/containers/storage v1.45.5-0.20230315220505-1c6287eea927
@ -94,6 +95,7 @@ require (
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsouza/go-dockerclient v1.9.3 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect

4
go.sum
View File

@ -253,6 +253,8 @@ github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6J
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.24.3-0.20230314083015-0c6d07e02a9a h1:2xIif78r5x2nmdb5uhjXBZuexiDAt1c/XIXFxFhfKSk=
github.com/containers/image/v5 v5.24.3-0.20230314083015-0c6d07e02a9a/go.mod h1:9PM/hiCVTh6dt8Swi7eYKXKHIaPabHn8gtFV2YD44Mk=
github.com/containers/libhvee v0.0.1 h1:QpknIQ2VG54HLNoXb0ZXdmah8lw7EC2atD5pX7543pI=
github.com/containers/libhvee v0.0.1/go.mod h1:bV1MfbuXk/ZLWHiWZpm8aePOR6iJGD1q55guYhH4CnA=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
@ -395,6 +397,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=

View File

@ -11,7 +11,7 @@ import (
var (
// vmtype refers to qemu (vs libvirt, krun, etc).
vmtype = "apple"
vmtype = machine.AppleHvVirt
)
func GetVirtualizationProvider() machine.VirtProvider {

View File

@ -57,7 +57,7 @@ type VirtProvider interface {
LoadVMByName(name string) (VM, error)
NewMachine(opts InitOptions) (VM, error)
RemoveAndCleanMachines() error
VMType() string
VMType() VMType
}
type RemoteConnectionType string
@ -183,7 +183,7 @@ func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url
}
// GetCacheDir returns the dir where VM images are downloaded into when pulled
func GetCacheDir(vmType string) (string, error) {
func GetCacheDir(vmType VMType) (string, error) {
dataDir, err := GetDataDir(vmType)
if err != nil {
return "", err
@ -197,12 +197,12 @@ func GetCacheDir(vmType string) (string, error) {
// GetDataDir returns the filepath where vm images should
// live for podman-machine.
func GetDataDir(vmType string) (string, error) {
func GetDataDir(vmType VMType) (string, error) {
dataDirPrefix, err := DataDirPrefix()
if err != nil {
return "", err
}
dataDir := filepath.Join(dataDirPrefix, vmType)
dataDir := filepath.Join(dataDirPrefix, vmType.String())
if _, err := os.Stat(dataDir); !errors.Is(err, os.ErrNotExist) {
return dataDir, nil
}
@ -222,12 +222,12 @@ func DataDirPrefix() (string, error) {
// GetConfigDir returns the filepath to where configuration
// files for podman-machine should live
func GetConfDir(vmType string) (string, error) {
func GetConfDir(vmType VMType) (string, error) {
confDirPrefix, err := ConfDirPrefix()
if err != nil {
return "", err
}
confDir := filepath.Join(confDirPrefix, vmType)
confDir := filepath.Join(confDirPrefix, vmType.String())
if _, err := os.Stat(confDir); !errors.Is(err, os.ErrNotExist) {
return confDir, nil
}
@ -379,3 +379,24 @@ type ConnectionConfig struct {
// PodmanPipe is the exported podman service named pipe (Windows hosts only)
PodmanPipe *VMFile `json:"PodmanPipe"`
}
type VMType int64
const (
QemuVirt VMType = iota
WSLVirt
AppleHvVirt
HyperVVirt
)
func (v VMType) String() string {
switch v {
case WSLVirt:
return "wsl"
case AppleHvVirt:
return "applehv"
case HyperVVirt:
return "hyperv"
}
return "qemu"
}

View File

@ -100,7 +100,7 @@ type FcosDownload struct {
Download
}
func NewFcosDownloader(vmType, vmName string, imageStream FCOSStream, vp VirtProvider) (DistributionDownload, error) {
func NewFcosDownloader(vmType VMType, vmName string, imageStream FCOSStream, vp VirtProvider) (DistributionDownload, error) {
info, err := GetFCOSDownload(vp, imageStream)
if err != nil {
return nil, err

View File

@ -0,0 +1,280 @@
//go:build windows
// +build windows
package hyperv
import (
"encoding/json"
"errors"
"github.com/sirupsen/logrus"
"io/fs"
"os"
"path/filepath"
"time"
"github.com/containers/libhvee/pkg/hypervctl"
"github.com/containers/podman/v4/pkg/machine"
"github.com/docker/go-units"
)
type Virtualization struct {
artifact machine.Artifact
compression machine.ImageCompression
format machine.ImageFormat
}
func (v Virtualization) Artifact() machine.Artifact {
return machine.None
}
func (v Virtualization) CheckExclusiveActiveVM() (bool, string, error) {
vmm := hypervctl.NewVirtualMachineManager()
// Use of GetAll is OK here because we do not want to use the same name
// as something already *actually* configured in hyperv
vms, err := vmm.GetAll()
if err != nil {
return false, "", err
}
for _, vm := range vms {
if vm.IsStarting() || vm.State() == hypervctl.Enabled {
return true, vm.ElementName, nil
}
}
return false, "", nil
}
func (v Virtualization) Compression() machine.ImageCompression {
return v.compression
}
func (v Virtualization) Format() machine.ImageFormat {
return v.format
}
func (v Virtualization) IsValidVMName(name string) (bool, error) {
// We check both the local filesystem and hyperv for the valid name
mm := HyperVMachine{Name: name}
configDir, err := machine.GetConfDir(v.VMType())
if err != nil {
return false, err
}
if err := loadMacMachineFromJSON(configDir, &mm); err != nil {
return false, err
}
// The name is valid for the local filesystem
if _, err := hypervctl.NewVirtualMachineManager().GetMachine(name); err != nil {
return false, err
}
// The lookup in hyperv worked, so it is also valid there
return true, nil
}
func (v Virtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, error) {
var response []*machine.ListResponse
vmm := hypervctl.NewVirtualMachineManager()
vms, err := vmm.GetAll()
if err != nil {
return nil, err
}
for _, vm := range vms {
m := &HyperVMachine{Name: vm.ElementName}
mm, err := m.loadFromFile()
if err != nil {
return nil, err
}
mlr := machine.ListResponse{
Name: mm.Name,
CreatedAt: mm.Created,
LastUp: mm.LastUp,
Running: vm.State() == hypervctl.Enabled,
Starting: vm.IsStarting(),
Stream: mm.ImageStream,
VMType: machine.HyperVVirt.String(),
CPUs: mm.CPUs,
Memory: mm.Memory * units.MiB,
DiskSize: mm.DiskSize * units.GiB,
Port: mm.Port,
RemoteUsername: mm.RemoteUsername,
IdentityPath: mm.IdentityPath,
}
response = append(response, &mlr)
}
return response, err
}
func (v Virtualization) LoadVMByName(name string) (machine.VM, error) {
m := &HyperVMachine{Name: name}
return m.loadFromFile()
}
func (v Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
m := HyperVMachine{Name: opts.Name}
if len(opts.ImagePath) < 1 {
return nil, errors.New("must define --image-path for hyperv support")
}
configDir, err := machine.GetConfDir(machine.HyperVVirt)
if err != nil {
return nil, err
}
configPath, err := machine.NewMachineFile(getVMConfigPath(configDir, opts.Name), nil)
if err != nil {
return nil, err
}
m.ConfigPath = *configPath
ignitionPath, err := machine.NewMachineFile(filepath.Join(configDir, m.Name)+".ign", nil)
if err != nil {
return nil, err
}
m.IgnitionFile = *ignitionPath
// Set creation time
m.Created = time.Now()
dataDir, err := machine.GetDataDir(machine.HyperVVirt)
if err != nil {
return nil, err
}
// Acquire the image
// Until we are producing vhdx images in fcos, all images must be fed to us
// with --image-path. We should, however, accept both a file or url
g, err := machine.NewGenericDownloader(machine.HyperVVirt, opts.Name, opts.ImagePath)
if err != nil {
return nil, err
}
imagePath, err := machine.NewMachineFile(g.Get().GetLocalUncompressedFile(dataDir), nil)
if err != nil {
return nil, err
}
m.ImagePath = *imagePath
if err := machine.DownloadImage(g); err != nil {
return nil, err
}
config := hypervctl.HardwareConfig{
CPUs: uint16(opts.CPUS),
DiskPath: imagePath.GetPath(),
// TODO remove this and make real
DiskSize: 100,
Memory: int32(opts.Memory),
}
// Write the json configuration file which will be loaded by
// LoadByName
b, err := json.MarshalIndent(m, "", " ")
if err != nil {
return nil, err
}
if err := os.WriteFile(m.ConfigPath.GetPath(), b, 0644); err != nil {
return nil, err
}
vmm := hypervctl.NewVirtualMachineManager()
if err := vmm.NewVirtualMachine(opts.Name, &config); err != nil {
return nil, err
}
return v.LoadVMByName(opts.Name)
}
func (v Virtualization) RemoveAndCleanMachines() error {
// Error handling used here is following what qemu did
var (
prevErr error
)
// The next three info lookups must succeed or we return
mms, err := v.loadFromLocalJson()
if err != nil {
return err
}
configDir, err := machine.GetConfDir(vmtype)
if err != nil {
return err
}
dataDir, err := machine.GetDataDir(vmtype)
if err != nil {
return err
}
vmm := hypervctl.NewVirtualMachineManager()
for _, mm := range mms {
vm, err := vmm.GetMachine(mm.Name)
if err != nil {
prevErr = handlePrevError(err, prevErr)
}
// If the VM is not stopped, we need to stop it
// TODO stop might not be enough if the state is dorked. we may need
// something like forceoff hard switch
if vm.State() != hypervctl.Disabled {
if err := vm.Stop(); err != nil {
prevErr = handlePrevError(err, prevErr)
}
}
if err := vm.Remove(mm.ImagePath.GetPath()); err != nil {
prevErr = handlePrevError(err, prevErr)
}
}
// Nuke the config and dataDirs
if err := os.RemoveAll(configDir); err != nil {
prevErr = handlePrevError(err, prevErr)
}
if err := os.RemoveAll(dataDir); err != nil {
prevErr = handlePrevError(err, prevErr)
}
return prevErr
}
func (v Virtualization) VMType() machine.VMType {
return vmtype
}
func (v Virtualization) loadFromLocalJson() ([]*HyperVMachine, error) {
var (
jsonFiles []string
mms []*HyperVMachine
)
configDir, err := machine.GetConfDir(v.VMType())
if err != nil {
return nil, err
}
if err := filepath.WalkDir(configDir, func(input string, d fs.DirEntry, e error) error {
if e != nil {
return e
}
if filepath.Ext(d.Name()) == "json" {
jsonFiles = append(jsonFiles, input)
}
return nil
}); err != nil {
return nil, err
}
for _, jsonFile := range jsonFiles {
mm := HyperVMachine{}
if err := loadMacMachineFromJSON(jsonFile, &mm); err != nil {
return nil, err
}
if err != nil {
return nil, err
}
mms = append(mms, &mm)
}
return mms, nil
}
func handlePrevError(e, prevErr error) error {
if prevErr != nil {
logrus.Error(e)
}
return e
}

View File

@ -0,0 +1,384 @@
//go:build windows
// +build windows
package hyperv
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/docker/go-units"
"net/url"
"os"
"path/filepath"
"strconv"
"time"
"github.com/containers/libhvee/pkg/hypervctl"
"github.com/containers/podman/v4/pkg/machine"
"github.com/containers/storage/pkg/homedir"
"github.com/sirupsen/logrus"
)
var (
// vmtype refers to qemu (vs libvirt, krun, etc).
vmtype = machine.HyperVVirt
)
func GetVirtualizationProvider() machine.VirtProvider {
return &Virtualization{
artifact: machine.HyperV,
compression: machine.Zip,
format: machine.Vhdx,
}
}
const (
// Some of this will need to change when we are closer to having
// working code.
VolumeTypeVirtfs = "virtfs"
MountType9p = "9p"
dockerSock = "/var/run/docker.sock"
dockerConnectTimeout = 5 * time.Second
apiUpTimeout = 20 * time.Second
)
type apiForwardingState int
const (
noForwarding apiForwardingState = iota
claimUnsupported
notInstalled
machineLocal
dockerGlobal
)
type HyperVMachine struct {
// copied from qemu, cull and add as needed
// ConfigPath is the fully qualified path to the configuration file
ConfigPath machine.VMFile
// The command line representation of the qemu command
//CmdLine []string
// HostUser contains info about host user
machine.HostUser
// ImageConfig describes the bootable image
machine.ImageConfig
// Mounts is the list of remote filesystems to mount
Mounts []machine.Mount
// Name of VM
Name string
// PidFilePath is the where the Proxy PID file lives
//PidFilePath machine.VMFile
// VMPidFilePath is the where the VM PID file lives
//VMPidFilePath machine.VMFile
// QMPMonitor is the qemu monitor object for sending commands
//QMPMonitor Monitor
// ReadySocket tells host when vm is booted
ReadySocket machine.VMFile
// ResourceConfig is physical attrs of the VM
machine.ResourceConfig
// SSHConfig for accessing the remote vm
machine.SSHConfig
// Starting tells us whether the machine is running or if we have just dialed it to start it
Starting bool
// Created contains the original created time instead of querying the file mod time
Created time.Time
// LastUp contains the last recorded uptime
LastUp time.Time
}
func (m *HyperVMachine) Init(opts machine.InitOptions) (bool, error) {
var (
key string
)
sshDir := filepath.Join(homedir.Get(), ".ssh")
m.IdentityPath = filepath.Join(sshDir, m.Name)
if len(opts.IgnitionPath) < 1 {
uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", fmt.Sprintf("/run/user/%d/podman/podman.sock", m.UID), strconv.Itoa(m.Port), m.RemoteUsername)
uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(m.Port), "root")
identity := filepath.Join(sshDir, m.Name)
uris := []url.URL{uri, uriRoot}
names := []string{m.Name, m.Name + "-root"}
// The first connection defined when connections is empty will become the default
// regardless of IsDefault, so order according to rootful
if opts.Rootful {
uris[0], names[0], uris[1], names[1] = uris[1], names[1], uris[0], names[0]
}
for i := 0; i < 2; i++ {
if err := machine.AddConnection(&uris[i], names[i], identity, opts.IsDefault && i == 0); err != nil {
return false, err
}
}
} else {
fmt.Println("An ignition path was provided. No SSH connection was added to Podman")
}
if len(opts.IgnitionPath) < 1 {
var err error
key, err = machine.CreateSSHKeys(m.IdentityPath)
if err != nil {
return false, err
}
}
m.ResourceConfig = machine.ResourceConfig{
CPUs: opts.CPUS,
DiskSize: opts.DiskSize,
Memory: opts.Memory,
}
// If the user provides an ignition file, we need to
// copy it into the conf dir
if len(opts.IgnitionPath) > 0 {
inputIgnition, err := os.ReadFile(opts.IgnitionPath)
if err != nil {
return false, err
}
return false, os.WriteFile(m.IgnitionFile.GetPath(), inputIgnition, 0644)
}
// Write the JSON file for the second time. First time was in NewMachine
b, err := json.MarshalIndent(m, "", " ")
if err != nil {
return false, err
}
if err := os.WriteFile(m.ConfigPath.GetPath(), b, 0644); err != nil {
return false, err
}
if m.UID == 0 {
m.UID = 1000
}
// c/common sets the default machine user for "windows" to be "user"; this
// is meant for the WSL implementation that does not use FCOS. For FCOS,
// however, we want to use the DefaultIgnitionUserName which is currently
// "core"
user := opts.Username
if user == "user" {
user = machine.DefaultIgnitionUserName
}
// Write the ignition file
ign := machine.DynamicIgnition{
Name: user,
Key: key,
VMName: m.Name,
TimeZone: opts.TimeZone,
WritePath: m.IgnitionFile.GetPath(),
UID: m.UID,
}
if err := machine.NewIgnitionFile(ign, machine.HyperVVirt); err != nil {
return false, err
}
// The ignition file has been written. We now need to
// read it so that we can put it into key-value pairs
ignFile, err := m.IgnitionFile.Read()
if err != nil {
return false, err
}
reader := bytes.NewReader(ignFile)
vm, err := hypervctl.NewVirtualMachineManager().GetMachine(m.Name)
if err != nil {
return false, err
}
err = vm.SplitAndAddIgnition("ignition.config.", reader)
return err == nil, err
}
func (m *HyperVMachine) Inspect() (*machine.InspectInfo, error) {
vm, err := hypervctl.NewVirtualMachineManager().GetMachine(m.Name)
if err != nil {
return nil, err
}
cfg, err := vm.GetConfig(m.ImagePath.GetPath())
if err != nil {
return nil, err
}
return &machine.InspectInfo{
ConfigPath: m.ConfigPath,
ConnectionInfo: machine.ConnectionConfig{},
Created: m.Created,
Image: machine.ImageConfig{
IgnitionFile: machine.VMFile{},
ImageStream: "",
ImagePath: machine.VMFile{},
},
LastUp: m.LastUp,
Name: m.Name,
Resources: machine.ResourceConfig{
CPUs: uint64(cfg.Hardware.CPUs),
DiskSize: 0,
Memory: uint64(cfg.Hardware.Memory),
},
SSHConfig: m.SSHConfig,
State: vm.State().String(),
}, nil
}
func (m *HyperVMachine) Remove(_ string, opts machine.RemoveOptions) (string, func() error, error) {
var (
files []string
diskPath string
)
vmm := hypervctl.NewVirtualMachineManager()
vm, err := vmm.GetMachine(m.Name)
if err != nil {
return "", nil, err
}
// In hyperv, they call running 'enabled'
if vm.State() == hypervctl.Enabled {
// TODO Add for force
// Right now, the ole response from hyperv is segv'ing
// and until it comes back clean, force cannot be implemented
// because it will always fail.
return "", nil, hypervctl.ErrMachineStateInvalid
}
// Collect all the files that need to be destroyed
if !opts.SaveKeys {
files = append(files, m.IdentityPath, m.IdentityPath+".pub")
}
if !opts.SaveIgnition {
files = append(files, m.IgnitionFile.GetPath())
}
if !opts.SaveImage {
diskPath := m.ImagePath.GetPath()
files = append(files, diskPath)
}
if err := machine.RemoveConnection(m.Name); err != nil {
logrus.Error(err)
}
if err := machine.RemoveConnection(m.Name + "-root"); err != nil {
logrus.Error(err)
}
files = append(files, getVMConfigPath(m.ConfigPath.GetPath(), m.Name))
confirmationMessage := "\nThe following files will be deleted:\n\n"
for _, msg := range files {
confirmationMessage += msg + "\n"
}
confirmationMessage += "\n"
return confirmationMessage, func() error {
for _, f := range files {
if err := os.Remove(f); err != nil && !errors.Is(err, os.ErrNotExist) {
logrus.Error(err)
}
}
return vm.Remove(diskPath)
}, nil
}
func (m *HyperVMachine) Set(name string, opts machine.SetOptions) ([]error, error) {
return nil, machine.ErrNotImplemented
}
func (m *HyperVMachine) SSH(name string, opts machine.SSHOptions) error {
return machine.ErrNotImplemented
}
func (m *HyperVMachine) Start(name string, opts machine.StartOptions) error {
// TODO We need to hold Start until it actually finishes booting and ignition stuff
vmm := hypervctl.NewVirtualMachineManager()
vm, err := vmm.GetMachine(m.Name)
if err != nil {
return err
}
if vm.State() != hypervctl.Disabled {
return hypervctl.ErrMachineStateInvalid
}
return vm.Start()
}
func (m *HyperVMachine) State(_ bool) (machine.Status, error) {
vmm := hypervctl.NewVirtualMachineManager()
vm, err := vmm.GetMachine(m.Name)
if err != nil {
return "", err
}
if vm.IsStarting() {
return machine.Starting, nil
}
if vm.State() == hypervctl.Enabled {
return machine.Running, nil
}
// Following QEMU pattern here where only three
// states seem valid
return machine.Stopped, nil
}
func (m *HyperVMachine) Stop(name string, opts machine.StopOptions) error {
vmm := hypervctl.NewVirtualMachineManager()
vm, err := vmm.GetMachine(m.Name)
if err != nil {
return err
}
if vm.State() != hypervctl.Enabled {
return hypervctl.ErrMachineStateInvalid
}
return vm.Stop()
}
func (m *HyperVMachine) loadFromFile() (*HyperVMachine, error) {
if len(m.Name) < 1 {
return nil, errors.New("encountered machine with no name")
}
vmm := hypervctl.NewVirtualMachineManager()
vm, err := vmm.GetMachine(m.Name)
if err != nil {
return nil, err
}
configDir, err := machine.GetConfDir(machine.HyperVVirt)
if err != nil {
return nil, err
}
jsonPath := getVMConfigPath(configDir, m.Name)
mm := HyperVMachine{}
if err := loadMacMachineFromJSON(jsonPath, &mm); err != nil {
return nil, err
}
cfg, err := vm.GetConfig(mm.ImagePath.GetPath())
if err != nil {
return nil, err
}
// If the machine is on, we can get what it is actually using
if cfg.Hardware.CPUs > 0 {
mm.CPUs = uint64(cfg.Hardware.CPUs)
}
// Same for memory
if cfg.Hardware.Memory > 0 {
mm.Memory = uint64(cfg.Hardware.Memory)
}
mm.DiskSize = cfg.Hardware.DiskSize * units.MiB
mm.LastUp = cfg.Status.LastUp
return &mm, nil
}
// getVMConfigPath is a simple wrapper for getting the fully-qualified
// path of the vm json config file. It should be used to get conformity
func getVMConfigPath(configDir, vmName string) string {
return filepath.Join(configDir, fmt.Sprintf("%s.json", vmName))
}
func loadMacMachineFromJSON(fqConfigPath string, macMachine *HyperVMachine) error {
b, err := os.ReadFile(fqConfigPath)
if err != nil {
return err
}
return json.Unmarshal(b, macMachine)
}

View File

@ -61,7 +61,7 @@ type DynamicIgnition struct {
}
// NewIgnitionFile
func NewIgnitionFile(ign DynamicIgnition) error {
func NewIgnitionFile(ign DynamicIgnition, vmType VMType) error {
if len(ign.Name) < 1 {
ign.Name = DefaultIgnitionUserName
}
@ -187,7 +187,6 @@ ExecStartPost=/usr/bin/systemctl daemon-reload
[Install]
WantedBy=sysinit.target
`
_ = ready
ignSystemd := Systemd{
Units: []Unit{
{
@ -214,11 +213,6 @@ WantedBy=sysinit.target
Name: "remove-moby.service",
Contents: &deMoby,
},
{
Enabled: boolToPtr(true),
Name: "envset-fwcfg.service",
Contents: &envset,
},
}}
ignConfig := Config{
Ignition: ignVersion,
@ -226,6 +220,17 @@ WantedBy=sysinit.target
Storage: ignStorage,
Systemd: ignSystemd,
}
// Only qemu has the qemu firmware environment setting
if vmType == QemuVirt {
qemuUnit := Unit{
Enabled: boolToPtr(true),
Name: "envset-fwcfg.service",
Contents: &envset,
}
ignSystemd.Units = append(ignSystemd.Units, qemuUnit)
}
b, err := json.Marshal(ignConfig)
if err != nil {
return err

View File

@ -4,6 +4,7 @@
package machine
import (
"archive/zip"
"bufio"
"errors"
"fmt"
@ -13,6 +14,7 @@ import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
@ -31,7 +33,7 @@ type GenericDownload struct {
}
// NewGenericDownloader is used when the disk image is provided by the user
func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload, error) {
func NewGenericDownloader(vmType VMType, vmName, pullPath string) (DistributionDownload, error) {
var (
imageName string
)
@ -57,7 +59,7 @@ func NewGenericDownloader(vmType, vmName, pullPath string) (DistributionDownload
}
dl.VMName = vmName
dl.ImageName = imageName
dl.LocalUncompressedFile = filepath.Join(dataDir, imageName)
dl.LocalUncompressedFile = dl.GetLocalUncompressedFile(dataDir)
// The download needs to be pulled into the datadir
gd := GenericDownload{Download: dl}
@ -86,9 +88,11 @@ func supportedURL(path string) (url *url2.URL) {
}
func (d Download) GetLocalUncompressedFile(dataDir string) string {
extension := compressionFromFile(dataDir)
uncompressedFilename := d.VMName + "_" + d.ImageName
return filepath.Join(dataDir, strings.TrimSuffix(uncompressedFilename, extension.String()))
compressedFilename := d.VMName + "_" + d.ImageName
extension := compressionFromFile(compressedFilename)
uncompressedFile := strings.TrimSuffix(compressedFilename, fmt.Sprintf(".%s", extension.String()))
d.LocalUncompressedFile = filepath.Join(dataDir, uncompressedFile)
return d.LocalUncompressedFile
}
func (g GenericDownload) Get() *Download {
@ -193,6 +197,7 @@ func DownloadVMImage(downloadURL *url2.URL, imageName string, localImagePath str
}
func Decompress(localPath, uncompressedPath string) error {
var isZip bool
uncompressedFileWriter, err := os.OpenFile(uncompressedPath, os.O_CREATE|os.O_RDWR, 0600)
if err != nil {
return err
@ -201,14 +206,19 @@ func Decompress(localPath, uncompressedPath string) error {
if err != nil {
return err
}
if strings.HasSuffix(localPath, ".zip") {
isZip = true
}
compressionType := archive.DetectCompression(sourceFile)
if compressionType != archive.Uncompressed {
if compressionType != archive.Uncompressed || isZip {
fmt.Println("Extracting compressed file")
}
if compressionType == archive.Xz {
return decompressXZ(localPath, uncompressedFileWriter)
}
if isZip && runtime.GOOS == "windows" {
return decompressZip(localPath, uncompressedFileWriter)
}
return decompressEverythingElse(localPath, uncompressedFileWriter)
}
@ -280,6 +290,32 @@ func decompressEverythingElse(src string, output io.WriteCloser) error {
return err
}
func decompressZip(src string, output io.WriteCloser) error {
zipReader, err := zip.OpenReader(src)
if err != nil {
return err
}
if len(zipReader.File) != 1 {
return errors.New("machine image files should consist of a single compressed file")
}
f, err := zipReader.File[0].Open()
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
logrus.Error(err)
}
}()
defer func() {
if err := output.Close(); err != nil {
logrus.Error(err)
}
}()
_, err = io.Copy(output, f)
return err
}
func RemoveImageAfterExpire(dir string, expire time.Duration) error {
now := time.Now()
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {

View File

@ -37,7 +37,7 @@ import (
var (
// vmtype refers to qemu (vs libvirt, krun, etc).
// Could this be moved into Provider
vmtype = "qemu"
vmtype = machine.QemuVirt
)
func GetVirtualizationProvider() machine.VirtProvider {
@ -392,7 +392,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
UID: v.UID,
}
err = machine.NewIgnitionFile(ign)
err = machine.NewIgnitionFile(ign, machine.QemuVirt)
return err == nil, err
}
@ -1335,7 +1335,7 @@ func (v *MachineVM) isIncompatible() bool {
}
func (v *MachineVM) userGlobalSocketLink() (string, error) {
path, err := machine.GetDataDir(v.Name)
path, err := machine.GetDataDir(machine.QemuVirt)
if err != nil {
logrus.Errorf("Resolving data dir: %s", err.Error())
return "", err
@ -1346,7 +1346,7 @@ func (v *MachineVM) userGlobalSocketLink() (string, error) {
func (v *MachineVM) forwardSocketPath() (*machine.VMFile, error) {
sockName := "podman.sock"
path, err := machine.GetDataDir(v.Name)
path, err := machine.GetDataDir(machine.QemuVirt)
if err != nil {
logrus.Errorf("Resolving data dir: %s", err.Error())
return nil, err
@ -1743,7 +1743,7 @@ func (p *Virtualization) RemoveAndCleanMachines() error {
return prevErr
}
func (p *Virtualization) VMType() string {
func (p *Virtualization) VMType() machine.VMType {
return vmtype
}

View File

@ -27,7 +27,7 @@ type FedoraDownload struct {
machine.Download
}
func NewFedoraDownloader(vmType, vmName, releaseStream string) (machine.DistributionDownload, error) {
func NewFedoraDownloader(vmType machine.VMType, vmName, releaseStream string) (machine.DistributionDownload, error) {
downloadURL, version, arch, size, err := getFedoraDownload()
if err != nil {
return nil, err

View File

@ -29,7 +29,7 @@ import (
var (
// vmtype refers to qemu (vs libvirt, krun, etc)
vmtype = "wsl"
vmtype = machine.WSLVirt
)
const (
@ -1713,6 +1713,6 @@ func (p *Virtualization) RemoveAndCleanMachines() error {
return prevErr
}
func (p *Virtualization) VMType() string {
func (p *Virtualization) VMType() machine.VMType {
return vmtype
}

201
vendor/github.com/containers/libhvee/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,72 @@
//go:build windows
// +build windows
package hypervctl
import (
"github.com/containers/libhvee/pkg/wmiext"
)
const SyntheticDiskDriveType = "Microsoft:Hyper-V:Synthetic Disk Drive"
type SyntheticDiskDriveSettings struct {
ResourceSettings
systemSettings *SystemSettings
controllerSettings *ScsiControllerSettings
}
type diskAssociation interface {
setParent(parent string)
setHostResource(resource []string)
Path() string
}
func (d *SyntheticDiskDriveSettings) DefineVirtualHardDisk(vhdxFile string, beforeAdd func(*VirtualHardDiskStorageSettings)) (*VirtualHardDiskStorageSettings, error) {
vhd := &VirtualHardDiskStorageSettings{}
var cb func()
if beforeAdd != nil {
cb = func() {
beforeAdd(vhd)
}
}
if err := createDiskResourceInternal(d.systemSettings.Path(), d.Path(), vhdxFile, vhd, VirtualHardDiskType, cb); err != nil {
return nil, err
}
vhd.driveSettings = d
vhd.systemSettings = d.systemSettings
return vhd, nil
}
func createDiskResourceInternal(systemPath string, drivePath string, file string, settings diskAssociation, resourceType string, cb func()) error {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return err
}
defer service.Close()
if err = populateDefaults(resourceType, settings); err != nil {
return err
}
settings.setHostResource([]string{file})
settings.setParent(drivePath)
if cb != nil {
cb()
}
diskResource, err := createResourceSettingGeneric(settings, resourceType)
if err != nil {
return err
}
path, err := addResource(service, systemPath, diskResource)
if err != nil {
return err
}
return service.GetObjectAsObject(path, settings)
}

View File

@ -0,0 +1,186 @@
//go:build windows
// +build windows
package hypervctl
type DriveSettingsBuilder struct {
systemSettings *SystemSettings
err error
}
type ControllerSettingsBuilder struct {
driveSettingsBuilder DriveSettingsBuilder
controllerSettings *ScsiControllerSettings
err error
}
type SyntheticDiskDriveSettingsBuilder struct {
controllerBuilder *ControllerSettingsBuilder
driveSettings *SyntheticDiskDriveSettings
err error
}
type SyntheticDvdDriveSettingsBuilder struct {
controllerBuilder *ControllerSettingsBuilder
driveSettings *SyntheticDvdDriveSettings
err error
}
type VirtualHardDiskStorageSettingsBuilder struct {
driveBuilder *SyntheticDiskDriveSettingsBuilder
diskSettings *VirtualHardDiskStorageSettings
err error
}
type VirtualDvdDiskStorageSettingsBuilder struct {
driveBuilder *SyntheticDvdDriveSettingsBuilder
diskSettings *VirtualDvdDiskStorageSettings
err error
}
func NewDriveSettingsBuilder(systemSettings *SystemSettings) *DriveSettingsBuilder {
return &DriveSettingsBuilder{systemSettings: systemSettings}
}
func (builder *DriveSettingsBuilder) AddScsiController() *ControllerSettingsBuilder {
if builder.err != nil {
return &ControllerSettingsBuilder{driveSettingsBuilder: *builder, err: builder.err}
}
controllerSettings, err := builder.systemSettings.AddScsiController()
builder.setErr(err)
return &ControllerSettingsBuilder{
driveSettingsBuilder: *builder,
controllerSettings: controllerSettings,
err: err,
}
}
func (builder *ControllerSettingsBuilder) AddSyntheticDiskDrive(slot uint) *SyntheticDiskDriveSettingsBuilder {
if builder.err != nil {
return &SyntheticDiskDriveSettingsBuilder{controllerBuilder: builder, err: builder.err}
}
driveSettings, err := builder.controllerSettings.AddSyntheticDiskDrive(slot)
builder.setErr(err)
return &SyntheticDiskDriveSettingsBuilder{
controllerBuilder: builder,
driveSettings: driveSettings,
err: err,
}
}
func (builder *ControllerSettingsBuilder) AddSyntheticDvdDrive(slot uint) *SyntheticDvdDriveSettingsBuilder {
if builder.err != nil {
return &SyntheticDvdDriveSettingsBuilder{controllerBuilder: builder, err: builder.err}
}
driveSettings, err := builder.controllerSettings.AddSyntheticDvdDrive(slot)
builder.setErr(err)
return &SyntheticDvdDriveSettingsBuilder{
controllerBuilder: builder,
driveSettings: driveSettings,
err: err,
}
}
func (builder *SyntheticDiskDriveSettingsBuilder) DefineVirtualHardDisk(vhdxFile string, beforeAdd func(*VirtualHardDiskStorageSettings)) *VirtualHardDiskStorageSettingsBuilder {
if builder.err != nil {
return &VirtualHardDiskStorageSettingsBuilder{driveBuilder: builder, err: builder.err}
}
diskSettings, err := builder.driveSettings.DefineVirtualHardDisk(vhdxFile, beforeAdd)
builder.setErr(err)
return &VirtualHardDiskStorageSettingsBuilder{
driveBuilder: builder,
diskSettings: diskSettings,
err: err,
}
}
func (builder *SyntheticDvdDriveSettingsBuilder) DefineVirtualDvdDisk(imageFile string) *VirtualDvdDiskStorageSettingsBuilder {
if builder.err != nil {
return &VirtualDvdDiskStorageSettingsBuilder{driveBuilder: builder, err: builder.err}
}
diskSettings, err := builder.driveSettings.DefineVirtualDvdDisk(imageFile)
builder.setErr(err)
return &VirtualDvdDiskStorageSettingsBuilder{
driveBuilder: builder,
diskSettings: diskSettings,
err: err,
}
}
func (builder *SyntheticDvdDriveSettingsBuilder) setErr(err error) {
builder.err = err
builder.controllerBuilder.setErr(err)
}
func (builder *SyntheticDiskDriveSettingsBuilder) setErr(err error) {
builder.err = err
builder.controllerBuilder.setErr(err)
}
func (builder *ControllerSettingsBuilder) setErr(err error) {
builder.err = err
builder.driveSettingsBuilder.setErr(err)
}
func (builder *DriveSettingsBuilder) setErr(err error) {
builder.err = err
}
func (builder *ControllerSettingsBuilder) Finish() *DriveSettingsBuilder {
return &builder.driveSettingsBuilder
}
func (builder *VirtualHardDiskStorageSettingsBuilder) Finish() *SyntheticDiskDriveSettingsBuilder {
return builder.driveBuilder
}
func (builder *VirtualDvdDiskStorageSettingsBuilder) Finish() *SyntheticDvdDriveSettingsBuilder {
return builder.driveBuilder
}
func (builder *SyntheticDiskDriveSettingsBuilder) Finish() *ControllerSettingsBuilder {
return builder.controllerBuilder
}
func (builder *SyntheticDvdDriveSettingsBuilder) Finish() *ControllerSettingsBuilder {
return builder.controllerBuilder
}
func (builder *VirtualHardDiskStorageSettingsBuilder) Get(s **VirtualHardDiskStorageSettings) *VirtualHardDiskStorageSettingsBuilder {
*s = builder.diskSettings
return builder
}
func (builder *VirtualDvdDiskStorageSettingsBuilder) Get(s **VirtualDvdDiskStorageSettings) *VirtualDvdDiskStorageSettingsBuilder {
*s = builder.diskSettings
return builder
}
func (builder *SyntheticDiskDriveSettingsBuilder) Get(s **SyntheticDiskDriveSettings) *SyntheticDiskDriveSettingsBuilder {
*s = builder.driveSettings
return builder
}
func (builder *SyntheticDvdDriveSettingsBuilder) Get(s **SyntheticDvdDriveSettings) *SyntheticDvdDriveSettingsBuilder {
*s = builder.driveSettings
return builder
}
func (builder *ControllerSettingsBuilder) Get(s **ScsiControllerSettings) *ControllerSettingsBuilder {
*s = builder.controllerSettings
return builder
}
func (builder *DriveSettingsBuilder) Complete() error {
return builder.err
}

View File

@ -0,0 +1,24 @@
//go:build windows
// +build windows
package hypervctl
const SyntheticDvdDriveType = "Microsoft:Hyper-V:Synthetic DVD Drive"
type SyntheticDvdDriveSettings struct {
ResourceSettings
systemSettings *SystemSettings
controllerSettings *ScsiControllerSettings
}
func (d *SyntheticDvdDriveSettings) DefineVirtualDvdDisk(imageFile string) (*VirtualDvdDiskStorageSettings, error) {
vdvd := &VirtualDvdDiskStorageSettings{}
if err := createDiskResourceInternal(d.systemSettings.Path(), d.Path(), imageFile, vdvd, VirtualDvdDiskType, nil); err != nil {
return nil, err
}
vdvd.driveSettings = d
vdvd.systemSettings = d.systemSettings
return vdvd, nil
}

View File

@ -0,0 +1,117 @@
//go:build windows
// +build windows
package hypervctl
import (
"errors"
"fmt"
)
// VM State errors
var (
ErrMachineAlreadyRunning = errors.New("machine already running")
ErrMachineNotRunning = errors.New("machine not running")
ErrMachineStateInvalid = errors.New("machine in invalid state for action")
ErrMachineStarting = errors.New("machine is currently starting")
)
// VM Creation errors
var (
ErrMachineAlreadyExists = errors.New("machine already exists")
)
type DestroySystemResult int32
// VM Destroy Exit Codes
const (
VMDestroyCompletedwithNoError DestroySystemResult = 0
VMDestroyNotSupported DestroySystemResult = 1
VMDestroyFailed DestroySystemResult = 2
VMDestroyTimeout DestroySystemResult = 3
VMDestroyInvalidParameter DestroySystemResult = 4
VMDestroyInvalidState DestroySystemResult = 5
)
func (e DestroySystemResult) Reason() string {
switch e {
case VMDestroyNotSupported:
return "not supported"
case VMDestroyFailed:
return "failed"
case VMDestroyTimeout:
return "timeout"
case VMDestroyInvalidParameter:
return "invalid parameter"
case VMDestroyInvalidState:
return "invalid state"
}
return "Unknown"
}
// Shutdown operation error codes
const (
ErrShutdownFailed = 32768
ErrShutdownAccessDenied = 32769
ErrShutdownNotSupported = 32770
ErrShutdownStatusUnkown = 32771
ErrShutdownTimeout = 32772
ErrShutdownInvalidParameter = 32773
ErrShutdownSystemInUse = 32774
ErrShutdownInvalidState = 32775
ErrShutdownIncorrectData = 32776
ErrShutdownNotAvailable = 32777
ErrShutdownOutOfMemory = 32778
ErrShutdownFileNotFound = 32779
ErrShutdownNotReady = 32780
ErrShutdownMachineLocked = 32781
ErrShutdownInProgress = 32782
)
type shutdownCompError struct {
errorCode int
message string
}
func (s *shutdownCompError) Error() string {
return fmt.Sprintf("%s (%d)", s.message, s.errorCode)
}
func translateShutdownError(code int) error {
var message string
switch code {
case ErrShutdownFailed:
message = "shutdown failed"
case ErrShutdownAccessDenied:
message = "access was denied"
case ErrShutdownNotSupported:
message = "shutdown not supported by virtual machine"
case ErrShutdownStatusUnkown:
message = "virtual machine status is unknown"
case ErrShutdownTimeout:
message = "timeout starting shutdown"
case ErrShutdownInvalidParameter:
message = "invalid parameter"
case ErrShutdownSystemInUse:
message = "system in use"
case ErrShutdownInvalidState:
message = "virtual machine is in an invalid state for shutdown"
case ErrShutdownIncorrectData:
message = "incorrect data type"
case ErrShutdownNotAvailable:
message = "system is not available"
case ErrShutdownOutOfMemory:
message = "out of memory"
case ErrShutdownFileNotFound:
message = "file not found"
case ErrShutdownMachineLocked:
message = "machine is locked and cannot be shut down without the force option"
case ErrShutdownInProgress:
message = "shutdown is already in progress"
default:
message = "unknown error"
}
return &shutdownCompError{code, message}
}

View File

@ -0,0 +1,50 @@
//go:build windows
// +build windows
package hypervctl
const EthernetPortAllocationResourceType = "Microsoft:Hyper-V:Ethernet Connection"
type EthernetPortAllocationSettings struct {
InstanceID string // = "Microsoft:GUID\DeviceSpecificData"
Caption string // = "Ethernet Switch Port Settings"
Description string // = "Ethernet Switch Port Settings"
ElementName string
ResourceType uint16 // = 33
OtherResourceType string
ResourceSubType string
PoolID string
ConsumerVisibility uint16 // = 3
HostResource []string
AllocationUnits string
VirtualQuantity uint64
Reservation uint64
Limit uint64
Weight uint32 // = 0
AutomaticAllocation bool
AutomaticDeallocation bool
Parent string
Connection []string
Address string
MappingBehavior uint16
AddressOnParent string
VirtualQuantityUnits string // = "count"
DesiredVLANEndpointMode uint16
OtherEndpointMode string
EnabledState uint16
LastKnownSwitchName string
RequiredFeatures []string
RequiredFeatureHints []string
TestReplicaPoolID string
TestReplicaSwitchName string
CompartmentGuid string
}
func fetchEthernetPortAllocationSettings() (*EthernetPortAllocationSettings, error) {
settings := &EthernetPortAllocationSettings{}
return settings, populateDefaults(EthernetPortAllocationResourceType, settings)
}
func creatEthernetPortAllocationSettings(settings *EthernetPortAllocationSettings) (string, error) {
return createResourceSettingGeneric(settings, EthernetPortAllocationResourceType)
}

View File

@ -0,0 +1,109 @@
//go:build windows
// +build windows
package hypervctl
import (
"fmt"
"github.com/containers/libhvee/pkg/wmiext"
)
const SyntheticEthernetPortResourceType = "Microsoft:Hyper-V:Synthetic Ethernet Port"
const DefaultSwitchId = "C08CB7B8-9B3C-408E-8E30-5E16A3AEB444"
type SyntheticEthernetPortSettings struct {
S__PATH string
InstanceID string
Caption string // = "Virtual Ethernet Port Default Settings"
Description string // = "Describes the default settings for the virtual Ethernet port resources."
ElementName string
ResourceType uint16 // = 10
OtherResourceType string
ResourceSubType string // = "Microsoft:Hyper-V:Synthetic Ethernet Port"
PoolID string
ConsumerVisibility uint16 // = 3
HostResource []string
AllocationUnits string // = "count"
VirtualQuantity uint64 // = 1
Reservation uint64 // = 1
Limit uint64 // = 1
Weight uint32 // = 0
AutomaticAllocation bool // = True
AutomaticDeallocation bool // = True
Parent string
Connection []string
Address string
MappingBehavior uint16
AddressOnParent string
VirtualQuantityUnits string // = "count"
DesiredVLANEndpointMode uint16
OtherEndpointMode string
VirtualSystemIdentifiers []string
DeviceNamingEnabled bool // = FALSE
AllowPacketDirect bool // = FALSE
StaticMacAddress bool // = False
ClusterMonitored bool // = TRUE
systemSettings *SystemSettings
}
func (p *SyntheticEthernetPortSettings) Path() string {
return p.S__PATH
}
func (p *SyntheticEthernetPortSettings) DefineEthernetPortConnection(switchName string) (*EthernetPortAllocationSettings, error) {
const wqlFormat = "select * from Msvm_VirtualEthernetSwitch where %s = '%s'"
var wqlProperty, wqlValue string
if len(switchName) > 0 {
wqlProperty = "ElementName"
wqlValue = switchName
} else {
wqlProperty = "Name"
wqlValue = DefaultSwitchId
}
wql := fmt.Sprintf(wqlFormat, wqlProperty, wqlValue)
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return nil, err
}
defer service.Close()
switchInst, err := service.FindFirstInstance(wql)
if err != nil {
return nil, err
}
defer switchInst.Close()
switchPath, err := switchInst.Path()
if err != nil {
return nil, err
}
connectSettings, err := fetchEthernetPortAllocationSettings()
if err != nil {
return nil, err
}
connectSettings.Parent = p.Path()
connectSettings.HostResource = append(connectSettings.HostResource, switchPath)
resource, err := creatEthernetPortAllocationSettings(connectSettings)
if err != nil {
return nil, err
}
path, err := addResource(service, p.systemSettings.Path(), resource)
if err != nil {
return nil, err
}
if err := service.GetObjectAsObject(path, connectSettings); err != nil {
return nil, err
}
return connectSettings, nil
}

View File

@ -0,0 +1,138 @@
//go:build windows
// +build windows
package hypervctl
import (
"encoding/xml"
"fmt"
"io"
"strings"
"github.com/containers/libhvee/pkg/wmiext"
)
const (
KvpOperationFailed = 32768
KvpAccessDenied = 32769
KvpNotSupported = 32770
KvpStatusUnknown = 32771
KvpTimeoutOccurred = 32772
KvpIllegalArgument = 32773
KvpSystemInUse = 32774
KvpInvalidState = 32775
KvpIncorrectDataType = 32776
KvpSystemNotAvailable = 32777
KvpOutOfMemory = 32778
KvpNotFound = 32779
KvpExchangeDataItemName = "Msvm_KvpExchangeDataItem"
MemorySettingDataName = "Msvm_MemorySettingData"
)
type CimKvpItems struct {
Instances []CimKvpItem `xml:"INSTANCE"`
}
type CimKvpItem struct {
Properties []CimKvpItemProperty `xml:"PROPERTY"`
}
type CimKvpItemProperty struct {
Name string `xml:"NAME,attr"`
Value string `xml:"VALUE"`
}
type KvpError struct {
ErrorCode int
message string
}
func (k *KvpError) Error() string {
return fmt.Sprintf("%s (%d)", k.message, k.ErrorCode)
}
func createKvpItem(service *wmiext.Service, key string, value string) (string, error) {
item, err := service.SpawnInstance(KvpExchangeDataItemName)
if err != nil {
return "", err
}
defer item.Close()
_ = item.Put("Name", key)
_ = item.Put("Data", value)
_ = item.Put("Source", 0)
itemStr := item.GetCimText()
return itemStr, nil
}
func parseKvpMapXml(kvpXml string) (map[string]string, error) {
// Workaround XML decoder's inability to handle multiple root elements
r := io.MultiReader(
strings.NewReader("<root>"),
strings.NewReader(kvpXml),
strings.NewReader("</root>"),
)
var items CimKvpItems
if err := xml.NewDecoder(r).Decode(&items); err != nil {
return nil, err
}
ret := make(map[string]string)
for _, item := range items.Instances {
var key, value string
for _, prop := range item.Properties {
if strings.EqualFold(prop.Name, "Name") {
key = prop.Value
} else if strings.EqualFold(prop.Name, "Data") {
value = prop.Value
}
}
if len(key) > 0 {
ret[key] = value
}
}
return ret, nil
}
func translateKvpError(source error, illegalSuggestion string) error {
j, ok := source.(*wmiext.JobError)
if !ok {
return source
}
var message string
switch j.ErrorCode {
case KvpOperationFailed:
message = "Operation failed"
case KvpAccessDenied:
message = "Access denied"
case KvpNotSupported:
message = "Not supported"
case KvpStatusUnknown:
message = "Status is unknown"
case KvpTimeoutOccurred:
message = "Timeout occurred"
case KvpIllegalArgument:
message = "Illegal argument (" + illegalSuggestion + ")"
case KvpSystemInUse:
message = "System is in use"
case KvpInvalidState:
message = "Invalid state for this operation"
case KvpIncorrectDataType:
message = "Incorrect data type"
case KvpSystemNotAvailable:
message = "System is not available"
case KvpOutOfMemory:
message = "Out of memory"
case KvpNotFound:
message = "Not found"
default:
return source
}
return &KvpError{j.ErrorCode, message}
}

View File

@ -0,0 +1,50 @@
//go:build windows
// +build windows
package hypervctl
const MemoryResourceType = "Microsoft:Hyper-V:Memory"
type MemorySettings struct {
S__PATH string
InstanceID string
Caption string // = "Memory Default Settings"
Description string // = "Describes the default settings for the memory resources."
ElementName string
ResourceType uint16 // = 4
OtherResourceType string
ResourceSubType string // = "Microsoft:Hyper-V:Memory"
PoolID string
ConsumerVisibility uint16
HostResource []string
HugePagesEnabled bool
AllocationUnits string // = "byte * 2^20"
VirtualQuantity uint64
Reservation uint64
Limit uint64
Weight uint32
AutomaticAllocation bool // = True
AutomaticDeallocation bool // = True
Parent string
Connection []string
Address string
MappingBehavior uint16
AddressOnParent string
VirtualQuantityUnits string // = "byte * 2^20"
DynamicMemoryEnabled bool
TargetMemoryBuffer uint32
IsVirtualized bool // = True
SwapFilesInUse bool
MaxMemoryBlocksPerNumaNode uint64
SgxSize uint64
SgxEnabled bool
}
func createMemorySettings(settings *MemorySettings) (string, error) {
return createResourceSettingGeneric(settings, MemoryResourceType)
}
func fetchDefaultMemorySettings() (*MemorySettings, error) {
settings := &MemorySettings{}
return settings, populateDefaults(MemoryResourceType, settings)
}

View File

@ -0,0 +1,86 @@
//go:build windows
// +build windows
package hypervctl
type NetworkSettingsBuilder struct {
systemSettings *SystemSettings
err error
}
type SyntheticEthernetPortSettingsBuilder struct {
networkSettingsBuilder *NetworkSettingsBuilder
portSettings *SyntheticEthernetPortSettings
err error
}
type EthernetPortAllocationSettingsBuilder struct {
portSettingsBuilder *SyntheticEthernetPortSettingsBuilder
allocSettings *EthernetPortAllocationSettings
err error
}
func NewNetworkSettingsBuilder(systemSettings *SystemSettings) *NetworkSettingsBuilder {
return &NetworkSettingsBuilder{systemSettings: systemSettings}
}
func (builder *NetworkSettingsBuilder) AddSyntheticEthernetPort(beforeAdd func(*SyntheticEthernetPortSettings)) *SyntheticEthernetPortSettingsBuilder {
if builder.err != nil {
return &SyntheticEthernetPortSettingsBuilder{networkSettingsBuilder: builder, err: builder.err}
}
portSettings, err := builder.systemSettings.AddSyntheticEthernetPort(beforeAdd)
builder.setErr(err)
return &SyntheticEthernetPortSettingsBuilder{
networkSettingsBuilder: builder,
portSettings: portSettings,
err: err,
}
}
func (builder *SyntheticEthernetPortSettingsBuilder) AddEthernetPortAllocation(switchName string) *EthernetPortAllocationSettingsBuilder {
if builder.err != nil {
return &EthernetPortAllocationSettingsBuilder{portSettingsBuilder: builder, err: builder.err}
}
allocSettings, err := builder.portSettings.DefineEthernetPortConnection(switchName)
builder.setErr(err)
return &EthernetPortAllocationSettingsBuilder{
portSettingsBuilder: builder,
allocSettings: allocSettings,
err: err,
}
}
func (builder *SyntheticEthernetPortSettingsBuilder) Finish() *NetworkSettingsBuilder {
return builder.networkSettingsBuilder
}
func (builder *EthernetPortAllocationSettingsBuilder) Finish() *SyntheticEthernetPortSettingsBuilder {
return builder.portSettingsBuilder
}
func (builder *NetworkSettingsBuilder) setErr(err error) {
builder.err = err
}
func (builder *SyntheticEthernetPortSettingsBuilder) setErr(err error) {
builder.err = err
builder.networkSettingsBuilder.setErr(err)
}
func (builder *EthernetPortAllocationSettingsBuilder) Get(s **EthernetPortAllocationSettings) *EthernetPortAllocationSettingsBuilder {
*s = builder.allocSettings
return builder
}
func (builder *SyntheticEthernetPortSettingsBuilder) Get(s **SyntheticEthernetPortSettings) *SyntheticEthernetPortSettingsBuilder {
*s = builder.portSettings
return builder
}
func (builder *NetworkSettingsBuilder) Complete() error {
return builder.err
}

View File

@ -0,0 +1,92 @@
//go:build windows
// +build windows
package hypervctl
const ProcessorResourceType = "Microsoft:Hyper-V:Processor"
/*
AllocationUnits : percent / 1000
AllowACountMCount : True
AutomaticAllocation : True
AutomaticDeallocation : True
Caption : Processor
Connection :
ConsumerVisibility : 3
CpuGroupId : 00000000-0000-0000-0000-000000000000
Description : Settings for Microsoft Virtual Processor.
DisableSpeculationControls : False
ElementName : Processor
EnableHostResourceProtection : False
EnableLegacyApicMode : False
EnablePageShattering : 0
EnablePerfmonIpt : False
EnablePerfmonLbr : False
EnablePerfmonPebs : False
EnablePerfmonPmu : False
ExposeVirtualizationExtensions : False
HideHypervisorPresent : False
HostResource :
HwThreadsPerCore : 0
InstanceID : Microsoft:B5314955-3924-42BA-ABF9-793993D340A0\b637f346-6a0e-4dec-af52-bd70cb80a21d\0
Limit : 100000
LimitCPUID : False
LimitProcessorFeatures : False
MappingBehavior :
MaxNumaNodesPerSocket : 1
MaxProcessorsPerNumaNode : 4
OtherResourceType :
Parent :
PoolID :
Reservation : 0
ResourceSubType : Microsoft:Hyper-V:Processor
ResourceType : 3
VirtualQuantity : 2
VirtualQuantityUnits : count
Weight : 100
*/
type ProcessorSettings struct {
S__PATH string
InstanceID string
Caption string // = "Processor"
Description string // = "A logical processor of the hypervisor running on the host computer system."
ElementName string
ResourceType uint16 // = 3
OtherResourceType string
ResourceSubType string // = "Microsoft:Hyper-V:Processor"
PoolID string
ConsumerVisibility uint16
HostResource []string
AllocationUnits string // = "percent / 1000"
VirtualQuantity uint64 // = "count"
Reservation uint64 // = 0
Limit uint64 // = 100000
Weight uint32 // = 100
AutomaticAllocation bool // = True
AutomaticDeallocation bool // = True
Parent string
Connection []string
Address string
MappingBehavior uint16
AddressOnParent string
VirtualQuantityUnits string // = "count"
LimitCPUID bool
HwThreadsPerCore uint64
LimitProcessorFeatures bool
MaxProcessorsPerNumaNode uint64
MaxNumaNodesPerSocket uint64
EnableHostResourceProtection bool
CpuGroupId string
HideHypervisorPresent bool
ExposeVirtualizationExtensions bool
}
func fetchDefaultProcessorSettings() (*ProcessorSettings, error) {
settings := &ProcessorSettings{}
return settings, populateDefaults(ProcessorResourceType, settings)
}
func createProcessorSettings(settings *ProcessorSettings) (string, error) {
return createResourceSettingGeneric(settings, ProcessorResourceType)
}

View File

@ -0,0 +1,135 @@
//go:build windows
// +build windows
package hypervctl
import (
"errors"
"fmt"
"github.com/containers/libhvee/pkg/wmiext"
)
type ResourceSettings struct {
S__PATH string
InstanceID string // = "Microsoft:GUID\DeviceSpecificData"
Caption string
Description string
ElementName string
ResourceType uint16
OtherResourceType string
ResourceSubType string
PoolID string
ConsumerVisibility uint16
HostResource []string
AllocationUnits string
VirtualQuantity uint64
Reservation uint64
Limit uint64
Weight uint32
AutomaticAllocation bool
AutomaticDeallocation bool
Parent string
Connection []string
Address string
MappingBehavior uint16
AddressOnParent string
VirtualQuantityUnits string // = "count"
VirtualSystemIdentifiers []string // = { "GUID" }
}
func (s *ResourceSettings) setParent(parent string) {
s.Parent = parent
}
func (s *ResourceSettings) setAddressOnParent(address string) {
s.AddressOnParent = address
}
func (s *ResourceSettings) Path() string {
return s.S__PATH
}
func createResourceSettingGeneric(settings interface{}, resourceType string) (string, error) {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return "", err
}
ref, err := findResourceDefaults(service, resourceType)
if err != nil {
return "", err
}
resource, err := service.GetObject(ref)
if err != nil {
return "", err
}
defer resource.Close()
resource, err = resource.CloneInstance()
if err != nil {
return "", err
}
defer resource.Close()
if err = resource.PutAll(settings); err != nil {
return "", err
}
return resource.GetCimText(), nil
}
func populateDefaults(subType string, settings interface{}) error {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return err
}
defer service.Close()
ref, err := findResourceDefaults(service, subType)
if err != nil {
return err
}
return service.GetObjectAsObject(ref, settings)
}
func findResourceDefaults(service *wmiext.Service, subType string) (string, error) {
wql := fmt.Sprintf("SELECT * FROM Msvm_AllocationCapabilities WHERE ResourceSubType = '%s'", subType)
instance, err := service.FindFirstInstance(wql)
if err != nil {
return "", err
}
defer instance.Close()
path, err := instance.Path()
if err != nil {
return "", err
}
enum, err := service.ExecQuery(fmt.Sprintf("references of {%s} where ResultClass = Msvm_SettingsDefineCapabilities", path))
if err != nil {
return "", err
}
defer enum.Close()
for {
entry, err := enum.Next()
if err != nil {
return "", err
}
if entry == nil {
return "", errors.New("could not find settings definition for resource")
}
value, vErr := entry.GetAsUint("ValueRole")
ref, pErr := entry.GetAsString("PartComponent")
entry.Close()
if vErr == nil && pErr == nil && value == 0 {
return ref, nil
}
}
}

View File

@ -0,0 +1,69 @@
//go:build windows
// +build windows
package hypervctl
import (
"fmt"
"github.com/containers/libhvee/pkg/wmiext"
)
type ScsiControllerSettings struct {
ResourceSettings
systemSettings *SystemSettings
}
type driveAssociation interface {
setParent(parent string)
setAddressOnParent(address string)
}
func (c *ScsiControllerSettings) AddSyntheticDiskDrive(slot uint) (*SyntheticDiskDriveSettings, error) {
drive := &SyntheticDiskDriveSettings{}
if err := c.createSyntheticDriveInternal(slot, drive, SyntheticDiskDriveType); err != nil {
return nil, err
}
drive.systemSettings = c.systemSettings
drive.controllerSettings = c
return drive, nil
}
func (c *ScsiControllerSettings) AddSyntheticDvdDrive(slot uint) (*SyntheticDvdDriveSettings, error) {
drive := &SyntheticDvdDriveSettings{}
if err := c.createSyntheticDriveInternal(slot, drive, SyntheticDvdDriveType); err != nil {
return nil, err
}
drive.systemSettings = c.systemSettings
drive.controllerSettings = c
return drive, nil
}
func (c *ScsiControllerSettings) createSyntheticDriveInternal(slot uint, settings driveAssociation, resourceType string) error {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return err
}
defer service.Close()
if err = populateDefaults(resourceType, settings); err != nil {
return err
}
settings.setParent(c.Path())
settings.setAddressOnParent(fmt.Sprintf("%d", slot))
driveResource, err := createResourceSettingGeneric(settings, resourceType)
if err != nil {
return err
}
path, err := addResource(service, c.systemSettings.Path(), driveResource)
if err != nil {
return err
}
err = service.GetObjectAsObject(path, settings)
return err
}

View File

@ -0,0 +1,61 @@
//go:build windows
// +build windows
package hypervctl
type StorageAllocationSettings struct {
S__PATH string
InstanceID string
Caption string // = "Hard Disk Image Default Settings"
Description string // = "Describes the default settings for the hard disk image resources"
ElementName string
ResourceType uint16
OtherResourceType string
ResourceSubType string
PoolID string
ConsumerVisibility uint16
HostResource []string
AllocationUnits string
VirtualQuantity uint64
Limit uint64 // = 1
Weight uint32
StorageQoSPolicyID string
AutomaticAllocation bool
AutomaticDeallocation bool
Parent string
Connection []string
Address string
MappingBehavior uint16
AddressOnParent string
VirtualResourceBlockSize uint64
VirtualQuantityUnits string // = "count(fixed size block)"
Access uint16
HostResourceBlockSize uint64
Reservation uint64
HostExtentStartingAddress uint64
HostExtentName string
HostExtentNameFormat uint16
OtherHostExtentNameFormat string
HostExtentNameNamespace uint16
OtherHostExtentNameNamespace string
IOPSLimit uint64
IOPSReservation uint64
IOPSAllocationUnits string
PersistentReservationsSupported bool
CachingMode uint16
SnapshotId string // = ""
IgnoreFlushes bool
WriteHardeningMethod uint16
}
func (s *StorageAllocationSettings) setParent(parent string) {
s.Parent = parent
}
func (s *StorageAllocationSettings) setHostResource(resource []string) {
s.HostResource = resource
}
func (s *StorageAllocationSettings) Path() string {
return s.S__PATH
}

View File

@ -0,0 +1,203 @@
//go:build windows
// +build windows
package hypervctl
import "time"
const (
SummaryRequestName = 0
SummaryRequestElementName = 1
SummaryRequestCreationTime = 2
SummaryRequestNotes = 3
SummaryRequestProcessors = 4
SummaryRequestSmallThumbnail = 5
SummaryRequestMediumThumbnail = 6
SummaryRequestLargeThumbnail = 7
SummaryRequestAllocatedGPU = 8
SummaryRequestVirtualSwitchNames = 9
SummaryRequestVersion = 10
SummaryRequestShielded = 11
SummaryRequestEnabledState = 100
SummaryRequestProcessorLoad = 101
SummaryRequestProcessorLoadHistory = 102
SummaryRequestMemoryUsage = 103
SummaryRequestHeartbeat = 104
SummaryRequestUptime = 105
SummaryRequestGuestOperatingSystem = 106
SummaryRequestSnapshots = 107
SummaryRequestAsynchronousTasks = 108
SummaryRequestHealthState = 109
SummaryRequestOperationalStatus = 110
SummaryRequestStatusDescriptions = 111
SummaryRequestMemoryAvailable = 112
SummaryRequestMemoryBuffer = 113
SummaryRequestReplicationMode = 114
SummaryRequestReplicationState = 115
SummaryRequestReplicationHealth = 116
SummaryRequestApplicationHealth = 117
SummaryRequestReplicationStateEx = 118
SummaryRequestReplicationHealthEx = 119
SummaryRequestSwapFilesInUse = 120
SummaryRequestIntegrationServicesVersionState = 121
SummaryRequestReplicationProvider = 122
SummaryRequestMemorySpansPhysicalNumaNodes = 123
SummaryRequestIntegrationServicesVersionState2 = 132
SummaryRequestOtherEnabledState = 132
)
type SummaryRequestSet []uint
var (
// SummaryRequestCommon includes a smaller subset of commonly used fields
SummaryRequestCommon = SummaryRequestSet{
SummaryRequestName,
SummaryRequestElementName,
SummaryRequestCreationTime,
SummaryRequestNotes,
SummaryRequestProcessors,
SummaryRequestEnabledState,
SummaryRequestProcessorLoad,
SummaryRequestMemoryUsage,
SummaryRequestHeartbeat,
SummaryRequestUptime,
SummaryRequestGuestOperatingSystem,
SummaryRequestHealthState,
SummaryRequestOperationalStatus,
SummaryRequestStatusDescriptions,
SummaryRequestMemoryAvailable,
SummaryRequestMemoryBuffer,
SummaryRequestSwapFilesInUse,
}
// SummaryRequestNearAll includes everything but load history and thumbnails
SummaryRequestNearAll = SummaryRequestSet{
SummaryRequestName,
SummaryRequestElementName,
SummaryRequestCreationTime,
SummaryRequestNotes,
SummaryRequestProcessors,
SummaryRequestAllocatedGPU,
SummaryRequestVirtualSwitchNames,
SummaryRequestVersion,
SummaryRequestShielded,
SummaryRequestEnabledState,
SummaryRequestProcessorLoad,
SummaryRequestMemoryUsage,
SummaryRequestHeartbeat,
SummaryRequestUptime,
SummaryRequestGuestOperatingSystem,
SummaryRequestSnapshots,
SummaryRequestAsynchronousTasks,
SummaryRequestHealthState,
SummaryRequestOperationalStatus,
SummaryRequestStatusDescriptions,
SummaryRequestMemoryAvailable,
SummaryRequestMemoryBuffer,
SummaryRequestReplicationMode,
SummaryRequestReplicationState,
SummaryRequestReplicationHealth,
SummaryRequestApplicationHealth,
SummaryRequestReplicationStateEx,
SummaryRequestReplicationHealthEx,
SummaryRequestSwapFilesInUse,
SummaryRequestIntegrationServicesVersionState,
SummaryRequestReplicationProvider,
SummaryRequestMemorySpansPhysicalNumaNodes,
SummaryRequestIntegrationServicesVersionState2,
SummaryRequestOtherEnabledState,
}
)
// SummaryInformation https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-summaryinformation
type SummaryInformation struct {
InstanceID string
AllocatedGPU string
Shielded bool
AsynchronousTasks []ConcreteJob
CreationTime time.Time
ElementName string
EnabledState uint16
OtherEnabledState string
GuestOperatingSystem string
HealthState uint16
Heartbeat uint16
MemoryUsage uint64
MemoryAvailable int32
AvailableMemoryBuffer int32
SwapFilesInUse bool
Name string
Notes string
Version string
NumberOfProcessors uint16
OperationalStatus []uint16
ProcessorLoad uint16
ProcessorLoadHistory []uint16
Snapshots []SystemSettings
StatusDescriptions []string
ThumbnailImage []uint8
ThumbnailImageHeight uint16
ThumbnailImageWidth uint16
UpTime uint64
ReplicationState uint16
ReplicationStateEx []uint16
ReplicationHealth uint16
ReplicationHealthEx []uint16
ReplicationMode uint16
TestReplicaSystem string // REF to CIM_ComputerSystem
ApplicationHealth uint16
IntegrationServicesVersionState uint16
MemorySpansPhysicalNumaNodes bool
ReplicationProviderId []string
EnhancedSessionModeState uint16
VirtualSwitchNames []string
VirtualSystemSubType string
HostComputerSystemName string
}
// CIM_ConcreteJob https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-concretejob
type ConcreteJob struct {
InstanceID string
Caption string
Description string
ElementName string
InstallDate time.Time
Name string
OperationalStatus []uint16 // = { 2 }
StatusDescriptions []string // = { "OK" }
Status string
HealthState uint16 // = 5
CommunicationStatus uint16
DetailedStatus uint16
OperatingStatus uint16
PrimaryStatus uint16
JobStatus string
TimeSubmitted time.Time
ScheduledStartTime time.Time
StartTime time.Time
ElapsedTime time.Duration
JobRunTimes uint32
RunMonth uint8
RunDay int8
RunDayOfWeek int8
RunStartInterval time.Time
LocalOrUtcTime uint16
UntilTime time.Time
Notify string
Owner string
Priority uint32
PercentComplete uint16
DeleteOnCompletion bool
ErrorCode uint16
ErrorDescription string
ErrorSummaryDescription string
RecoveryAction uint16
OtherRecoveryAction string
JobState uint16
TimeOfLastStateChange time.Time
TimeBeforeRemoval time.Duration // =
Cancellable bool
JobType uint16
}

View File

@ -0,0 +1,207 @@
//go:build windows
// +build windows
package hypervctl
import (
"fmt"
"time"
"github.com/containers/libhvee/pkg/wmiext"
)
type SystemSettings struct {
S__PATH string
InstanceID string
Caption string // = "Virtual Machine Settings"
Description string
ElementName string
VirtualSystemIdentifier string
VirtualSystemType string
Notes []string
CreationTime time.Time
ConfigurationID string
ConfigurationDataRoot string
ConfigurationFile string
SnapshotDataRoot string
SuspendDataRoot string
SwapFileDataRoot string
LogDataRoot string
AutomaticStartupAction uint16 // non-zero
AutomaticStartupActionDelay time.Duration
AutomaticStartupActionSequenceNumber uint16
AutomaticShutdownAction uint16 // non-zero
AutomaticRecoveryAction uint16 // non-zero
RecoveryFile string
BIOSGUID string
BIOSSerialNumber string
BaseBoardSerialNumber string
ChassisSerialNumber string
Architecture string
ChassisAssetTag string
BIOSNumLock bool
BootOrder []uint16
Parent string
UserSnapshotType uint16 // non-zero
IsSaved bool
AdditionalRecoveryInformation string
AllowFullSCSICommandSet bool
DebugChannelId uint32
DebugPortEnabled uint16
DebugPort uint32
Version string
IncrementalBackupEnabled bool
VirtualNumaEnabled bool
AllowReducedFcRedundancy bool // = False
VirtualSystemSubType string
BootSourceOrder []string
PauseAfterBootFailure bool
NetworkBootPreferredProtocol uint16 // non-zero
GuestControlledCacheTypes bool
AutomaticSnapshotsEnabled bool
IsAutomaticSnapshot bool
GuestStateFile string
GuestStateDataRoot string
LockOnDisconnect bool
ParentPackage string
AutomaticCriticalErrorActionTimeout time.Duration
AutomaticCriticalErrorAction uint16
ConsoleMode uint16
SecureBootEnabled bool
SecureBootTemplateId string
LowMmioGapSize uint64
HighMmioGapSize uint64
EnhancedSessionTransportType uint16
}
func DefaultSystemSettings() *SystemSettings {
return &SystemSettings{
// setup all non-zero settings
AutomaticStartupAction: 2, // no auto-start
AutomaticShutdownAction: 4, // shutdown
AutomaticRecoveryAction: 3, // restart
UserSnapshotType: 2, // no snapshotting
NetworkBootPreferredProtocol: 4096, // ipv4 for pxe
VirtualSystemSubType: "Microsoft:Hyper-V:SubType:2",
}
}
func (s *SystemSettings) Path() string {
return s.S__PATH
}
func (s *SystemSettings) AddScsiController() (*ScsiControllerSettings, error) {
const scsiControllerType = "Microsoft:Hyper-V:Synthetic SCSI Controller"
controller := &ScsiControllerSettings{}
if err := s.createSystemResourceInternal(controller, scsiControllerType, nil); err != nil {
return nil, err
}
controller.systemSettings = s
return controller, nil
}
func (s *SystemSettings) createSystemResourceInternal(settings interface{}, resourceType string, cb func()) error {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return err
}
defer service.Close()
if err = populateDefaults(resourceType, settings); err != nil {
return err
}
if cb != nil {
cb()
}
resourceStr, err := createResourceSettingGeneric(settings, resourceType)
if err != nil {
return err
}
path, err := addResource(service, s.Path(), resourceStr)
if err != nil {
return err
}
err = service.GetObjectAsObject(path, settings)
return err
}
func (s *SystemSettings) AddSyntheticEthernetPort(beforeAdd func(*SyntheticEthernetPortSettings)) (*SyntheticEthernetPortSettings, error) {
const networkAdapterType = SyntheticEthernetPortResourceType
port := &SyntheticEthernetPortSettings{}
var cb func()
if beforeAdd != nil {
cb = func() {
beforeAdd(port)
}
}
if err := s.createSystemResourceInternal(port, networkAdapterType, cb); err != nil {
return nil, err
}
port.systemSettings = s
return port, nil
}
func addResource(service *wmiext.Service, systemSettingPath string, resourceSettings string) (string, error) {
vsms, err := service.GetSingletonInstance(VirtualSystemManagementService)
if err != nil {
return "", err
}
defer vsms.Close()
var res int32
var resultingSettings []string
var job *wmiext.Instance
err = vsms.BeginInvoke("AddResourceSettings").
In("AffectedConfiguration", systemSettingPath).
In("ResourceSettings", []string{resourceSettings}).
Execute().
Out("Job", &job).
Out("ResultingResourceSettings", &resultingSettings).
Out("ReturnValue", &res).End()
if err != nil {
return "", fmt.Errorf("AddResourceSettings failed: %w", err)
}
err = waitVMResult(res, service, job)
if len(resultingSettings) > 0 {
return resultingSettings[0], err
}
return "", err
}
func (s *SystemSettings) GetVM() (*VirtualMachine, error) {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return nil, err
}
defer service.Close()
inst, err := service.FindFirstRelatedInstance(s.Path(), "Msvm_ComputerSystem")
if err != nil {
return nil, err
}
defer inst.Close()
vm := &VirtualMachine{}
if err = inst.GetAll(vm); err != nil {
return nil, err
}
return vm, nil
}

View File

@ -0,0 +1,161 @@
//go:build windows
// +build windows
package hypervctl
import (
"fmt"
"github.com/containers/libhvee/pkg/wmiext"
)
type SystemSettingsBuilder struct {
systemSettings *SystemSettings
processorSettings *ProcessorSettings
memorySettings *MemorySettings
err error
}
func NewSystemSettingsBuilder() *SystemSettingsBuilder {
return &SystemSettingsBuilder{}
}
func (builder *SystemSettingsBuilder) PrepareSystemSettings(name string, beforeAdd func(*SystemSettings)) *SystemSettingsBuilder {
if builder.err != nil {
return builder
}
if builder.systemSettings == nil {
settings := DefaultSystemSettings()
settings.ElementName = name
builder.systemSettings = settings
}
if beforeAdd != nil {
beforeAdd(builder.systemSettings)
}
return builder
}
func (builder *SystemSettingsBuilder) PrepareProcessorSettings(beforeAdd func(*ProcessorSettings)) *SystemSettingsBuilder {
if builder.err != nil {
return builder
}
if builder.processorSettings == nil {
settings, err := fetchDefaultProcessorSettings()
if err != nil {
builder.err = err
return builder
}
builder.processorSettings = settings
}
if beforeAdd != nil {
beforeAdd(builder.processorSettings)
}
return builder
}
func (builder *SystemSettingsBuilder) PrepareMemorySettings(beforeAdd func(*MemorySettings)) *SystemSettingsBuilder {
if builder.err != nil {
return builder
}
if builder.memorySettings == nil {
settings, err := fetchDefaultMemorySettings()
if err != nil {
builder.err = err
return builder
}
builder.memorySettings = settings
}
if beforeAdd != nil {
beforeAdd(builder.memorySettings)
}
return builder
}
func (builder *SystemSettingsBuilder) Build() (*SystemSettings, error) {
var service *wmiext.Service
var err error
if builder.PrepareSystemSettings("unnamed-vm", nil).
PrepareProcessorSettings(nil).
PrepareMemorySettings(nil).err != nil {
return nil, err
}
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return nil, err
}
defer service.Close()
systemSettingsInst, err := service.SpawnInstance("Msvm_VirtualSystemSettingData")
if err != nil {
return nil, err
}
defer systemSettingsInst.Close()
err = systemSettingsInst.PutAll(builder.systemSettings)
if err != nil {
return nil, err
}
memoryStr, err := createMemorySettings(builder.memorySettings)
if err != nil {
return nil, err
}
processorStr, err := createProcessorSettings(builder.processorSettings)
if err != nil {
return nil, err
}
vsms, err := service.GetSingletonInstance(VirtualSystemManagementService)
if err != nil {
return nil, err
}
defer vsms.Close()
systemStr := systemSettingsInst.GetCimText()
var job *wmiext.Instance
var res int32
var resultingSystem string
err = vsms.BeginInvoke("DefineSystem").
In("SystemSettings", systemStr).
In("ResourceSettings", []string{memoryStr, processorStr}).
Execute().
Out("Job", &job).
Out("ResultingSystem", &resultingSystem).
Out("ReturnValue", &res).End()
if err != nil {
return nil, fmt.Errorf("failed to define system: %w", err)
}
err = waitVMResult(res, service, job)
if err != nil {
return nil, fmt.Errorf("failed to define system: %w", err)
}
newSettings, err := service.FindFirstRelatedInstance(resultingSystem, "Msvm_VirtualSystemSettingData")
if err != nil {
return nil, err
}
path, err := newSettings.Path()
if err != nil {
return nil, err
}
if err = service.GetObjectAsObject(path, builder.systemSettings); err != nil {
return nil, err
}
return builder.systemSettings, nil
}

View File

@ -0,0 +1,13 @@
//go:build windows
// +build windows
package hypervctl
const VirtualDvdDiskType = "Microsoft:Hyper-V:Virtual CD/DVD Disk"
type VirtualDvdDiskStorageSettings struct {
StorageAllocationSettings
systemSettings *SystemSettings
driveSettings *SyntheticDvdDriveSettings
}

View File

@ -0,0 +1,28 @@
//go:build windows
// +build windows
package hypervctl
import "time"
type VirtualHardDiskSettings struct {
S__PATH string
InstanceID string
Caption string // = "Virtual Hard Disk Setting Data"
Description string // = "Setting Data for a Virtual Hard Disk"
ElementName string
Type uint16
Format uint16
Path string
ParentPath string
ParentTimestamp time.Time
ParentIdentifier string
MaxInternalSize uint64
BlockSize uint32
LogicalSectorSize uint32
PhysicalSectorSize uint32
VirtualDiskId string
DataAlignment uint64
PmemAddressAbstractionType uint16
IsPmemCompatible bool
}

View File

@ -0,0 +1,13 @@
//go:build windows
// +build windows
package hypervctl
const VirtualHardDiskType = "Microsoft:Hyper-V:Virtual Hard Disk"
type VirtualHardDiskStorageSettings struct {
StorageAllocationSettings
systemSettings *SystemSettings
driveSettings *SyntheticDiskDriveSettings
}

View File

@ -0,0 +1,498 @@
//go:build windows
// +build windows
package hypervctl
import (
"bytes"
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/containers/libhvee/pkg/kvp/ginsu"
"github.com/containers/libhvee/pkg/wmiext"
)
// delete this when close to being done
var (
ErrNotImplemented = errors.New("function not implemented")
)
type VirtualMachine struct {
S__PATH string `json:"-"`
S__CLASS string `json:"-"`
InstanceID string
Caption string
Description string
ElementName string
InstallDate time.Time
OperationalStatus []uint16
StatusDescriptions []string
Status string
HealthState uint16
CommunicationStatus uint16
DetailedStatus uint16
OperatingStatus uint16
PrimaryStatus uint16
EnabledState uint16
OtherEnabledState string
RequestedState uint16
EnabledDefault uint16
TimeOfLastStateChange string
AvailableRequestedStates []uint16
TransitioningToState uint16
CreationClassName string
Name string
PrimaryOwnerName string
PrimaryOwnerContact string
Roles []string
NameFormat string
OtherIdentifyingInfo []string
IdentifyingDescriptions []string
Dedicated []uint16
OtherDedicatedDescriptions []string
ResetCapability uint16
PowerManagementCapabilities []uint16
OnTimeInMilliseconds uint64
ProcessID uint32
TimeOfLastConfigurationChange string
NumberOfNumaNodes uint16
ReplicationState uint16
ReplicationHealth uint16
ReplicationMode uint16
FailedOverReplicationType uint16
LastReplicationType uint16
LastApplicationConsistentReplicationTime string
LastReplicationTime time.Time
LastSuccessfulBackupTime string
EnhancedSessionModeState uint16
vmm *VirtualMachineManager
}
func (vm *VirtualMachine) Path() string {
return vm.S__PATH
}
func (vm *VirtualMachine) SplitAndAddIgnition(keyPrefix string, ignRdr *bytes.Reader) error {
parts, err := ginsu.Dice(ignRdr)
if err != nil {
return err
}
for idx, val := range parts {
key := fmt.Sprintf("%s%d", keyPrefix, idx)
if err := vm.AddKeyValuePair(key, val); err != nil {
return err
}
}
return nil
}
func (vm *VirtualMachine) AddKeyValuePair(key string, value string) error {
return vm.kvpOperation("AddKvpItems", key, value, "key already exists?")
}
func (vm *VirtualMachine) ModifyKeyValuePair(key string, value string) error {
return vm.kvpOperation("ModifyKvpItems", key, value, "key invalid?")
}
func (vm *VirtualMachine) PutKeyValuePair(key string, value string) error {
err := vm.AddKeyValuePair(key, value)
kvpError, ok := err.(*KvpError)
if !ok || kvpError.ErrorCode != KvpIllegalArgument {
return err
}
return vm.ModifyKeyValuePair(key, value)
}
func (vm *VirtualMachine) RemoveKeyValuePair(key string) error {
return vm.kvpOperation("RemoveKvpItems", key, "", "key invalid?")
}
func (vm *VirtualMachine) GetKeyValuePairs() (map[string]string, error) {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return nil, err
}
defer service.Close()
i, err := service.FindFirstRelatedInstance(vm.Path(), "Msvm_KvpExchangeComponent")
if err != nil {
return nil, err
}
defer i.Close()
var path string
path, err = i.GetAsString("__PATH")
if err != nil {
return nil, err
}
i, err = service.FindFirstRelatedInstance(path, "Msvm_KvpExchangeComponentSettingData")
if err != nil {
return nil, err
}
defer i.Close()
s, err := i.GetAsString("HostExchangeItems")
if err != nil {
return nil, err
}
return parseKvpMapXml(s)
}
func (vm *VirtualMachine) kvpOperation(op string, key string, value string, illegalSuggestion string) error {
var service *wmiext.Service
var vsms, job *wmiext.Instance
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return err
}
defer service.Close()
vsms, err = service.GetSingletonInstance(VirtualSystemManagementService)
if err != nil {
return err
}
defer vsms.Close()
itemStr, err := createKvpItem(service, key, value)
if err != nil {
return err
}
execution := vsms.BeginInvoke(op).
In("TargetSystem", vm.Path()).
In("DataItems", []string{itemStr}).
Execute()
if err := execution.Out("Job", &job).End(); err != nil {
return fmt.Errorf("%s execution failed: %w", op, err)
}
err = translateKvpError(wmiext.WaitJob(service, job), illegalSuggestion)
defer job.Close()
return err
}
func waitVMResult(res int32, service *wmiext.Service, job *wmiext.Instance) error {
var err error
if res == 4096 {
err = wmiext.WaitJob(service, job)
defer job.Close()
}
if err != nil {
desc, _ := job.GetAsString("ErrorDescription")
desc = strings.Replace(desc, "\n", " ", -1)
return fmt.Errorf("failed to define system: %w (%s)", err, desc)
}
return err
}
func (vm *VirtualMachine) Stop() error {
if !Enabled.equal(vm.EnabledState) {
return ErrMachineNotRunning
}
var (
err error
res int32
srv *wmiext.Service
)
if srv, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return err
}
wmiInst, err := srv.FindFirstRelatedInstance(vm.Path(), "Msvm_ShutdownComponent")
if err != nil {
return err
}
// https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-shutdowncomponent-initiateshutdown
err = wmiInst.BeginInvoke("InitiateShutdown").
In("Reason", "User requested").
In("Force", false).
Execute().
Out("ReturnValue", &res).End()
if err != nil {
return err
}
if res != 0 {
return translateShutdownError(int(res))
}
return nil
}
func (vm *VirtualMachine) Start() error {
var (
srv *wmiext.Service
err error
job *wmiext.Instance
res int32
)
if s := vm.EnabledState; !Disabled.equal(s) {
if Enabled.equal(s) {
return ErrMachineAlreadyRunning
} else if Starting.equal(s) {
return ErrMachineAlreadyRunning
}
return errors.New("machine not in a state to start")
}
if srv, err = getService(srv); err != nil {
return err
}
defer srv.Close()
instance, err := srv.GetObject(vm.Path())
if err != nil {
return err
}
defer instance.Close()
// https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/cim-concretejob-requeststatechange
if err := instance.BeginInvoke("RequestStateChange").
In("RequestedState", uint16(start)).
In("TimeoutPeriod", &time.Time{}).
Execute().
Out("Job", &job).
Out("ReturnValue", &res).End(); err != nil {
return err
}
return waitVMResult(res, srv, job)
}
func getService(_ *wmiext.Service) (*wmiext.Service, error) {
// any reason why when we instantiate a vm, we should NOT just embed a service?
return wmiext.NewLocalService(HyperVNamespace)
}
func (vm *VirtualMachine) list() ([]*HyperVConfig, error) {
return nil, ErrNotImplemented
}
func (vm *VirtualMachine) GetConfig(diskPath string) (*HyperVConfig, error) {
var (
diskSize uint64
)
summary, err := vm.GetSummaryInformation(SummaryRequestCommon)
if err != nil {
return nil, err
}
// Grabbing actual disk size
diskPathInfo, err := os.Stat(diskPath)
if err != nil {
return nil, err
}
diskSize = uint64(diskPathInfo.Size())
config := HyperVConfig{
Hardware: HardwareConfig{
CPUs: summary.NumberOfProcessors,
DiskPath: diskPath,
DiskSize: diskSize,
Memory: summary.MemoryAvailable,
},
Status: Statuses{
Created: vm.InstallDate,
LastUp: time.Time{},
Running: Enabled.equal(vm.EnabledState),
Starting: vm.IsStarting(),
State: EnabledState(vm.EnabledState),
},
}
return &config, nil
}
// GetSummaryInformation returns the live VM summary information for this virtual machine.
// The requestedFields parameter controls which fields of summary information are populated.
// SummaryRequestCommon and SummaryRequestNearAll provide predefined combinations for this
// parameter
func (vm *VirtualMachine) GetSummaryInformation(requestedFields SummaryRequestSet) (*SummaryInformation, error) {
service, err := wmiext.NewLocalService(HyperVNamespace)
if err != nil {
return nil, err
}
defer service.Close()
instance, err := service.FindFirstRelatedInstance(vm.Path(), "Msvm_VirtualSystemSettingData")
if err != nil {
return nil, err
}
defer instance.Close()
path, err := instance.Path()
if err != nil {
return nil, err
}
result, err := vm.vmm.getSummaryInformation(path, requestedFields)
if err != nil {
return nil, err
}
if len(result) < 1 {
return nil, errors.New("summary information search returned an empty result set")
}
return &result[0], nil
}
// NewVirtualMachine creates a new vm in hyperv
// decided to not return a *VirtualMachine here because of how Podman is
// likely to use this. this could be easily added if desirable
func (vmm *VirtualMachineManager) NewVirtualMachine(name string, config *HardwareConfig) error {
exists, err := vmm.Exists(name)
if err != nil {
return err
}
if exists {
return ErrMachineAlreadyExists
}
// TODO I gotta believe there are naming restrictions for vms in hyperv?
// TODO If something fails during creation, do we rip things down or follow precedent from other machines? user deletes things
systemSettings, err := NewSystemSettingsBuilder().
PrepareSystemSettings(name, nil).
PrepareMemorySettings(func(ms *MemorySettings) {
//ms.DynamicMemoryEnabled = false
//ms.VirtualQuantity = 8192 // Startup memory
//ms.Reservation = config.Memory // min
// The API seems to require both of these even
// when not using dynamic memory
ms.Limit = uint64(config.Memory)
ms.VirtualQuantity = uint64(config.Memory)
}).
PrepareProcessorSettings(func(ps *ProcessorSettings) {
ps.VirtualQuantity = uint64(config.CPUs) // 4 cores
}).
Build()
if err != nil {
return err
}
//if err := vmm.CreateVhdxFile(config.DiskPath, config.DiskSize*1024*1024*1024); err != nil {
// return err
//}
if err := NewDriveSettingsBuilder(systemSettings).
AddScsiController().
AddSyntheticDiskDrive(0).
DefineVirtualHardDisk(config.DiskPath, func(vhdss *VirtualHardDiskStorageSettings) {
// set extra params like
// vhdss.IOPSLimit = 5000
}).
Finish(). // disk
Finish(). // drive
//AddSyntheticDvdDrive(1).
//DefineVirtualDvdDisk(isoFile).
//Finish(). // disk
//Finish(). // drive
Finish(). // controller
Complete(); err != nil {
return err
}
// Add default network connection
if err := NewNetworkSettingsBuilder(systemSettings).
AddSyntheticEthernetPort(nil).
AddEthernetPortAllocation(""). // "" = connect to default switch
Finish(). // allocation
Finish(). // port
Complete(); err != nil {
return err
}
return nil
}
func (vm *VirtualMachine) remove() (int32, error) {
var (
err error
res int32
srv *wmiext.Service
)
// Check for disabled/stopped state
if !Disabled.equal(vm.EnabledState) {
return -1, ErrMachineStateInvalid
}
if srv, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return -1, err
}
wmiInst, err := srv.FindFirstRelatedInstance(vm.Path(), "Msvm_VirtualSystemSettingData")
if err != nil {
return -1, err
}
defer wmiInst.Close()
path, err := wmiInst.Path()
if err != nil {
return -1, err
}
vsms, err := srv.GetSingletonInstance("Msvm_VirtualSystemManagementService")
if err != nil {
return -1, err
}
defer wmiInst.Close()
var (
job *wmiext.Instance
resultingSystem string
)
// https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/cim-virtualsystemmanagementservice-destroysystem
if err := vsms.BeginInvoke("DestroySystem").
In("AffectedSystem", path).
Execute().
Out("Job", &job).
Out("ResultingSystem", &resultingSystem).
Out("ReturnValue", &res).End(); err != nil {
return -1, err
}
// do i have this correct? you can get an error without a result?
if err := waitVMResult(res, srv, job); err != nil {
return -1, err
}
return res, nil
}
func (vm *VirtualMachine) Remove(diskPath string) error {
res, err := vm.remove()
if err != nil {
return err
}
if DestroySystemResult(res) == VMDestroyCompletedwithNoError {
// Remove disk only if we were given one
if len(diskPath) > 0 {
if err := os.Remove(diskPath); err != nil {
return err
}
}
return nil
}
return fmt.Errorf("failed to destroy system %s: %s", vm.Name, DestroySystemResult(res).Reason())
}
func (vm *VirtualMachine) State() EnabledState {
return EnabledState(vm.EnabledState)
}
func (vm *VirtualMachine) IsStarting() bool {
return Starting.equal(vm.EnabledState)
}

View File

@ -0,0 +1,114 @@
//go:build windows
// +build windows
package hypervctl
import (
"time"
)
type vmState uint16
const (
// Changes the state to 'Running'.
start vmState = 2
// Stops the job temporarily. The intention is to subsequently restart the job with 'Start'. It might be possible to
// enter the 'Service' state while suspended. (This is job-specific.)
suspend vmState = 3
// Stops the job cleanly, saves data, preserves the state, and shuts down all underlying processes'
// in an orderly manner.
terminate vmState = 4
//Terminates the job immediately with no requirement to save data or preserve the state.
kill vmState = 5
)
type EnabledState uint16
const (
// Unknown The state of the element could not be determined.
Unknown EnabledState = 0
// Other No description
Other EnabledState = 1
// Enabled The element is running.
Enabled EnabledState = 2
// Disabled The element is turned off.
Disabled EnabledState = 3
// ShuttingDown Shutting_Down The element is in the process of going to a Disabled state.
ShuttingDown EnabledState = 4
// NotApplicable The element does not support being enabled or disabled.
NotApplicable EnabledState = 5
// EnabledButOffline The element might be completing commands, and it will drop any new requests.
EnabledButOffline EnabledState = 6
// InTest The element is in a test state.
InTest EnabledState = 7
// Deferred The element might be completing commands, but it will queue any new requests.
Deferred EnabledState = 8
// Quiesce The element is enabled but in a restricted mode. The behavior of the element is similar to the Enabled state
//(2), but it processes only a restricted set of commands. All other requests are queued.
Quiesce EnabledState = 9
// Starting The element is in the process of going to an Enabled state (2). New requests are queued.
Starting EnabledState = 10
)
func (es EnabledState) String() string {
switch es {
case Unknown:
return "unknown"
case Other:
return "other"
case Enabled:
return "enabled"
case Disabled:
return "disabled"
case ShuttingDown:
return "shutting down"
case NotApplicable:
return "not applicable"
case EnabledButOffline:
return "enabled but offline"
case InTest:
return "in test"
case Deferred:
return "deferred"
case Quiesce:
return "quiesce"
}
return "starting"
}
func (es EnabledState) equal(s uint16) bool {
return es == EnabledState(s)
}
// HyperVConfig describes physical attributes of the machine
type HyperVConfig struct {
// Hardware configuration for HyperV
Hardware HardwareConfig
// state and descriptive info
Status Statuses
}
type HardwareConfig struct {
// CPUs to be assigned to the VM
CPUs uint16
// Diskpath is fully qualified location of the
// bootable disk image
DiskPath string
// Disk size in gigabytes assigned to the vm
DiskSize uint64
// Memory in megabytes assigned to the vm
Memory int32
}
type Statuses struct {
// time vm created
Created time.Time
// last time vm ran
LastUp time.Time
// is vm running
Running bool
// is vm starting up
Starting bool
// vm state
State EnabledState
}

View File

@ -0,0 +1,184 @@
//go:build windows
// +build windows
package hypervctl
import (
"fmt"
"github.com/containers/libhvee/pkg/wmiext"
)
const (
HyperVNamespace = "root\\virtualization\\v2"
VirtualSystemManagementService = "Msvm_VirtualSystemManagementService"
)
// https://learn.microsoft.com/en-us/windows/win32/hyperv_v2/msvm-computersystem
type VirtualMachineManager struct {
}
func NewVirtualMachineManager() *VirtualMachineManager {
return &VirtualMachineManager{}
}
func (vmm *VirtualMachineManager) GetAll() ([]*VirtualMachine, error) {
const wql = "Select * From Msvm_ComputerSystem Where Caption = 'Virtual Machine'"
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return []*VirtualMachine{}, err
}
defer service.Close()
var enum *wmiext.Enum
if enum, err = service.ExecQuery(wql); err != nil {
return nil, err
}
defer enum.Close()
var vms []*VirtualMachine
for {
vm := &VirtualMachine{vmm: vmm}
done, err := wmiext.NextObject(enum, vm)
if err != nil {
return vms, err
}
if done {
break
}
vms = append(vms, vm)
}
return vms, nil
}
func (vmm *VirtualMachineManager) Exists(name string) (bool, error) {
vms, err := vmm.GetAll()
if err != nil {
return false, err
}
for _, i := range vms {
// TODO should case be honored or ignored?
if i.Name == name {
return true, nil
}
}
return false, nil
}
func (*VirtualMachineManager) GetMachine(name string) (*VirtualMachine, error) {
const wql = "Select * From Msvm_ComputerSystem Where Caption = 'Virtual Machine' And ElementName='%s'"
vm := &VirtualMachine{}
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return vm, err
}
defer service.Close()
var enum *wmiext.Enum
if enum, err = service.ExecQuery(fmt.Sprintf(wql, name)); err != nil {
return nil, err
}
defer enum.Close()
done, err := wmiext.NextObject(enum, vm)
if err != nil {
return vm, err
}
if done {
return vm, fmt.Errorf("could not find virtual machine %q", name)
}
return vm, nil
}
func (*VirtualMachineManager) CreateVhdxFile(path string, maxSize uint64) error {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return err
}
defer service.Close()
settings := &VirtualHardDiskSettings{}
settings.Format = 3
settings.MaxInternalSize = maxSize
settings.Type = 3
settings.Path = path
instance, err := service.CreateInstance("Msvm_VirtualHardDiskSettingData", settings)
if err != nil {
return err
}
defer instance.Close()
settingsStr := instance.GetCimText()
imms, err := service.GetSingletonInstance("Msvm_ImageManagementService")
if err != nil {
return err
}
defer imms.Close()
var job *wmiext.Instance
var ret int32
err = imms.BeginInvoke("CreateVirtualHardDisk").
In("VirtualDiskSettingData", settingsStr).
Execute().
Out("Job", &job).
Out("ReturnValue", &ret).
End()
if err != nil {
return fmt.Errorf("failed to create vhdx: %w", err)
}
return waitVMResult(ret, service, job)
}
// GetSummaryInformation returns the live VM summary information for all virtual machines.
// The requestedFields parameter controls which fields of summary information are populated.
// SummaryRequestCommon and SummaryRequestNearAll provide predefined combinations for this
// parameter.
func (vmm *VirtualMachineManager) GetSummaryInformation(requestedFields SummaryRequestSet) ([]SummaryInformation, error) {
return vmm.getSummaryInformation("", requestedFields)
}
func (vmm *VirtualMachineManager) getSummaryInformation(settingsPath string, requestedFields SummaryRequestSet) ([]SummaryInformation, error) {
var service *wmiext.Service
var err error
if service, err = wmiext.NewLocalService(HyperVNamespace); err != nil {
return nil, err
}
defer service.Close()
vsms, err := service.GetSingletonInstance(VirtualSystemManagementService)
if err != nil {
return nil, err
}
defer vsms.Close()
var summary []SummaryInformation
inv := vsms.BeginInvoke("GetSummaryInformation").
In("RequestedInformation", []uint(requestedFields))
if len(settingsPath) > 0 {
inv.In("SettingData", []string{settingsPath})
}
err = inv.Execute().
Out("SummaryInformation", &summary).
End()
if err != nil {
return nil, err
}
return summary, nil
}

View File

@ -0,0 +1,46 @@
package ginsu
//
// This function is purposely "namespaces" for easy and
// lightweight vendoring.
//
import (
"bytes"
)
const (
// KvpValueMaxLen is the maximum real-world length of bytes that can
// be stored in the value of the wmi key-value pair data exchange
KvpValueMaxLen = int(990)
)
// Dice takes input and splits it into a string array so it can be
// passed by hyperv and wmi. Each part must be less than the maximum size
// of a kvp value
func Dice(k *bytes.Reader) ([]string, error) {
var (
// done is a simple bool indicator that we no longer
// need to iterate
done bool
parts []string
)
for {
sl := make([]byte, KvpValueMaxLen)
n, err := k.Read(sl)
if err != nil {
return parts, err
}
// if we read and the length is less that the max read,
// then we are at the end
if n < KvpValueMaxLen {
sl = sl[0:n]
done = true
}
parts = append(parts, string(sl))
if done {
break
}
}
return parts, nil
}

View File

@ -0,0 +1,202 @@
//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 ole.NewError(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 ole.NewError(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 ole.NewError(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
}

View File

@ -0,0 +1,475 @@
//go:build windows
// +build windows
package wmiext
import (
"errors"
"fmt"
"math"
"reflect"
"strconv"
"strings"
"time"
"unsafe"
"github.com/go-ole/go-ole"
)
var (
unixEpoch = time.Unix(0,0)
zeroTime = time.Time{}
)
// Automation variants do not follow the OLE rules, instead they use the following mapping:
// sint8 VT_I2 Signed 8-bit integer.
// sint16 VT_I2 Signed 16-bit integer.
// sint32 VT_I4 Signed 32-bit integer.
// sint64 VT_BSTR Signed 64-bit integer in string form. This type follows hexadecimal or decimal format
//
// according to the American National Standards Institute (ANSI) C rules.
//
// real32 VT_R4 4-byte floating-point value that follows the Institute of Electrical and Electronics
//
// Engineers, Inc. (IEEE) standard.
//
// real64 VT_R8 8-byte floating-point value that follows the IEEE standard.
// uint8 VT_UI1 Unsigned 8-bit integer.
// uint16 VT_I4 Unsigned 16-bit integer.
// uint32 VT_I4 Unsigned 32-bit integer.
// uint64 VT_BSTR Unsigned 64-bit integer in string form. This type follows hexadecimal or decimal format
//
// according to ANSI C rules.
// NewAutomationVariant returns a new VARIANT com
//
//gocyclo:ignore
func NewAutomationVariant(value interface{}) (ole.VARIANT, error) {
switch cast := value.(type) {
case bool:
if cast {
return ole.NewVariant(ole.VT_BOOL, 0xffff), nil
} else {
return ole.NewVariant(ole.VT_BOOL, 0), nil
}
case int8:
return ole.NewVariant(ole.VT_I2, int64(cast)), nil
case []int8:
return CreateNumericArrayVariant(cast, ole.VT_I2)
case int16:
return ole.NewVariant(ole.VT_I2, int64(cast)), nil
case []int16:
return CreateNumericArrayVariant(cast, ole.VT_I2)
case int32:
return ole.NewVariant(ole.VT_I4, int64(cast)), nil
case []int32:
return CreateNumericArrayVariant(cast, ole.VT_I4)
case int64:
s := fmt.Sprintf("%d", cast)
return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(s))))), nil
case []int64:
strs := make([]string, len(cast))
for i, num := range cast {
strs[i] = fmt.Sprintf("%d", num)
}
return CreateStringArrayVariant(strs)
case float32:
return ole.NewVariant(ole.VT_R4, int64(math.Float32bits(cast))), nil
case float64:
return ole.NewVariant(ole.VT_R8, int64(math.Float64bits(cast))), nil
case uint8:
return ole.NewVariant(ole.VT_UI1, int64(cast)), nil
case []uint8:
return CreateNumericArrayVariant(cast, ole.VT_UI1)
case uint16:
return ole.NewVariant(ole.VT_I4, int64(cast)), nil
case []uint16:
return CreateNumericArrayVariant(cast, ole.VT_I4)
case uint32:
return ole.NewVariant(ole.VT_I4, int64(cast)), nil
case []uint32:
return CreateNumericArrayVariant(cast, ole.VT_I4)
case uint64:
s := fmt.Sprintf("%d", cast)
return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(s))))), nil
case []uint64:
strs := make([]string, len(cast))
for i, num := range cast {
strs[i] = fmt.Sprintf("%d", num)
}
return CreateStringArrayVariant(strs)
// Assume 32 bit for generic (u)ints
case int:
return ole.NewVariant(ole.VT_I4, int64(cast)), nil
case uint:
return ole.NewVariant(ole.VT_I4, int64(cast)), nil
case []int:
return CreateNumericArrayVariant(cast, ole.VT_I4)
case []uint:
return CreateNumericArrayVariant(cast, ole.VT_I4)
case string:
return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(value.(string)))))), nil
case []string:
if len(cast) == 0 {
return ole.NewVariant(ole.VT_NULL, 0), nil
}
return CreateStringArrayVariant(cast)
case time.Time:
return convertTimeToDataTime(&cast), nil
case *time.Time:
return convertTimeToDataTime(cast), nil
case time.Duration:
return convertDurationToDateTime(cast), nil
case nil:
return ole.NewVariant(ole.VT_NULL, 0), nil
case *ole.IUnknown:
if cast == nil {
return ole.NewVariant(ole.VT_NULL, 0), nil
}
return ole.NewVariant(ole.VT_UNKNOWN, int64(uintptr(unsafe.Pointer(cast)))), nil
case *Instance:
if cast == nil {
return ole.NewVariant(ole.VT_NULL, 0), nil
}
return ole.NewVariant(ole.VT_UNKNOWN, int64(uintptr(unsafe.Pointer(cast.object)))), nil
default:
return ole.VARIANT{}, fmt.Errorf("unsupported type for automation variants %T", value)
}
}
func convertToGoType(variant *ole.VARIANT, outputValue reflect.Value, outputType reflect.Type) (value interface{}, err error) {
if variant.VT&ole.VT_ARRAY == ole.VT_ARRAY {
return convertVariantToArray(variant, outputType)
}
if variant.VT == ole.VT_UNKNOWN {
return convertVariantToStruct(variant, outputType)
}
switch cast := outputValue.Interface().(type) {
case bool:
return variant.Val != 0, nil
case time.Time:
return convertDataTimeToTime(variant)
case *time.Time:
x, err := convertDataTimeToTime(variant)
return &x, err
case time.Duration:
return convertIntervalToDuration(variant)
case uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64:
return convertVariantToInt(variant, outputType)
case float32, float64:
return convertVariantToFloat(variant, outputType)
case string:
return variant.ToString(), nil
default:
if variant.VT == ole.VT_NULL {
return nil, nil
}
return nil, fmt.Errorf("could not convert %d to %v", variant.VT, cast)
}
}
func convertInt64ToInt(value int64, outputType reflect.Type) (interface{}, error) {
switch outputType.Kind() {
case reflect.Int:
return int(value), nil
case reflect.Int8:
return int8(value), nil
case reflect.Int16:
return int16(value), nil
case reflect.Int32:
return int32(value), nil
case reflect.Int64:
return int64(value), nil
case reflect.Uint:
return uint(value), nil
case reflect.Uint8:
return uint8(value), nil
case reflect.Uint16:
return uint16(value), nil
case reflect.Uint32:
return uint32(value), nil
case reflect.Uint64:
return uint64(value), nil
default:
return 0, fmt.Errorf("could not convert int64 to %v", outputType)
}
}
func convertStringToInt64(str string, unsigned bool) (int64, error) {
if unsigned {
val, err := strconv.ParseUint(str, 0, 64)
return int64(val), err
}
return strconv.ParseInt(str, 0, 64)
}
func convertVariantToInt(variant *ole.VARIANT, outputType reflect.Type) (interface{}, error) {
var value int64
switch variant.VT {
case ole.VT_NULL:
fallthrough
case ole.VT_BOOL:
fallthrough
case ole.VT_I1, ole.VT_I2, ole.VT_I4, ole.VT_I8, ole.VT_INT:
fallthrough
case ole.VT_UI1, ole.VT_UI2, ole.VT_UI4, ole.VT_UI8, ole.VT_UINT:
value = variant.Val
case ole.VT_R4:
// not necessarily a useful conversion but handle it anyway
value = int64(*(*float32)(unsafe.Pointer(&variant.Val)))
case ole.VT_R8:
value = int64(*(*float64)(unsafe.Pointer(&variant.Val)))
case ole.VT_BSTR:
var err error
value, err = convertStringToInt64(variant.ToString(), outputType.Kind() == reflect.Uint64)
if err != nil {
return value, err
}
default:
return nil, fmt.Errorf("could not convert variant type %d to %v", variant.VT, outputType)
}
return convertInt64ToInt(value, outputType)
}
func convertVariantToFloat(variant *ole.VARIANT, outputType reflect.Type) (interface{}, error) {
var value float64
switch variant.VT {
case ole.VT_NULL:
fallthrough
case ole.VT_BOOL:
fallthrough
case ole.VT_I1, ole.VT_I2, ole.VT_I4, ole.VT_I8, ole.VT_INT:
fallthrough
case ole.VT_UI1, ole.VT_UI2, ole.VT_UI4, ole.VT_UI8, ole.VT_UINT:
value = float64(variant.Val)
case ole.VT_R4:
value = float64(*(*float32)(unsafe.Pointer(&variant.Val)))
case ole.VT_R8:
value = *(*float64)(unsafe.Pointer(&variant.Val))
case ole.VT_BSTR:
var err error
value, err = strconv.ParseFloat(variant.ToString(), 64)
if err != nil {
return value, err
}
default:
return nil, fmt.Errorf("could not convert variant type %d to %v", variant.VT, outputType)
}
if outputType.Kind() == reflect.Float32 {
return float32(value), nil
}
return value, nil
}
func convertVariantToStruct(variant *ole.VARIANT, outputType reflect.Type) (interface{}, error) {
if variant.VT != ole.VT_UNKNOWN {
return nil, fmt.Errorf("could not convert non-IUnknown variant type %d to %v", variant.VT, outputType)
}
ptr := variant.ToIUnknown()
var rawInstance struct {
*ole.IUnknown
*IWbemClassObjectVtbl
}
rawInstance.IUnknown = ptr
rawInstance.IWbemClassObjectVtbl = (*IWbemClassObjectVtbl)(unsafe.Pointer(ptr.RawVTable))
instance := (*Instance)(unsafe.Pointer(&rawInstance))
val := reflect.New(outputType)
err := instance.GetAll(val.Interface())
return val.Elem().Interface(), err
}
func convertVariantToArray(variant *ole.VARIANT, outputType reflect.Type) (interface{}, error) {
if variant.VT&ole.VT_ARRAY != ole.VT_ARRAY {
return nil, fmt.Errorf("could not convert non-array variant type %d to %v", variant.VT, outputType)
}
safeArrayConversion := ole.SafeArrayConversion{Array: *(**ole.SafeArray)(unsafe.Pointer(&variant.Val))}
arrayLen, err := safeArrayConversion.TotalElements(0)
if err != nil {
return nil, err
}
elemVT := (^ole.VT_ARRAY) & variant.VT
slice := reflect.MakeSlice(reflect.SliceOf(outputType.Elem()), int(arrayLen), int(arrayLen))
for i := 0; i < int(arrayLen); i++ {
elemVariant := ole.VARIANT{VT: elemVT}
elemSrc, err := safeArrayGetAsVariantVal(safeArrayConversion.Array, int64(i), elemVariant)
if err != nil {
return nil, err
}
elemVariant.Val = int64(elemSrc)
elemDest, err := convertToGoType(&elemVariant, slice.Index(i), outputType.Elem())
if err != nil {
return nil, err
}
slice.Index(i).Set(reflect.ValueOf(elemDest))
}
return slice.Interface(), nil
}
func convertToGenericValue(variant *ole.VARIANT) interface{} {
var result interface{}
if variant.VT&ole.VT_ARRAY == ole.VT_ARRAY {
safeArrayConversion := ole.SafeArrayConversion{Array: *(**ole.SafeArray)(unsafe.Pointer(&variant.Val))}
result = safeArrayConversion.ToValueArray()
} else {
result = variant.Value()
}
return result
}
func convertTimeToDataTime(time *time.Time) ole.VARIANT {
if time == nil || !time.After(WindowsEpoch) {
return ole.NewVariant(ole.VT_NULL, 0)
}
_, offset := time.Zone()
// convert to minutes
offset /= 60
//yyyymmddHHMMSS.mmmmmmsUUU
s := fmt.Sprintf("%s%+04d", time.Format("20060102150405.000000"), offset)
return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(s)))))
}
func convertDurationToDateTime(duration time.Duration) ole.VARIANT {
const daySeconds = time.Second * 86400
if duration == 0 {
return ole.NewVariant(ole.VT_NULL, 0)
}
days := duration / daySeconds
duration = duration % daySeconds
hours := duration / time.Hour
duration = duration % time.Hour
mins := duration / time.Minute
duration = duration % time.Minute
seconds := duration / time.Second
duration = duration % time.Second
micros := duration / time.Microsecond
s:=fmt.Sprintf("%08d%02d%02d%02d.%06d:000", days, hours, mins, seconds, micros)
return ole.NewVariant(ole.VT_BSTR, int64(uintptr(unsafe.Pointer(ole.SysAllocStringLen(s)))))
}
func extractDateTimeString(variant *ole.VARIANT) (string, error) {
switch variant.VT {
case ole.VT_BSTR:
return variant.ToString(), nil
case ole.VT_NULL:
return "", nil
default:
return "", errors.New("variant not compatible with dateTime field")
}
}
func convertDataTimeToTime(variant *ole.VARIANT) (time.Time, error) {
var err error
dateTime, err := extractDateTimeString(variant)
if err != nil || len(dateTime) == 0 {
return zeroTime, err
}
dLen := len(dateTime)
if dLen < 5 {
return zeroTime, errors.New("invalid datetime string")
}
if strings.HasPrefix(dateTime, "00000000000000.000000") {
// Zero time
return zeroTime, nil
}
zoneStart := dLen - 4
timePortion := dateTime[0:zoneStart]
var zoneMinutes int64
if dateTime[zoneStart] == ':' {
// interval ends in :000
return parseIntervalTime(dateTime)
}
zoneSuffix := dateTime[zoneStart:dLen]
zoneMinutes, err = strconv.ParseInt(zoneSuffix, 10, 0)
if err != nil {
return zeroTime, errors.New("invalid datetime string, zone did not parse")
}
timePortion = fmt.Sprintf("%s%+03d%02d", timePortion, zoneMinutes/60, abs(int(zoneMinutes%60)))
return time.Parse("20060102150405.000000-0700", timePortion)
}
// parseIntervalTime encodes an interval time as an offset to Unix time
// allowing a duration to be computed without precision loss
func parseIntervalTime(interval string) (time.Time, error) {
if len(interval) < 25 || interval[21:22] != ":" {
return time.Time{}, fmt.Errorf("invalid interval time: %s", interval)
}
days, err := parseUintChain(interval[0:8], nil)
hours, err := parseUintChain(interval[8:10], err)
mins, err := parseUintChain(interval[10:12], err)
secs, err := parseUintChain(interval[12:14], err)
micros, err := parseUintChain(interval[15:21], err)
if err != nil {
return time.Time{}, err
}
var stamp uint64 = secs
stamp += days * 86400
stamp += hours * 3600
stamp += mins * 60
return time.Unix(int64(stamp), int64(micros * 1000)), nil
}
func convertIntervalToDuration(variant *ole.VARIANT) (time.Duration, error) {
var err error
interval, err := extractDateTimeString(variant)
if err != nil || len(interval) == 0 {
return 0, err
}
t, err := parseIntervalTime(interval)
if err != nil {
return 0, nil
}
return t.Sub(unixEpoch), nil
}
func parseUintChain(str string, err error) (uint64, error) {
if err != nil {
return 0, err
}
return strconv.ParseUint(str, 10, 0)
}
func abs(num int) int {
if num < 0 {
return -num
}
return num
}

View File

@ -0,0 +1,94 @@
//go:build windows
// +build windows
package wmiext
import (
"fmt"
"syscall"
"unsafe"
"github.com/go-ole/go-ole"
)
type IEnumWbemClassObjectVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
Reset uintptr
Next uintptr
NextAsync uintptr
Clone uintptr
Skip uintptr
}
type Enum struct {
enum *ole.IUnknown
vTable *IEnumWbemClassObjectVtbl
service *Service
}
func (e *Enum) Close() {
if e != nil && e.enum != nil {
e.enum.Release()
}
}
func newEnum(enumerator *ole.IUnknown, service *Service) *Enum {
return &Enum{
enum: enumerator,
vTable: (*IEnumWbemClassObjectVtbl)(unsafe.Pointer(enumerator.RawVTable)),
service: service,
}
}
// NextObject obtains the next instance in an enumeration and sets all fields
// of the struct pointer passed through the target parameter. Otherwise, if
// the target parameter is not a struct pointer type, an error will be
// returned.
func NextObject(enum *Enum, target interface{}) (bool, error) {
var err error
var instance *Instance
if instance, err = enum.Next(); err != nil {
return false, err
}
if instance == nil {
return true, nil
}
defer instance.Close()
return false, instance.GetAll(target)
}
// Next returns the next object instance in this iteration
func (e *Enum) Next() (instance *Instance, err error) {
var res uintptr
var apObjects *ole.IUnknown
var uReturned uint32
res, _, _ = syscall.SyscallN(
e.vTable.Next, // IEnumWbemClassObject::Next()
uintptr(unsafe.Pointer(e.enum)), // IEnumWbemClassObject ptr
uintptr(WBEM_INFINITE), // [in] long lTimeout,
uintptr(1), // [in] ULONG uCount,
uintptr(unsafe.Pointer(&apObjects)), // [out] IWbemClassObject **apObjects,
uintptr(unsafe.Pointer(&uReturned))) // [out] ULONG *puReturned)
if res < 0 {
return nil, ole.NewError(res)
}
if uReturned < 1 {
switch res {
case WBEM_S_NO_ERROR, WBEM_S_FALSE:
// No more elements
return nil, nil
default:
return nil, fmt.Errorf("failure advancing enumeration (%d)", res)
}
}
return newInstance(apObjects, e.service), nil
}

113
vendor/github.com/containers/libhvee/pkg/wmiext/init.go generated vendored Normal file
View File

@ -0,0 +1,113 @@
//go:build windows
// +build windows
package wmiext
import (
"github.com/go-ole/go-ole"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
)
var (
ole32 = windows.NewLazySystemDLL("ole32.dll")
procCoSetProxyBlanket = ole32.NewProc("CoSetProxyBlanket")
procCoInitializeSecurity = ole32.NewProc("CoInitializeSecurity")
modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll")
procSafeArrayCreateVector = modoleaut32.NewProc("SafeArrayCreateVector")
procSafeArrayPutElement = modoleaut32.NewProc("SafeArrayPutElement")
procSafeArrayGetElement = modoleaut32.NewProc("SafeArrayGetElement")
procSafeArrayDestroy = modoleaut32.NewProc("SafeArrayDestroy")
clsidWbemObjectTextSrc = ole.NewGUID("{8d1c559d-84f0-4bb3-a7d5-56a7435a9ba6}")
iidIWbemObjectTextSrc = ole.NewGUID("{bfbf883a-cad7-11d3-a11b-00105a1f515a}")
wmiWbemTxtLocator *ole.IUnknown
wmiWbemLocator *ole.IUnknown
clsidWbemLocator = ole.NewGUID("4590f811-1d3a-11d0-891f-00aa004b2e24")
iidIWbemLocator = ole.NewGUID("dc12a687-737f-11cf-884d-00aa004b2e24")
)
const (
// WMI HRESULT values
WBEM_S_NO_ERROR = 0
WBEM_S_FALSE = 1
WBEM_S_NO_MORE_DATA = 0x40005
// WMI Generic flags
WBEM_FLAG_RETURN_WBEM_COMPLETE = 0x0
WBEM_FLAG_RETURN_IMMEDIATELY = 0x10
WBEM_FLAG_FORWARD_ONLY = 0x20
// WMI Query flags
WBEM_FLAG_SHALLOW = 1
// Timeout flags
WBEM_NO_WAIT = 0
WBEM_INFINITE = 0xFFFFFFFF
// COM Auth Flags
EOAC_NONE = 0
// RPC Authentication
RPC_C_AUTHN_WINNT = 10
// RPC Authentication Level
RPC_C_AUTHN_LEVEL_DEFAULT = 0
RPC_C_AUTHN_LEVEL_CALL = 3
// RPC Authorization
RPC_C_AUTHZ_NONE = 0
// RPC Impersonation
RPC_C_IMP_LEVEL_IMPERSONATE = 3
)
func init() {
var err error
err = ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
if err != nil {
if oleCode, ok := err.(*ole.OleError); ok {
code := oleCode.Code()
// 1 = Already init
if code != 0 && code != 1 {
logrus.Errorf("Unable to initialize COM: %s", err.Error())
return
}
}
}
initSecurity()
wmiWbemLocator, err = ole.CreateInstance(clsidWbemLocator, iidIWbemLocator)
if err != nil {
logrus.Errorf("Could not initialize Wbem components, WMI operations will likely fail %s", err.Error())
}
// IID_IWbemObjectTextSrc Obtain the initial locator to WMI
wmiWbemTxtLocator, err = ole.CreateInstance(clsidWbemObjectTextSrc, iidIWbemObjectTextSrc)
if err != nil {
logrus.Errorf("Could not initialize Wbem components, WMI operations will likely fail %s", err.Error())
}
}
func initSecurity() {
var svc int32 = -1
res, _, _ := procCoInitializeSecurity.Call( // CoInitializeSecurity
uintptr(0), // [in, optional] PSECURITY_DESCRIPTOR pSecDesc,
uintptr(svc), // [in] LONG cAuthSvc,
uintptr(0), // [in, optional] SOLE_AUTHENTICATION_SERVICE *asAuthSvc,
uintptr(0), // [in, optional] void *pReserved1,
uintptr(RPC_C_AUTHN_LEVEL_DEFAULT), // [in] DWORD dwAuthnLevel,
uintptr(RPC_C_IMP_LEVEL_IMPERSONATE), // [in] DWORD dwImpLevel,
uintptr(0), // [in, optional] void *pAuthList,
uintptr(EOAC_NONE), // [in] DWORD dwCapabilities,
uintptr(0)) // [in, optional] void *pReserved3
if res < 0 {
logrus.Errorf("Unable to initialize COM security: %s", ole.NewError(res).Error())
}
}

View File

@ -0,0 +1,634 @@
//go:build windows
// +build windows
package wmiext
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"syscall"
"time"
"unsafe"
"github.com/go-ole/go-ole"
)
const (
WmiPathKey = "__PATH"
)
var (
WindowsEpoch = time.Date(1601, 1, 1, 0, 0, 0, 0, time.UTC)
)
type Instance struct {
object *ole.IUnknown
vTable *IWbemClassObjectVtbl
service *Service
}
type IWbemClassObjectVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
GetQualifierSet uintptr
Get uintptr
Put uintptr
Delete uintptr
GetNames uintptr
BeginEnumeration uintptr
Next uintptr
EndEnumeration uintptr
GetPropertyQualifierSet uintptr
Clone uintptr
GetObjectText uintptr
SpawnDerivedClass uintptr
SpawnInstance uintptr
CompareTo uintptr
GetPropertyOrigin uintptr
InheritsFrom uintptr
GetMethod uintptr
PutMethod uintptr
DeleteMethod uintptr
BeginMethodEnumeration uintptr
NextMethod uintptr
EndMethodEnumeration uintptr
GetMethodQualifierSet uintptr
GetMethodOrigin uintptr
}
type CIMTYPE_ENUMERATION uint32
const (
CIM_ILLEGAL CIMTYPE_ENUMERATION = 0xFFF
CIM_EMPTY CIMTYPE_ENUMERATION = 0
CIM_SINT8 CIMTYPE_ENUMERATION = 16
CIM_UINT8 CIMTYPE_ENUMERATION = 17
CIM_SINT16 CIMTYPE_ENUMERATION = 2
CIM_UINT16 CIMTYPE_ENUMERATION = 18
CIM_SINT32 CIMTYPE_ENUMERATION = 3
CIM_UINT32 CIMTYPE_ENUMERATION = 19
CIM_SINT64 CIMTYPE_ENUMERATION = 20
CIM_UINT64 CIMTYPE_ENUMERATION = 21
CIM_REAL32 CIMTYPE_ENUMERATION = 4
CIM_REAL64 CIMTYPE_ENUMERATION = 5
CIM_BOOLEAN CIMTYPE_ENUMERATION = 11
CIM_STRING CIMTYPE_ENUMERATION = 8
CIM_DATETIME CIMTYPE_ENUMERATION = 101
CIM_REFERENCE CIMTYPE_ENUMERATION = 102
CIM_CHAR16 CIMTYPE_ENUMERATION = 103
CIM_OBJECT CIMTYPE_ENUMERATION = 13
CIM_FLAG_ARRAY CIMTYPE_ENUMERATION = 0x2000
)
type WBEM_FLAVOR_TYPE uint32
const (
WBEM_FLAVOR_DONT_PROPAGATE WBEM_FLAVOR_TYPE = 0
WBEM_FLAVOR_FLAG_PROPAGATE_TO_INSTANCE WBEM_FLAVOR_TYPE = 0x1
WBEM_FLAVOR_FLAG_PROPAGATE_TO_DERIVED_CLASS WBEM_FLAVOR_TYPE = 0x2
WBEM_FLAVOR_MASK_PROPAGATION WBEM_FLAVOR_TYPE = 0xf
WBEM_FLAVOR_OVERRIDABLE WBEM_FLAVOR_TYPE = 0
WBEM_FLAVOR_NOT_OVERRIDABLE WBEM_FLAVOR_TYPE = 0x10
WBEM_FLAVOR_MASK_PERMISSIONS WBEM_FLAVOR_TYPE = 0x10
WBEM_FLAVOR_ORIGIN_LOCAL WBEM_FLAVOR_TYPE = 0
WBEM_FLAVOR_ORIGIN_PROPAGATED WBEM_FLAVOR_TYPE = 0x20
WBEM_FLAVOR_ORIGIN_SYSTEM WBEM_FLAVOR_TYPE = 0x40
WBEM_FLAVOR_MASK_ORIGIN WBEM_FLAVOR_TYPE = 0x60
WBEM_FLAVOR_NOT_AMENDED WBEM_FLAVOR_TYPE = 0
WBEM_FLAVOR_AMENDED WBEM_FLAVOR_TYPE = 0x80
WBEM_FLAVOR_MASK_AMENDED WBEM_FLAVOR_TYPE = 0x80
)
func newInstance(object *ole.IUnknown, service *Service) *Instance {
instance := &Instance{
object: object,
vTable: (*IWbemClassObjectVtbl)(unsafe.Pointer(object.RawVTable)),
service: service,
}
return instance
}
// Close cleans up all memory associated with this instance.
func (i *Instance) Close() {
if i != nil && i.object != nil {
i.object.Release()
}
}
// GetClassName Gets the WMI class name for this WMI object instance
func (i *Instance) GetClassName() (className string, err error) {
return i.GetAsString(`__CLASS`)
}
// Path gets the WMI object path of this instance
func (i *Instance) Path() (string, error) {
ref, _, _, err := i.GetAsAny(WmiPathKey)
return ref.(string), err
}
// IsReferenceProperty returns whether the property is of type CIM_REFERENCE, a string which points to
// an object path of another instance.
func (i *Instance) IsReferenceProperty(name string) (bool, error) {
_, cimType, _, err := i.GetAsAny(name)
return cimType == CIM_REFERENCE, err
}
// SpawnInstance create a new WMI object instance that is zero-initialized. The returned instance
// will not respect expected default values, which must be populated by other means.
func (i *Instance) SpawnInstance() (instance *Instance, err error) {
var res uintptr
var newUnknown *ole.IUnknown
res, _, _ = syscall.SyscallN(
i.vTable.SpawnInstance, // IWbemClassObject::SpawnInstance(
uintptr(unsafe.Pointer(i.object)), // IWbemClassObject ptr
uintptr(0), // [in] long lFlags,
uintptr(unsafe.Pointer(&newUnknown))) // [out] IWbemClassObject **ppNewInstance)
if res != 0 {
return nil, ole.NewError(res)
}
return newInstance(newUnknown, i.service), nil
}
// CloneInstance create a new cloned copy of this WMI instance.
func (i *Instance) CloneInstance() (*Instance, error) {
classObj := i.object
vTable := (*IWbemClassObjectVtbl)(unsafe.Pointer(classObj.RawVTable))
var cloned *ole.IUnknown
ret, _, _ := syscall.SyscallN(
vTable.Clone, // IWbemClassObject::Clone(
uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr
uintptr(unsafe.Pointer(&cloned))) // [out] IWbemClassObject **ppCopy)
if ret != 0 {
return nil, ole.NewError(ret)
}
return newInstance(cloned, i.service), nil
}
// PutAll sets all fields of this instance to the passed src parameter's fields, converting accordingly.
// The src parameter must be a pointer to a struct, otherwise an error will be returned.
func (i *Instance) PutAll(src interface{}) error {
val := reflect.ValueOf(src)
if val.Kind() == reflect.Pointer {
val = val.Elem()
}
if val.Kind() != reflect.Struct {
return errors.New("not a struct or pointer to struct")
}
props, err := i.GetAllProperties()
if err != nil {
return err
}
return i.instancePutAllTraverse(val, props)
}
func (i *Instance) instancePutAllTraverse(val reflect.Value, propMap map[string]interface{}) error {
for j := 0; j < val.NumField(); j++ {
fieldVal := val.Field(j)
fieldType := val.Type().Field(j)
if fieldType.Type.Kind() == reflect.Struct && fieldType.Anonymous {
if err := i.instancePutAllTraverse(fieldVal, propMap); err != nil {
return err
}
continue
}
if strings.HasPrefix(fieldType.Name, "S__") {
continue
}
if !fieldType.IsExported() {
continue
}
if _, exists := propMap[fieldType.Name]; !exists {
continue
}
if fieldVal.Kind() == reflect.String && fieldVal.Len() == 0 {
continue
}
if err := i.Put(fieldType.Name, fieldVal.Interface()); err != nil {
return err
}
}
return nil
}
// Put sets the specified property to the passed Golang value, converting appropriately.
func (i *Instance) Put(name string, value interface{}) (err error) {
var variant ole.VARIANT
switch cast := value.(type) {
case ole.VARIANT:
variant = cast
case *ole.VARIANT:
variant = *cast
default:
variant, err = NewAutomationVariant(value)
if err != nil {
return err
}
}
var wszName *uint16
if wszName, err = syscall.UTF16PtrFromString(name); err != nil {
return
}
classObj := i.object
vTable := (*IWbemClassObjectVtbl)(unsafe.Pointer(classObj.RawVTable))
res, _, _ := syscall.SyscallN(
vTable.Put, // IWbemClassObject::Put(
uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr
uintptr(unsafe.Pointer(wszName)), // [in] LPCWSTR wszName,
uintptr(0), // [in] long lFlags,
uintptr(unsafe.Pointer(&variant)), // [in] VARIANT *pVal,
uintptr(0)) // [in] CIMTYPE Type)
if res != 0 {
return ole.NewError(res)
}
_ = variant.Clear()
return
}
// GetCimText returns the CIM XML representation of this instance. Some WMI methods use a string
// parameter to represent a full complex object, and this method is used to generate
// the expected format.
func (i *Instance) GetCimText() string {
type wmiWbemTxtSrcVtable struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
GetTxt uintptr
}
const CIM_XML_FORMAT = 1
classObj := i.object
vTable := (*wmiWbemTxtSrcVtable)(unsafe.Pointer(wmiWbemTxtLocator.RawVTable))
var retString *uint16
res, _, _ := syscall.SyscallN(
vTable.GetTxt, // IWbemObjectTextSrc::GetText()
uintptr(unsafe.Pointer(wmiWbemLocator)), // IWbemObjectTextSrc ptr
uintptr(0), // [in] long lFlags
uintptr(unsafe.Pointer(classObj)), // [in] IWbemClassObject *pObj
uintptr(CIM_XML_FORMAT), // [in] ULONG uObjTextFormat,
uintptr(0), // [in] IWbemContext *pCtx,
uintptr(unsafe.Pointer(&retString))) // [out] BSTR *strText)
if res != 0 {
return ""
}
itemStr := ole.BstrToString(retString)
return itemStr
}
// GetAll gets all fields that map to a target struct and populates all struct fields according to
// the expected type information. The target parameter should be a pointer to a struct, and
// will return an error otherwise.
func (i *Instance) GetAll(target interface{}) error {
elem := reflect.ValueOf(target)
if elem.Kind() != reflect.Ptr || elem.IsNil() {
return errors.New("invalid destination type for mapping a WMI instance to an object")
}
// deref pointer
elem = elem.Elem()
var err error
if err = i.BeginEnumeration(); err != nil {
return err
}
properties := make(map[string]*ole.VARIANT)
for {
var name string
var value *ole.VARIANT
var done bool
if done, name, value, _, _, err = i.NextAsVariant(); err != nil {
return err
}
if done {
break
}
if value != nil {
properties[name] = value
}
}
defer func() {
for _, v := range properties {
_ = v.Clear()
}
}()
_ = i.EndEnumeration()
return i.instanceGetAllPopulate(elem, elem.Type(), properties)
}
// GetAsAny gets a property and converts it to a Golang type that matches the internal
// variant automation type passed back from WMI. For usage with predictable static
// type mapping, use GetAsString(), GetAsUint(), or GetAll() instead of this method.
func (i *Instance) GetAsAny(name string) (interface{}, CIMTYPE_ENUMERATION, WBEM_FLAVOR_TYPE, error) {
variant, cimType, flavor, err := i.GetAsVariant(name)
if err != nil {
return nil, cimType, flavor, err
}
defer variant.Clear()
// Since there is no type information only perform the stock conversion
result := convertToGenericValue(variant)
return result, cimType, flavor, err
}
// GetAsString gets a property value as a string value, converting if necessary
func (i *Instance) GetAsString(name string) (value string, err error) {
variant, _, _, err := i.GetAsVariant(name)
if err != nil || variant == nil {
return "", err
}
defer variant.Clear()
// TODO: replace with something better
return fmt.Sprintf("%v", convertToGenericValue(variant)), nil
}
// GetAsUint gets a property value as a uint value, if conversion is possible. Otherwise,
// returns an error.
func (i *Instance) GetAsUint(name string) (uint, error) {
val, _, _, err := i.GetAsAny(name)
if err != nil {
return 0, err
}
switch ret := val.(type) {
case int:
return uint(ret), nil
case int8:
return uint(ret), nil
case int16:
return uint(ret), nil
case int32:
return uint(ret), nil
case int64:
return uint(ret), nil
case uint:
return ret, nil
case uint8:
return uint(ret), nil
case uint16:
return uint(ret), nil
case uint32:
return uint(ret), nil
case uint64:
return uint(ret), nil
case string:
parse, err := strconv.ParseUint(ret, 10, 64)
return uint(parse), err
default:
return 0, fmt.Errorf("type conversion from %T on param %s not supported", val, name)
}
}
// GetAsVariant obtains a specified property value, if it exists.
func (i *Instance) GetAsVariant(name string) (*ole.VARIANT, CIMTYPE_ENUMERATION, WBEM_FLAVOR_TYPE, error) {
var variant ole.VARIANT
var err error
var wszName *uint16
var cimType CIMTYPE_ENUMERATION
var flavor WBEM_FLAVOR_TYPE
if wszName, err = syscall.UTF16PtrFromString(name); err != nil {
return nil, 0, 0, err
}
classObj := i.object
vTable := (*IWbemClassObjectVtbl)(unsafe.Pointer(classObj.RawVTable))
res, _, _ := syscall.SyscallN(
vTable.Get, // IWbemClassObject::Get(
uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr
uintptr(unsafe.Pointer(wszName)), // [in] LPCWSTR wszName,
uintptr(0), // [in] long lFlags,
uintptr(unsafe.Pointer(&variant)), // [out] VARIANT *pVal,
uintptr(unsafe.Pointer(&cimType)), // [out, optional] CIMTYPE *pType,
uintptr(unsafe.Pointer(&flavor))) // [out, optional] long *plFlavor)
if res != 0 {
return nil, 0, 0, ole.NewError(res)
}
return &variant, cimType, flavor, nil
}
// Next retrieves the next property as a Golang type when iterating the properties using an enumerator
// created by BeginEnumeration(). The returned value's type represents the internal automation type
// used by WMI. It is usually preferred to use GetAsXXX(), GetAll(), or GetAll Properties() over this
// method.
func (i *Instance) Next() (done bool, name string, value interface{}, cimType CIMTYPE_ENUMERATION, flavor WBEM_FLAVOR_TYPE, err error) {
var variant *ole.VARIANT
done, name, variant, cimType, flavor, err = i.NextAsVariant()
if err == nil && !done {
defer variant.Clear()
value = convertToGenericValue(variant)
}
return
}
// NextAsVariant retrieves the next property as a VARIANT type when iterating the properties using an enumerator
// created by BeginEnumeration(). The returned value's type represents the internal automation type
// used by WMI. It is usually preferred to use GetAsXXX(), GetAll(), or GetAllProperties() over this
// method. Callers are responsible for clearing the VARIANT, otherwise associated memory will leak.
func (i *Instance) NextAsVariant() (bool, string, *ole.VARIANT, CIMTYPE_ENUMERATION, WBEM_FLAVOR_TYPE, error) {
var res uintptr
var strName *uint16
var variant ole.VARIANT
var cimType CIMTYPE_ENUMERATION
var flavor WBEM_FLAVOR_TYPE
res, _, _ = syscall.SyscallN(
i.vTable.Next, // IWbemClassObject::Next(
uintptr(unsafe.Pointer(i.object)), // IWbemClassObject ptr
uintptr(0), // [in] long lFlags,
uintptr(unsafe.Pointer(&strName)), // [out] BSTR *strName,
uintptr(unsafe.Pointer(&variant)), // [out] VARIANT *pVal,
uintptr(unsafe.Pointer(&cimType)), // [out, optional] CIMTYPE *pType,
uintptr(unsafe.Pointer(&flavor))) // [out, optional] long *plFlavor
if res < 0 {
return false, "", nil, cimType, flavor, ole.NewError(res)
}
if res == WBEM_S_NO_MORE_DATA {
return true, "", nil, cimType, flavor, nil
}
defer ole.SysFreeString((*int16)(unsafe.Pointer(strName)))
name := ole.BstrToString(strName)
return false, name, &variant, cimType, flavor, nil
}
// GetAllProperties gets all properties on this instance. The returned map is keyed by the field name and the value
// is a Golang type which matches the WMI internal implementation. For static type conversions,
// it's recommended to use either GetAll(), which uses struct fields for type information, or
// the GetAsXXX() methods.
func (i *Instance) GetAllProperties() (map[string]interface{}, error) {
var err error
properties := make(map[string]interface{})
if err = i.BeginEnumeration(); err != nil {
return nil, err
}
defer i.EndEnumeration()
for {
var name string
var value interface{}
var done bool
if done, name, value, _, _, err = i.Next(); err != nil || done {
return properties, err
}
properties[name] = value
}
}
// GetMethodParameters returns a WMI class object which represents the [in] method parameters for a method invocation.
// This is an advanced method, used for dynamic introspection or manual method invocation. In most
// cases it is recommended to use BeginInvoke() instead, which constructs the parameter payload
// automatically.
func (i *Instance) GetMethodParameters(method string) (*Instance, error) {
var err error
var res uintptr
var inSignature *ole.IUnknown
var wszName *uint16
if wszName, err = syscall.UTF16PtrFromString(method); err != nil {
return nil, err
}
res, _, _ = syscall.SyscallN(
i.vTable.GetMethod, // IWbemClassObject::GetMethod(
uintptr(unsafe.Pointer(i.object)), // IWbemClassObject ptr
uintptr(unsafe.Pointer(wszName)), // [in] LPCWSTR wszName
uintptr(0), // [in] long lFlags,
uintptr(unsafe.Pointer(&inSignature)), // [out] IWbemClassObject **ppInSignature,
uintptr(0)) // [out] IWbemClassObject **ppOutSignature)
if res != 0 {
return nil, ole.NewError(res)
}
return newInstance(inSignature, i.service), nil
}
func (i *Instance) instanceGetAllPopulate(elem reflect.Value, elemType reflect.Type, properties map[string]*ole.VARIANT) error {
var err error
for j := 0; j < elemType.NumField(); j++ {
fieldType := elemType.Field(j)
fieldVal := elem.Field(j)
if !fieldType.IsExported() {
continue
}
if fieldType.Type.Kind() == reflect.Struct && fieldType.Anonymous {
if err := i.instanceGetAllPopulate(fieldVal, fieldType.Type, properties); err != nil {
return err
}
continue
}
fieldName := fieldType.Name
if strings.HasPrefix(fieldName, "S__") {
fieldName = fieldName[1:]
}
if variant, ok := properties[fieldName]; ok {
var val interface{}
if val, err = convertToGoType(variant, fieldVal, fieldType.Type); err != nil {
return err
}
if val != nil {
fieldVal.Set(reflect.ValueOf(val))
}
}
}
return nil
}
// BeginEnumeration begins iterating the property list on this instance. This is an advanced method.
// In most cases, the GetAsXXX() methods, GetAll(), and GetAllProperties() methods should be
// preferred.
func (i *Instance) BeginEnumeration() error {
classObj := i.object
vTable := (*IWbemClassObjectVtbl)(unsafe.Pointer(classObj.RawVTable))
result, _, _ := syscall.SyscallN(
vTable.BeginEnumeration, // IWbemClassObject::BeginEnumeration(
uintptr(unsafe.Pointer(classObj)), // IWbemClassObject ptr,
uintptr(0)) // [in] long lEnumFlags) // 0 = defaults
if result != 0 {
return ole.NewError(result)
}
return nil
}
// EndEnumeration completes iterating a property list on this instance. This is an advanced method.
// In most cases, the GetAsXXX() methods, GetAll(), and GetAllProperties() methods
// should be preferred.
func (i *Instance) EndEnumeration() error {
res, _, _ := syscall.SyscallN(
i.vTable.EndEnumeration, // IWbemClassObject::EndEnumeration(
uintptr(unsafe.Pointer(i.object))) // IWbemClassObject ptr)
if res != 0 {
return ole.NewError(res)
}
return nil
}
// BeginInvoke invokes a method on this Instance. Returns a MethodExecutor builder object
// that is used to construct the input parameters (via calls to In()), perform the
// invocation (using calls to Execute()), retrieve output parameters (via calls to
// Out()), and finally the method return value (using a call to End())
func (i *Instance) BeginInvoke(method string) *MethodExecutor {
objPath, err := i.Path()
if err != nil {
return &MethodExecutor{err: err}
}
var class, inParam *Instance
if class, err = i.service.GetClassInstance(i); err == nil {
inParam, err = class.GetMethodParameters(method)
class.Close()
}
return &MethodExecutor{method: method, path: objPath, service: i.service, inParam: inParam, err: err}
}

View File

@ -0,0 +1,125 @@
//go:build windows
// +build windows
package wmiext
import (
"fmt"
"reflect"
"github.com/go-ole/go-ole"
)
type MethodExecutor struct {
err error
path string
method string
service *Service
inParam *Instance
outParam *Instance
}
// In sets an input parameter for the method of this invocation, converting appropriately
func (e *MethodExecutor) In(name string, value interface{}) *MethodExecutor {
if e.err == nil && e.inParam != nil {
switch t := value.(type) {
case *Instance:
var ref bool
if ref, e.err = e.inParam.IsReferenceProperty(name); e.err != nil {
return e
}
if !ref {
// Embedded Object
break
}
if value, e.err = t.Path(); e.err != nil {
return e
}
}
e.err = e.inParam.Put(name, value)
}
return e
}
// Out sets the specified output parameter, and assigns the value parameter to the result.
// The value parameter must be a reference to the field that should be set.
func (e *MethodExecutor) Out(name string, value interface{}) *MethodExecutor {
if e.err == nil && e.outParam != nil {
var variant *ole.VARIANT
var cimType CIMTYPE_ENUMERATION
var result interface{}
dest := reflect.ValueOf(value)
if dest.Kind() != reflect.Ptr {
e.err = fmt.Errorf("Out() on %q called with %T, out parameters must be a reference", name, value)
return e
}
dest = dest.Elem()
variant, cimType, _, e.err = e.outParam.GetAsVariant(name)
if e.err != nil || variant == nil {
return e
}
defer variant.Clear()
if _, ok := value.(**Instance); ok && cimType == CIM_REFERENCE {
path := variant.ToString()
result, e.err = e.service.GetObject(path)
if e.err != nil {
return e
}
} else {
target := reflect.ValueOf(value).Elem()
result, e.err = convertToGoType(variant, target, target.Type())
if e.err != nil {
return e
}
}
newValue := reflect.ValueOf(result)
if result == nil {
// Nil must be typed to the destination
newValue = reflect.Zero(dest.Type())
}
dest.Set(newValue)
}
return e
}
// Execute executes the method after in parameters have been specified using In()
func (e *MethodExecutor) Execute() *MethodExecutor {
defer e.cleanupInputs()
if e.err == nil {
e.outParam, e.err = e.service.ExecMethod(e.path, e.method, e.inParam)
}
return e
}
func (e *MethodExecutor) cleanupInputs() {
if e.inParam != nil {
e.inParam.Close()
e.inParam = nil
}
}
// End completes the method invocation and returns an error indicating the return
// code of the underlying method
func (e *MethodExecutor) End() error {
e.cleanupInputs()
if e.outParam != nil {
e.outParam.Close()
e.outParam = nil
}
return e.err
}
// Obtains the last error that occurred while building the invocation. Once
// an error has occurred, all future operations are treated as a no-op.
func (e *MethodExecutor) Error() error {
return e.err
}

53
vendor/github.com/containers/libhvee/pkg/wmiext/job.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
//go:build windows
// +build windows
package wmiext
import (
"fmt"
"time"
)
type JobError struct {
ErrorCode int
}
func (err *JobError) Error() string {
return fmt.Sprintf("Job failed with error code: %d", err.ErrorCode)
}
// WaitJob waits on the specified job instance until it has completed and
// returns a JobError containing the result code in the event of
// a failure.
func WaitJob(service *Service, job *Instance) error {
var jobs []*Instance
defer func() {
for _, job := range jobs {
job.Close()
}
}()
for {
state, _, _, err := job.GetAsAny("JobState")
if err != nil {
return err
}
time.Sleep(100 * time.Millisecond)
job, _ = service.RefetchObject(job)
jobs = append(jobs, job)
// 7+ = completed
if state.(int32) >= 7 {
break
}
}
result, _, _, err := job.GetAsAny("ErrorCode")
if err != nil {
return err
}
if result.(int32) != 0 {
return &JobError{ErrorCode: int(result.(int32))}
}
return nil
}

View File

@ -0,0 +1,396 @@
//go:build windows
// +build windows
package wmiext
import (
"errors"
"fmt"
"syscall"
"unsafe"
"github.com/go-ole/go-ole"
)
type IWbemLocatorVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
ConnectServer uintptr
}
type Service struct {
service *ole.IUnknown
vTable *IWbemServicesVtbl
}
type IWbemServicesVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
OpenNamespace uintptr
CancelAsyncCall uintptr
QueryObjectSink uintptr
GetObject uintptr
GetObjectAsync uintptr
PutClass uintptr
PutClassAsync uintptr
DeleteClass uintptr
DeleteClassAsync uintptr
CreateClassEnum uintptr
CreateClassEnumAsync uintptr
PutInstance uintptr
PutInstanceAsync uintptr
DeleteInstance uintptr
DeleteInstanceAsync uintptr
CreateInstanceEnum uintptr
CreateInstanceEnumAsync uintptr
ExecQuery uintptr
ExecQueryAsync uintptr
ExecNotificationQuery uintptr
ExecNotificationQueryAsync uintptr
ExecMethod uintptr
ExecMethodAsync uintptr
}
func connectService(namespace string) (*Service, error) {
if wmiWbemLocator == nil {
return nil, errors.New("WMI failed initialization, service calls can not proceed")
}
var err error
var res uintptr
var strResource *uint16
var service *ole.IUnknown
loc := fmt.Sprintf(`\\.\%s`, namespace)
if strResource, err = syscall.UTF16PtrFromString(loc); err != nil {
return nil, err
}
myVTable := (*IWbemLocatorVtbl)(unsafe.Pointer(wmiWbemLocator.RawVTable))
res, _, _ = syscall.SyscallN(
myVTable.ConnectServer, // IWbemLocator::ConnectServer(
uintptr(unsafe.Pointer(wmiWbemLocator)), // IWbemLocator ptr
uintptr(unsafe.Pointer(strResource)), // [in] const BSTR strNetworkResource,
uintptr(0), // [in] const BSTR strUser,
uintptr(0), // [in] const BSTR strPassword,
uintptr(0), // [in] const BSTR strLocale,
uintptr(WBEM_FLAG_CONNECT_USE_MAX_WAIT), // [in] long lSecurityFlags,
uintptr(0), // [in] const BSTR strAuthority,
uintptr(0), // [in] IWbemContext *pCtx,
uintptr(unsafe.Pointer(&service))) // [out] IWbemServices **ppNamespace)
if res != 0 {
return nil, ole.NewError(res)
}
if err = CoSetProxyBlanket(service); err != nil {
return nil, err
}
return newService(service), nil
}
func newService(service *ole.IUnknown) *Service {
return &Service{
service: service,
vTable: (*IWbemServicesVtbl)(unsafe.Pointer(service.RawVTable)),
}
}
const (
WBEM_FLAG_CONNECT_USE_MAX_WAIT = 0x80
)
func CoSetProxyBlanket(service *ole.IUnknown) (err error) {
res, _, _ := procCoSetProxyBlanket.Call( //CoSetProxyBlanket(
uintptr(unsafe.Pointer(service)), // [in] IUnknown *pProxy,
uintptr(RPC_C_AUTHN_WINNT), // [in] DWORD dwAuthnSvc,
uintptr(RPC_C_AUTHZ_NONE), // [in] DWORD dwAuthzSvc,
uintptr(0), // [in, opt] OLECHAR *pServerPrincName,
uintptr(RPC_C_AUTHN_LEVEL_CALL), // [in] DWORD dwAuthnLevel,
uintptr(RPC_C_IMP_LEVEL_IMPERSONATE), // [in] DWORD dwImpLevel,
uintptr(0), // [in, opt] RPC_AUTH_IDENTITY_HANDLE pAuthInfo,
uintptr(EOAC_NONE)) // [in] DWORD dwCapabilities)
if res != 0 {
return ole.NewError(res)
}
return nil
}
// NewLocalService creates a service and connect it to the local system at the specified namespace
func NewLocalService(namespace string) (s *Service, err error) {
return connectService(namespace)
}
// Close frees all associated memory with this service
func (s *Service) Close() {
if s != nil && s.service != nil {
s.service.Release()
}
}
// ExecQuery executes a WQL query and returns an enumeration to iterate the result set.
// Queries are executed in a semi-synchronous fashion.
func (s *Service) ExecQuery(wqlQuery string) (*Enum, error) {
var err error
var pEnum *ole.IUnknown
var strQuery *uint16
var strQL *uint16
if strQL, err = syscall.UTF16PtrFromString("WQL"); err != nil {
return nil, err
}
if strQuery, err = syscall.UTF16PtrFromString(wqlQuery); err != nil {
return nil, err
}
// Semisynchronous mode = return immed + forward (for perf)
flags := WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY
hres, _, _ := syscall.SyscallN(
s.vTable.ExecQuery, // IWbemServices::ExecQuery(
uintptr(unsafe.Pointer(s.service)), // IWbemServices ptr
uintptr(unsafe.Pointer(strQL)), // [in] const BSTR strQueryLanguage,
uintptr(unsafe.Pointer(strQuery)), // [in] const BSTR strQuery,
uintptr(flags), // [in] long lFlags,
uintptr(0), // [in] IWbemContext *pCtx,
uintptr(unsafe.Pointer(&pEnum))) // [out] IEnumWbemClassObject **ppEnum)
if hres != 0 {
return nil, ole.NewError(hres)
}
if err = CoSetProxyBlanket(pEnum); err != nil {
return nil, err
}
return newEnum(pEnum, s), nil
}
// GetObject obtains a single WMI class or instance given its path
func (s *Service) GetObject(objectPath string) (instance *Instance, err error) {
var pObject *ole.IUnknown
var strObjectPath *uint16
if strObjectPath, err = syscall.UTF16PtrFromString(objectPath); err != nil {
return
}
// Synchronous call
flags := WBEM_FLAG_RETURN_WBEM_COMPLETE
res, _, _ := syscall.SyscallN(
s.vTable.GetObject, // IWbemServices::GetObject(
uintptr(unsafe.Pointer(s.service)), // IWbemServices ptr
uintptr(unsafe.Pointer(strObjectPath)), // [in] const BSTR strObjectPath,
uintptr(flags), // [in] long lFlags,
uintptr(0), // [in] IWbemContext *pCtx,
uintptr(unsafe.Pointer(&pObject)), // [out] IWbemClassObject **ppObject,
uintptr(0)) // [out] IWbemCallResult **ppCallResult)
if res < 0 {
// returns WBEM_E_PROVIDER_NOT_FOUND when no entry found
return nil, ole.NewError(res)
}
return newInstance(pObject, s), nil
}
// GetObjectAsObject gets an object by its path and set all fields of the passed in target to match the instance's
// properties. Conversion is performed as appropriate.
func (s *Service) GetObjectAsObject(objPath string, target interface{}) error {
instance, err := s.GetObject(objPath)
if err != nil {
return err
}
defer instance.Close()
return instance.GetAll(target)
}
// CreateInstanceEnum creates an enumerator that iterates all registered object instances for a given className.
func (s *Service) CreateInstanceEnum(className string) (*Enum, error) {
var err error
var pEnum *ole.IUnknown
var strFilter *uint16
if strFilter, err = syscall.UTF16PtrFromString(className); err != nil {
return nil, err
}
// No subclasses in result set
flags := WBEM_FLAG_SHALLOW
res, _, _ := syscall.SyscallN(
s.vTable.CreateInstanceEnum, // IWbemServices::CreateInstanceEnum(
uintptr(unsafe.Pointer(s.service)), // IWbemServices ptr
uintptr(unsafe.Pointer(strFilter)), // [in] const BSTR strFilter,
uintptr(flags), // [in] long lFlags,
uintptr(0), // [in] IWbemContext *pCtx,
uintptr(unsafe.Pointer(&pEnum))) // [out] IEnumWbemClassObject **ppEnum)
if res < 0 {
return nil, ole.NewError(res)
}
if err = CoSetProxyBlanket(pEnum); err != nil {
return nil, err
}
return newEnum(pEnum, s), nil
}
// ExecMethod executes a method using the specified class and parameter payload instance. The parameter payload
// instance can be constructed using Instance.GetMethodParameters(). This is an advanced method, it is
// recommended to use BeginInvoke() instead, where possible.
func (s *Service) ExecMethod(className string, methodName string, inParams *Instance) (*Instance, error) {
var err error
var outParams *ole.IUnknown
var strObjectPath *uint16
var strMethodName *uint16
if strObjectPath, err = syscall.UTF16PtrFromString(className); err != nil {
return nil, err
}
if strMethodName, err = syscall.UTF16PtrFromString(methodName); err != nil {
return nil, err
}
res, _, _ := syscall.SyscallN(
s.vTable.ExecMethod, // IWbemServices::ExecMethod(
uintptr(unsafe.Pointer(s.service)), // IWbemServices ptr
uintptr(unsafe.Pointer(strObjectPath)), // [in] const BSTR strObjectPath,
uintptr(unsafe.Pointer(strMethodName)), // [in] const BSTR strMethodName,
uintptr(0), // [in] long lFlags,
uintptr(0), // [in] IWbemContext *pCtx,
uintptr(unsafe.Pointer(inParams.object)), // [in] IWbemClassObject *pInParams,
uintptr(unsafe.Pointer(&outParams)), // [out] IWbemClassObject **ppOutParams,
uintptr(0)) // [out] IWbemCallResult **ppCallResult)
if res < 0 {
return nil, ole.NewError(res)
}
return newInstance(outParams, s), nil
}
// FindFirstInstance find and returns the first WMI Instance in the result set for a WSL query.
func (s *Service) FindFirstInstance(wql string) (*Instance, error) {
var enum *Enum
var err error
if enum, err = s.ExecQuery(wql); err != nil {
return nil, err
}
defer enum.Close()
instance, err := enum.Next()
if err != nil {
return nil, err
}
if instance == nil {
return nil, errors.New("no results found")
}
return instance, nil
}
// FindFirstRelatedInstance finds and returns a related associator of the specified WMI object path of the
// expected className type.
func (s *Service) FindFirstRelatedInstance(objPath string, className string) (*Instance, error) {
wql := fmt.Sprintf("ASSOCIATORS OF {%s} WHERE ResultClass = %s", objPath, className)
return s.FindFirstInstance(wql)
}
// FindFirstObject finds and returns the first WMI Instance in the result set for a WSL query, and
// populates the specified struct pointer passed in through the target parameter.
func (s *Service) FindFirstObject(wql string, target interface{}) error {
var enum *Enum
var err error
if enum, err = s.ExecQuery(wql); err != nil {
return err
}
defer enum.Close()
done, err := NextObject(enum, target)
if err != nil {
return err
}
if done {
return errors.New("no results found")
}
return nil
}
// GetSingletonInstance gets the first WMI instance of the specified object class type. This is a
// shortcut method for uses where only one instance is expected.
func (s *Service) GetSingletonInstance(className string) (*Instance, error) {
var (
enum *Enum
instance *Instance
err error
)
if enum, err = s.CreateInstanceEnum(className); err != nil {
return nil, err
}
defer enum.Close()
if instance, err = enum.Next(); err != nil {
return nil, err
}
return instance, nil
}
// CreateInstance creates a new WMI object class instance of the specified className, and sets
// all properties according to the passed in struct pointer through the src
// parameter, converting appropriately.
func (s *Service) CreateInstance(className string, src interface{}) (*Instance, error) {
instance, err := s.SpawnInstance(className)
if err != nil {
return nil, err
}
return instance, instance.PutAll(src)
}
// SpawnInstance creates a new zeroed WMI instance. This instance will not contain expected values.
// Those must be retrieved and set separately, or CreateInstance() can be used instead.
func (s *Service) SpawnInstance(className string) (*Instance, error) {
var class *Instance
var err error
if class, err = s.GetObject(className); err != nil {
return nil, err
}
defer class.Close()
return class.SpawnInstance()
}
// RefetchObject re-fetches the object and returns a new instance. The original instance will not
// automatically Close(). Callers of this method will need to manually close the
// original.
func (s *Service) RefetchObject(instance *Instance) (*Instance, error) {
path, err := instance.Path()
if err != nil {
return instance, err
}
return s.GetObject(path)
}
// GetClassInstance gets the WMI class instance associated with the specified object instance.
// This method is used to perform schema queries.
func (s *Service) GetClassInstance(obj *Instance) (*Instance, error) {
name, err := obj.GetClassName()
if err != nil {
return nil, err
}
return s.GetObject(name)
}

8
vendor/github.com/go-ole/go-ole/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,8 @@
language: go
sudo: false
go:
- 1.9.x
- 1.10.x
- 1.11.x
- tip

49
vendor/github.com/go-ole/go-ole/ChangeLog.md generated vendored Normal file
View File

@ -0,0 +1,49 @@
# Version 1.x.x
* **Add more test cases and reference new test COM server project.** (Placeholder for future additions)
# Version 1.2.0-alphaX
**Minimum supported version is now Go 1.4. Go 1.1 support is deprecated, but should still build.**
* Added CI configuration for Travis-CI and AppVeyor.
* Added test InterfaceID and ClassID for the COM Test Server project.
* Added more inline documentation (#83).
* Added IEnumVARIANT implementation (#88).
* Added IEnumVARIANT test cases (#99, #100, #101).
* Added support for retrieving `time.Time` from VARIANT (#92).
* Added test case for IUnknown (#64).
* Added test case for IDispatch (#64).
* Added test cases for scalar variants (#64, #76).
# Version 1.1.1
* Fixes for Linux build.
* Fixes for Windows build.
# Version 1.1.0
The change to provide building on all platforms is a new feature. The increase in minor version reflects that and allows those who wish to stay on 1.0.x to continue to do so. Support for 1.0.x will be limited to bug fixes.
* Move GUID out of variables.go into its own file to make new documentation available.
* Move OleError out of ole.go into its own file to make new documentation available.
* Add documentation to utility functions.
* Add documentation to variant receiver functions.
* Add documentation to ole structures.
* Make variant available to other systems outside of Windows.
* Make OLE structures available to other systems outside of Windows.
## New Features
* Library should now be built on all platforms supported by Go. Library will NOOP on any platform that is not Windows.
* More functions are now documented and available on godoc.org.
# Version 1.0.1
1. Fix package references from repository location change.
# Version 1.0.0
This version is stable enough for use. The COM API is still incomplete, but provides enough functionality for accessing COM servers using IDispatch interface.
There is no changelog for this version. Check commits for history.

21
vendor/github.com/go-ole/go-ole/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright © 2013-2017 Yasuhiro Matsumoto, <mattn.jp@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

46
vendor/github.com/go-ole/go-ole/README.md generated vendored Normal file
View File

@ -0,0 +1,46 @@
# Go OLE
[![Build status](https://ci.appveyor.com/api/projects/status/qr0u2sf7q43us9fj?svg=true)](https://ci.appveyor.com/project/jacobsantos/go-ole-jgs28)
[![Build Status](https://travis-ci.org/go-ole/go-ole.svg?branch=master)](https://travis-ci.org/go-ole/go-ole)
[![GoDoc](https://godoc.org/github.com/go-ole/go-ole?status.svg)](https://godoc.org/github.com/go-ole/go-ole)
Go bindings for Windows COM using shared libraries instead of cgo.
By Yasuhiro Matsumoto.
## Install
To experiment with go-ole, you can just compile and run the example program:
```
go get github.com/go-ole/go-ole
cd /path/to/go-ole/
go test
cd /path/to/go-ole/example/excel
go run excel.go
```
## Continuous Integration
Continuous integration configuration has been added for both Travis-CI and AppVeyor. You will have to add these to your own account for your fork in order for it to run.
**Travis-CI**
Travis-CI was added to check builds on Linux to ensure that `go get` works when cross building. Currently, Travis-CI is not used to test cross-building, but this may be changed in the future. It is also not currently possible to test the library on Linux, since COM API is specific to Windows and it is not currently possible to run a COM server on Linux or even connect to a remote COM server.
**AppVeyor**
AppVeyor is used to build on Windows using the (in-development) test COM server. It is currently only used to test the build and ensure that the code works on Windows. It will be used to register a COM server and then run the test cases based on the test COM server.
The tests currently do run and do pass and this should be maintained with commits.
## Versioning
Go OLE uses [semantic versioning](http://semver.org) for version numbers, which is similar to the version contract of the Go language. Which means that the major version will always maintain backwards compatibility with minor versions. Minor versions will only add new additions and changes. Fixes will always be in patch.
This contract should allow you to upgrade to new minor and patch versions without breakage or modifications to your existing code. Leave a ticket, if there is breakage, so that it could be fixed.
## LICENSE
Under the MIT License: http://mattn.mit-license.org/2013

54
vendor/github.com/go-ole/go-ole/appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,54 @@
# Notes:
# - Minimal appveyor.yml file is an empty file. All sections are optional.
# - Indent each level of configuration with 2 spaces. Do not use tabs!
# - All section names are case-sensitive.
# - Section names should be unique on each level.
version: "1.3.0.{build}-alpha-{branch}"
os: Windows Server 2012 R2
branches:
only:
- master
- v1.2
- v1.1
- v1.0
skip_tags: true
clone_folder: c:\gopath\src\github.com\go-ole\go-ole
environment:
GOPATH: c:\gopath
matrix:
- GOARCH: amd64
GOVERSION: 1.5
GOROOT: c:\go
DOWNLOADPLATFORM: "x64"
install:
- choco install mingw
- SET PATH=c:\tools\mingw64\bin;%PATH%
# - Download COM Server
- ps: Start-FileDownload "https://github.com/go-ole/test-com-server/releases/download/v1.0.2/test-com-server-${env:DOWNLOADPLATFORM}.zip"
- 7z e test-com-server-%DOWNLOADPLATFORM%.zip -oc:\gopath\src\github.com\go-ole\go-ole > NUL
- c:\gopath\src\github.com\go-ole\go-ole\build\register-assembly.bat
# - set
- go version
- go env
- go get -u golang.org/x/tools/cmd/cover
- go get -u golang.org/x/tools/cmd/godoc
- go get -u golang.org/x/tools/cmd/stringer
build_script:
- cd c:\gopath\src\github.com\go-ole\go-ole
- go get -v -t ./...
- go build
- go test -v -cover ./...
# disable automatic tests
test: off
# disable deployment
deploy: off

344
vendor/github.com/go-ole/go-ole/com.go generated vendored Normal file
View File

@ -0,0 +1,344 @@
// +build windows
package ole
import (
"syscall"
"unicode/utf16"
"unsafe"
)
var (
procCoInitialize = modole32.NewProc("CoInitialize")
procCoInitializeEx = modole32.NewProc("CoInitializeEx")
procCoUninitialize = modole32.NewProc("CoUninitialize")
procCoCreateInstance = modole32.NewProc("CoCreateInstance")
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
procCLSIDFromProgID = modole32.NewProc("CLSIDFromProgID")
procCLSIDFromString = modole32.NewProc("CLSIDFromString")
procStringFromCLSID = modole32.NewProc("StringFromCLSID")
procStringFromIID = modole32.NewProc("StringFromIID")
procIIDFromString = modole32.NewProc("IIDFromString")
procCoGetObject = modole32.NewProc("CoGetObject")
procGetUserDefaultLCID = modkernel32.NewProc("GetUserDefaultLCID")
procCopyMemory = modkernel32.NewProc("RtlMoveMemory")
procVariantInit = modoleaut32.NewProc("VariantInit")
procVariantClear = modoleaut32.NewProc("VariantClear")
procVariantTimeToSystemTime = modoleaut32.NewProc("VariantTimeToSystemTime")
procSysAllocString = modoleaut32.NewProc("SysAllocString")
procSysAllocStringLen = modoleaut32.NewProc("SysAllocStringLen")
procSysFreeString = modoleaut32.NewProc("SysFreeString")
procSysStringLen = modoleaut32.NewProc("SysStringLen")
procCreateDispTypeInfo = modoleaut32.NewProc("CreateDispTypeInfo")
procCreateStdDispatch = modoleaut32.NewProc("CreateStdDispatch")
procGetActiveObject = modoleaut32.NewProc("GetActiveObject")
procGetMessageW = moduser32.NewProc("GetMessageW")
procDispatchMessageW = moduser32.NewProc("DispatchMessageW")
)
// coInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func coInitialize() (err error) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx
// Suggests that no value should be passed to CoInitialized.
// Could just be Call() since the parameter is optional. <-- Needs testing to be sure.
hr, _, _ := procCoInitialize.Call(uintptr(0))
if hr != 0 {
err = NewError(hr)
}
return
}
// coInitializeEx initializes COM library with concurrency model.
func coInitializeEx(coinit uint32) (err error) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx
// Suggests that the first parameter is not only optional but should always be NULL.
hr, _, _ := procCoInitializeEx.Call(uintptr(0), uintptr(coinit))
if hr != 0 {
err = NewError(hr)
}
return
}
// CoInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func CoInitialize(p uintptr) (err error) {
// p is ignored and won't be used.
// Avoid any variable not used errors.
p = uintptr(0)
return coInitialize()
}
// CoInitializeEx initializes COM library with concurrency model.
func CoInitializeEx(p uintptr, coinit uint32) (err error) {
// Avoid any variable not used errors.
p = uintptr(0)
return coInitializeEx(coinit)
}
// CoUninitialize uninitializes COM Library.
func CoUninitialize() {
procCoUninitialize.Call()
}
// CoTaskMemFree frees memory pointer.
func CoTaskMemFree(memptr uintptr) {
procCoTaskMemFree.Call(memptr)
}
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
//
// The Programmatic Identifier must be registered, because it will be looked up
// in the Windows Registry. The registry entry has the following keys: CLSID,
// Insertable, Protocol and Shell
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
//
// programID identifies the class id with less precision and is not guaranteed
// to be unique. These are usually found in the registry under
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
// "Program.Component.Version" with version being optional.
//
// CLSIDFromProgID in Windows API.
func CLSIDFromProgID(progId string) (clsid *GUID, err error) {
var guid GUID
lpszProgID := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
hr, _, _ := procCLSIDFromProgID.Call(lpszProgID, uintptr(unsafe.Pointer(&guid)))
if hr != 0 {
err = NewError(hr)
}
clsid = &guid
return
}
// CLSIDFromString retrieves Class ID from string representation.
//
// This is technically the string version of the GUID and will convert the
// string to object.
//
// CLSIDFromString in Windows API.
func CLSIDFromString(str string) (clsid *GUID, err error) {
var guid GUID
lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))
hr, _, _ := procCLSIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
if hr != 0 {
err = NewError(hr)
}
clsid = &guid
return
}
// StringFromCLSID returns GUID formated string from GUID object.
func StringFromCLSID(clsid *GUID) (str string, err error) {
var p *uint16
hr, _, _ := procStringFromCLSID.Call(uintptr(unsafe.Pointer(clsid)), uintptr(unsafe.Pointer(&p)))
if hr != 0 {
err = NewError(hr)
}
str = LpOleStrToString(p)
return
}
// IIDFromString returns GUID from program ID.
func IIDFromString(progId string) (clsid *GUID, err error) {
var guid GUID
lpsz := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(progId)))
hr, _, _ := procIIDFromString.Call(lpsz, uintptr(unsafe.Pointer(&guid)))
if hr != 0 {
err = NewError(hr)
}
clsid = &guid
return
}
// StringFromIID returns GUID formatted string from GUID object.
func StringFromIID(iid *GUID) (str string, err error) {
var p *uint16
hr, _, _ := procStringFromIID.Call(uintptr(unsafe.Pointer(iid)), uintptr(unsafe.Pointer(&p)))
if hr != 0 {
err = NewError(hr)
}
str = LpOleStrToString(p)
return
}
// CreateInstance of single uninitialized object with GUID.
func CreateInstance(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
if iid == nil {
iid = IID_IUnknown
}
hr, _, _ := procCoCreateInstance.Call(
uintptr(unsafe.Pointer(clsid)),
0,
CLSCTX_SERVER,
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&unk)))
if hr != 0 {
err = NewError(hr)
}
return
}
// GetActiveObject retrieves pointer to active object.
func GetActiveObject(clsid *GUID, iid *GUID) (unk *IUnknown, err error) {
if iid == nil {
iid = IID_IUnknown
}
hr, _, _ := procGetActiveObject.Call(
uintptr(unsafe.Pointer(clsid)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&unk)))
if hr != 0 {
err = NewError(hr)
}
return
}
type BindOpts struct {
CbStruct uint32
GrfFlags uint32
GrfMode uint32
TickCountDeadline uint32
}
// GetObject retrieves pointer to active object.
func GetObject(programID string, bindOpts *BindOpts, iid *GUID) (unk *IUnknown, err error) {
if bindOpts != nil {
bindOpts.CbStruct = uint32(unsafe.Sizeof(BindOpts{}))
}
if iid == nil {
iid = IID_IUnknown
}
hr, _, _ := procCoGetObject.Call(
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(programID))),
uintptr(unsafe.Pointer(bindOpts)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&unk)))
if hr != 0 {
err = NewError(hr)
}
return
}
// VariantInit initializes variant.
func VariantInit(v *VARIANT) (err error) {
hr, _, _ := procVariantInit.Call(uintptr(unsafe.Pointer(v)))
if hr != 0 {
err = NewError(hr)
}
return
}
// VariantClear clears value in Variant settings to VT_EMPTY.
func VariantClear(v *VARIANT) (err error) {
hr, _, _ := procVariantClear.Call(uintptr(unsafe.Pointer(v)))
if hr != 0 {
err = NewError(hr)
}
return
}
// SysAllocString allocates memory for string and copies string into memory.
func SysAllocString(v string) (ss *int16) {
pss, _, _ := procSysAllocString.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(v))))
ss = (*int16)(unsafe.Pointer(pss))
return
}
// SysAllocStringLen copies up to length of given string returning pointer.
func SysAllocStringLen(v string) (ss *int16) {
utf16 := utf16.Encode([]rune(v + "\x00"))
ptr := &utf16[0]
pss, _, _ := procSysAllocStringLen.Call(uintptr(unsafe.Pointer(ptr)), uintptr(len(utf16)-1))
ss = (*int16)(unsafe.Pointer(pss))
return
}
// SysFreeString frees string system memory. This must be called with SysAllocString.
func SysFreeString(v *int16) (err error) {
hr, _, _ := procSysFreeString.Call(uintptr(unsafe.Pointer(v)))
if hr != 0 {
err = NewError(hr)
}
return
}
// SysStringLen is the length of the system allocated string.
func SysStringLen(v *int16) uint32 {
l, _, _ := procSysStringLen.Call(uintptr(unsafe.Pointer(v)))
return uint32(l)
}
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
//
// This handles default IDispatch implementation for objects. It haves a few
// limitations with only supporting one language. It will also only return
// default exception codes.
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (disp *IDispatch, err error) {
hr, _, _ := procCreateStdDispatch.Call(
uintptr(unsafe.Pointer(unk)),
v,
uintptr(unsafe.Pointer(ptinfo)),
uintptr(unsafe.Pointer(&disp)))
if hr != 0 {
err = NewError(hr)
}
return
}
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
//
// This will not handle the full implementation of the interface.
func CreateDispTypeInfo(idata *INTERFACEDATA) (pptinfo *IUnknown, err error) {
hr, _, _ := procCreateDispTypeInfo.Call(
uintptr(unsafe.Pointer(idata)),
uintptr(GetUserDefaultLCID()),
uintptr(unsafe.Pointer(&pptinfo)))
if hr != 0 {
err = NewError(hr)
}
return
}
// copyMemory moves location of a block of memory.
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {
procCopyMemory.Call(uintptr(dest), uintptr(src), uintptr(length))
}
// GetUserDefaultLCID retrieves current user default locale.
func GetUserDefaultLCID() (lcid uint32) {
ret, _, _ := procGetUserDefaultLCID.Call()
lcid = uint32(ret)
return
}
// GetMessage in message queue from runtime.
//
// This function appears to block. PeekMessage does not block.
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (ret int32, err error) {
r0, _, err := procGetMessageW.Call(uintptr(unsafe.Pointer(msg)), uintptr(hwnd), uintptr(MsgFilterMin), uintptr(MsgFilterMax))
ret = int32(r0)
return
}
// DispatchMessage to window procedure.
func DispatchMessage(msg *Msg) (ret int32) {
r0, _, _ := procDispatchMessageW.Call(uintptr(unsafe.Pointer(msg)))
ret = int32(r0)
return
}

174
vendor/github.com/go-ole/go-ole/com_func.go generated vendored Normal file
View File

@ -0,0 +1,174 @@
// +build !windows
package ole
import (
"time"
"unsafe"
)
// coInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func coInitialize() error {
return NewError(E_NOTIMPL)
}
// coInitializeEx initializes COM library with concurrency model.
func coInitializeEx(coinit uint32) error {
return NewError(E_NOTIMPL)
}
// CoInitialize initializes COM library on current thread.
//
// MSDN documentation suggests that this function should not be called. Call
// CoInitializeEx() instead. The reason has to do with threading and this
// function is only for single-threaded apartments.
//
// That said, most users of the library have gotten away with just this
// function. If you are experiencing threading issues, then use
// CoInitializeEx().
func CoInitialize(p uintptr) error {
return NewError(E_NOTIMPL)
}
// CoInitializeEx initializes COM library with concurrency model.
func CoInitializeEx(p uintptr, coinit uint32) error {
return NewError(E_NOTIMPL)
}
// CoUninitialize uninitializes COM Library.
func CoUninitialize() {}
// CoTaskMemFree frees memory pointer.
func CoTaskMemFree(memptr uintptr) {}
// CLSIDFromProgID retrieves Class Identifier with the given Program Identifier.
//
// The Programmatic Identifier must be registered, because it will be looked up
// in the Windows Registry. The registry entry has the following keys: CLSID,
// Insertable, Protocol and Shell
// (https://msdn.microsoft.com/en-us/library/dd542719(v=vs.85).aspx).
//
// programID identifies the class id with less precision and is not guaranteed
// to be unique. These are usually found in the registry under
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes, usually with the format of
// "Program.Component.Version" with version being optional.
//
// CLSIDFromProgID in Windows API.
func CLSIDFromProgID(progId string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// CLSIDFromString retrieves Class ID from string representation.
//
// This is technically the string version of the GUID and will convert the
// string to object.
//
// CLSIDFromString in Windows API.
func CLSIDFromString(str string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// StringFromCLSID returns GUID formated string from GUID object.
func StringFromCLSID(clsid *GUID) (string, error) {
return "", NewError(E_NOTIMPL)
}
// IIDFromString returns GUID from program ID.
func IIDFromString(progId string) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// StringFromIID returns GUID formatted string from GUID object.
func StringFromIID(iid *GUID) (string, error) {
return "", NewError(E_NOTIMPL)
}
// CreateInstance of single uninitialized object with GUID.
func CreateInstance(clsid *GUID, iid *GUID) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// GetActiveObject retrieves pointer to active object.
func GetActiveObject(clsid *GUID, iid *GUID) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// VariantInit initializes variant.
func VariantInit(v *VARIANT) error {
return NewError(E_NOTIMPL)
}
// VariantClear clears value in Variant settings to VT_EMPTY.
func VariantClear(v *VARIANT) error {
return NewError(E_NOTIMPL)
}
// SysAllocString allocates memory for string and copies string into memory.
func SysAllocString(v string) *int16 {
u := int16(0)
return &u
}
// SysAllocStringLen copies up to length of given string returning pointer.
func SysAllocStringLen(v string) *int16 {
u := int16(0)
return &u
}
// SysFreeString frees string system memory. This must be called with SysAllocString.
func SysFreeString(v *int16) error {
return NewError(E_NOTIMPL)
}
// SysStringLen is the length of the system allocated string.
func SysStringLen(v *int16) uint32 {
return uint32(0)
}
// CreateStdDispatch provides default IDispatch implementation for IUnknown.
//
// This handles default IDispatch implementation for objects. It haves a few
// limitations with only supporting one language. It will also only return
// default exception codes.
func CreateStdDispatch(unk *IUnknown, v uintptr, ptinfo *IUnknown) (*IDispatch, error) {
return nil, NewError(E_NOTIMPL)
}
// CreateDispTypeInfo provides default ITypeInfo implementation for IDispatch.
//
// This will not handle the full implementation of the interface.
func CreateDispTypeInfo(idata *INTERFACEDATA) (*IUnknown, error) {
return nil, NewError(E_NOTIMPL)
}
// copyMemory moves location of a block of memory.
func copyMemory(dest unsafe.Pointer, src unsafe.Pointer, length uint32) {}
// GetUserDefaultLCID retrieves current user default locale.
func GetUserDefaultLCID() uint32 {
return uint32(0)
}
// GetMessage in message queue from runtime.
//
// This function appears to block. PeekMessage does not block.
func GetMessage(msg *Msg, hwnd uint32, MsgFilterMin uint32, MsgFilterMax uint32) (int32, error) {
return int32(0), NewError(E_NOTIMPL)
}
// DispatchMessage to window procedure.
func DispatchMessage(msg *Msg) int32 {
return int32(0)
}
func GetVariantDate(value uint64) (time.Time, error) {
return time.Now(), NewError(E_NOTIMPL)
}

192
vendor/github.com/go-ole/go-ole/connect.go generated vendored Normal file
View File

@ -0,0 +1,192 @@
package ole
// Connection contains IUnknown for fluent interface interaction.
//
// Deprecated. Use oleutil package instead.
type Connection struct {
Object *IUnknown // Access COM
}
// Initialize COM.
func (*Connection) Initialize() (err error) {
return coInitialize()
}
// Uninitialize COM.
func (*Connection) Uninitialize() {
CoUninitialize()
}
// Create IUnknown object based first on ProgId and then from String.
func (c *Connection) Create(progId string) (err error) {
var clsid *GUID
clsid, err = CLSIDFromProgID(progId)
if err != nil {
clsid, err = CLSIDFromString(progId)
if err != nil {
return
}
}
unknown, err := CreateInstance(clsid, IID_IUnknown)
if err != nil {
return
}
c.Object = unknown
return
}
// Release IUnknown object.
func (c *Connection) Release() {
c.Object.Release()
}
// Load COM object from list of programIDs or strings.
func (c *Connection) Load(names ...string) (errors []error) {
var tempErrors []error = make([]error, len(names))
var numErrors int = 0
for _, name := range names {
err := c.Create(name)
if err != nil {
tempErrors = append(tempErrors, err)
numErrors += 1
continue
}
break
}
copy(errors, tempErrors[0:numErrors])
return
}
// Dispatch returns Dispatch object.
func (c *Connection) Dispatch() (object *Dispatch, err error) {
dispatch, err := c.Object.QueryInterface(IID_IDispatch)
if err != nil {
return
}
object = &Dispatch{dispatch}
return
}
// Dispatch stores IDispatch object.
type Dispatch struct {
Object *IDispatch // Dispatch object.
}
// Call method on IDispatch with parameters.
func (d *Dispatch) Call(method string, params ...interface{}) (result *VARIANT, err error) {
id, err := d.GetId(method)
if err != nil {
return
}
result, err = d.Invoke(id, DISPATCH_METHOD, params)
return
}
// MustCall method on IDispatch with parameters.
func (d *Dispatch) MustCall(method string, params ...interface{}) (result *VARIANT) {
id, err := d.GetId(method)
if err != nil {
panic(err)
}
result, err = d.Invoke(id, DISPATCH_METHOD, params)
if err != nil {
panic(err)
}
return
}
// Get property on IDispatch with parameters.
func (d *Dispatch) Get(name string, params ...interface{}) (result *VARIANT, err error) {
id, err := d.GetId(name)
if err != nil {
return
}
result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params)
return
}
// MustGet property on IDispatch with parameters.
func (d *Dispatch) MustGet(name string, params ...interface{}) (result *VARIANT) {
id, err := d.GetId(name)
if err != nil {
panic(err)
}
result, err = d.Invoke(id, DISPATCH_PROPERTYGET, params)
if err != nil {
panic(err)
}
return
}
// Set property on IDispatch with parameters.
func (d *Dispatch) Set(name string, params ...interface{}) (result *VARIANT, err error) {
id, err := d.GetId(name)
if err != nil {
return
}
result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params)
return
}
// MustSet property on IDispatch with parameters.
func (d *Dispatch) MustSet(name string, params ...interface{}) (result *VARIANT) {
id, err := d.GetId(name)
if err != nil {
panic(err)
}
result, err = d.Invoke(id, DISPATCH_PROPERTYPUT, params)
if err != nil {
panic(err)
}
return
}
// GetId retrieves ID of name on IDispatch.
func (d *Dispatch) GetId(name string) (id int32, err error) {
var dispid []int32
dispid, err = d.Object.GetIDsOfName([]string{name})
if err != nil {
return
}
id = dispid[0]
return
}
// GetIds retrieves all IDs of names on IDispatch.
func (d *Dispatch) GetIds(names ...string) (dispid []int32, err error) {
dispid, err = d.Object.GetIDsOfName(names)
return
}
// Invoke IDispatch on DisplayID of dispatch type with parameters.
//
// There have been problems where if send cascading params..., it would error
// out because the parameters would be empty.
func (d *Dispatch) Invoke(id int32, dispatch int16, params []interface{}) (result *VARIANT, err error) {
if len(params) < 1 {
result, err = d.Object.Invoke(id, dispatch)
} else {
result, err = d.Object.Invoke(id, dispatch, params...)
}
return
}
// Release IDispatch object.
func (d *Dispatch) Release() {
d.Object.Release()
}
// Connect initializes COM and attempts to load IUnknown based on given names.
func Connect(names ...string) (connection *Connection) {
connection.Initialize()
connection.Load(names...)
return
}

153
vendor/github.com/go-ole/go-ole/constants.go generated vendored Normal file
View File

@ -0,0 +1,153 @@
package ole
const (
CLSCTX_INPROC_SERVER = 1
CLSCTX_INPROC_HANDLER = 2
CLSCTX_LOCAL_SERVER = 4
CLSCTX_INPROC_SERVER16 = 8
CLSCTX_REMOTE_SERVER = 16
CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER
CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER
CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
)
const (
COINIT_APARTMENTTHREADED = 0x2
COINIT_MULTITHREADED = 0x0
COINIT_DISABLE_OLE1DDE = 0x4
COINIT_SPEED_OVER_MEMORY = 0x8
)
const (
DISPATCH_METHOD = 1
DISPATCH_PROPERTYGET = 2
DISPATCH_PROPERTYPUT = 4
DISPATCH_PROPERTYPUTREF = 8
)
const (
S_OK = 0x00000000
E_UNEXPECTED = 0x8000FFFF
E_NOTIMPL = 0x80004001
E_OUTOFMEMORY = 0x8007000E
E_INVALIDARG = 0x80070057
E_NOINTERFACE = 0x80004002
E_POINTER = 0x80004003
E_HANDLE = 0x80070006
E_ABORT = 0x80004004
E_FAIL = 0x80004005
E_ACCESSDENIED = 0x80070005
E_PENDING = 0x8000000A
CO_E_CLASSSTRING = 0x800401F3
)
const (
CC_FASTCALL = iota
CC_CDECL
CC_MSCPASCAL
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL
CC_STDCALL
CC_FPFASTCALL
CC_SYSCALL
CC_MPWCDECL
CC_MPWPASCAL
CC_MAX = CC_MPWPASCAL
)
type VT uint16
const (
VT_EMPTY VT = 0x0
VT_NULL VT = 0x1
VT_I2 VT = 0x2
VT_I4 VT = 0x3
VT_R4 VT = 0x4
VT_R8 VT = 0x5
VT_CY VT = 0x6
VT_DATE VT = 0x7
VT_BSTR VT = 0x8
VT_DISPATCH VT = 0x9
VT_ERROR VT = 0xa
VT_BOOL VT = 0xb
VT_VARIANT VT = 0xc
VT_UNKNOWN VT = 0xd
VT_DECIMAL VT = 0xe
VT_I1 VT = 0x10
VT_UI1 VT = 0x11
VT_UI2 VT = 0x12
VT_UI4 VT = 0x13
VT_I8 VT = 0x14
VT_UI8 VT = 0x15
VT_INT VT = 0x16
VT_UINT VT = 0x17
VT_VOID VT = 0x18
VT_HRESULT VT = 0x19
VT_PTR VT = 0x1a
VT_SAFEARRAY VT = 0x1b
VT_CARRAY VT = 0x1c
VT_USERDEFINED VT = 0x1d
VT_LPSTR VT = 0x1e
VT_LPWSTR VT = 0x1f
VT_RECORD VT = 0x24
VT_INT_PTR VT = 0x25
VT_UINT_PTR VT = 0x26
VT_FILETIME VT = 0x40
VT_BLOB VT = 0x41
VT_STREAM VT = 0x42
VT_STORAGE VT = 0x43
VT_STREAMED_OBJECT VT = 0x44
VT_STORED_OBJECT VT = 0x45
VT_BLOB_OBJECT VT = 0x46
VT_CF VT = 0x47
VT_CLSID VT = 0x48
VT_BSTR_BLOB VT = 0xfff
VT_VECTOR VT = 0x1000
VT_ARRAY VT = 0x2000
VT_BYREF VT = 0x4000
VT_RESERVED VT = 0x8000
VT_ILLEGAL VT = 0xffff
VT_ILLEGALMASKED VT = 0xfff
VT_TYPEMASK VT = 0xfff
)
const (
DISPID_UNKNOWN = -1
DISPID_VALUE = 0
DISPID_PROPERTYPUT = -3
DISPID_NEWENUM = -4
DISPID_EVALUATE = -5
DISPID_CONSTRUCTOR = -6
DISPID_DESTRUCTOR = -7
DISPID_COLLECT = -8
)
const (
TKIND_ENUM = 1
TKIND_RECORD = 2
TKIND_MODULE = 3
TKIND_INTERFACE = 4
TKIND_DISPATCH = 5
TKIND_COCLASS = 6
TKIND_ALIAS = 7
TKIND_UNION = 8
TKIND_MAX = 9
)
// Safe Array Feature Flags
const (
FADF_AUTO = 0x0001
FADF_STATIC = 0x0002
FADF_EMBEDDED = 0x0004
FADF_FIXEDSIZE = 0x0010
FADF_RECORD = 0x0020
FADF_HAVEIID = 0x0040
FADF_HAVEVARTYPE = 0x0080
FADF_BSTR = 0x0100
FADF_UNKNOWN = 0x0200
FADF_DISPATCH = 0x0400
FADF_VARIANT = 0x0800
FADF_RESERVED = 0xF008
)

51
vendor/github.com/go-ole/go-ole/error.go generated vendored Normal file
View File

@ -0,0 +1,51 @@
package ole
// OleError stores COM errors.
type OleError struct {
hr uintptr
description string
subError error
}
// NewError creates new error with HResult.
func NewError(hr uintptr) *OleError {
return &OleError{hr: hr}
}
// NewErrorWithDescription creates new COM error with HResult and description.
func NewErrorWithDescription(hr uintptr, description string) *OleError {
return &OleError{hr: hr, description: description}
}
// NewErrorWithSubError creates new COM error with parent error.
func NewErrorWithSubError(hr uintptr, description string, err error) *OleError {
return &OleError{hr: hr, description: description, subError: err}
}
// Code is the HResult.
func (v *OleError) Code() uintptr {
return uintptr(v.hr)
}
// String description, either manually set or format message with error code.
func (v *OleError) String() string {
if v.description != "" {
return errstr(int(v.hr)) + " (" + v.description + ")"
}
return errstr(int(v.hr))
}
// Error implements error interface.
func (v *OleError) Error() string {
return v.String()
}
// Description retrieves error summary, if there is one.
func (v *OleError) Description() string {
return v.description
}
// SubError returns parent error, if there is one.
func (v *OleError) SubError() error {
return v.subError
}

8
vendor/github.com/go-ole/go-ole/error_func.go generated vendored Normal file
View File

@ -0,0 +1,8 @@
// +build !windows
package ole
// errstr converts error code to string.
func errstr(errno int) string {
return ""
}

24
vendor/github.com/go-ole/go-ole/error_windows.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// +build windows
package ole
import (
"fmt"
"syscall"
"unicode/utf16"
)
// errstr converts error code to string.
func errstr(errno int) string {
// ask windows for the remaining errors
var flags uint32 = syscall.FORMAT_MESSAGE_FROM_SYSTEM | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
b := make([]uint16, 300)
n, err := syscall.FormatMessage(flags, 0, uint32(errno), 0, b, nil)
if err != nil {
return fmt.Sprintf("error %d (FormatMessage failed with: %v)", errno, err)
}
// trim terminating \r and \n
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {
}
return string(utf16.Decode(b[:n]))
}

284
vendor/github.com/go-ole/go-ole/guid.go generated vendored Normal file
View File

@ -0,0 +1,284 @@
package ole
var (
// IID_NULL is null Interface ID, used when no other Interface ID is known.
IID_NULL = NewGUID("{00000000-0000-0000-0000-000000000000}")
// IID_IUnknown is for IUnknown interfaces.
IID_IUnknown = NewGUID("{00000000-0000-0000-C000-000000000046}")
// IID_IDispatch is for IDispatch interfaces.
IID_IDispatch = NewGUID("{00020400-0000-0000-C000-000000000046}")
// IID_IEnumVariant is for IEnumVariant interfaces
IID_IEnumVariant = NewGUID("{00020404-0000-0000-C000-000000000046}")
// IID_IConnectionPointContainer is for IConnectionPointContainer interfaces.
IID_IConnectionPointContainer = NewGUID("{B196B284-BAB4-101A-B69C-00AA00341D07}")
// IID_IConnectionPoint is for IConnectionPoint interfaces.
IID_IConnectionPoint = NewGUID("{B196B286-BAB4-101A-B69C-00AA00341D07}")
// IID_IInspectable is for IInspectable interfaces.
IID_IInspectable = NewGUID("{AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90}")
// IID_IProvideClassInfo is for IProvideClassInfo interfaces.
IID_IProvideClassInfo = NewGUID("{B196B283-BAB4-101A-B69C-00AA00341D07}")
)
// These are for testing and not part of any library.
var (
// IID_ICOMTestString is for ICOMTestString interfaces.
//
// {E0133EB4-C36F-469A-9D3D-C66B84BE19ED}
IID_ICOMTestString = NewGUID("{E0133EB4-C36F-469A-9D3D-C66B84BE19ED}")
// IID_ICOMTestInt8 is for ICOMTestInt8 interfaces.
//
// {BEB06610-EB84-4155-AF58-E2BFF53680B4}
IID_ICOMTestInt8 = NewGUID("{BEB06610-EB84-4155-AF58-E2BFF53680B4}")
// IID_ICOMTestInt16 is for ICOMTestInt16 interfaces.
//
// {DAA3F9FA-761E-4976-A860-8364CE55F6FC}
IID_ICOMTestInt16 = NewGUID("{DAA3F9FA-761E-4976-A860-8364CE55F6FC}")
// IID_ICOMTestInt32 is for ICOMTestInt32 interfaces.
//
// {E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}
IID_ICOMTestInt32 = NewGUID("{E3DEDEE7-38A2-4540-91D1-2EEF1D8891B0}")
// IID_ICOMTestInt64 is for ICOMTestInt64 interfaces.
//
// {8D437CBC-B3ED-485C-BC32-C336432A1623}
IID_ICOMTestInt64 = NewGUID("{8D437CBC-B3ED-485C-BC32-C336432A1623}")
// IID_ICOMTestFloat is for ICOMTestFloat interfaces.
//
// {BF1ED004-EA02-456A-AA55-2AC8AC6B054C}
IID_ICOMTestFloat = NewGUID("{BF1ED004-EA02-456A-AA55-2AC8AC6B054C}")
// IID_ICOMTestDouble is for ICOMTestDouble interfaces.
//
// {BF908A81-8687-4E93-999F-D86FAB284BA0}
IID_ICOMTestDouble = NewGUID("{BF908A81-8687-4E93-999F-D86FAB284BA0}")
// IID_ICOMTestBoolean is for ICOMTestBoolean interfaces.
//
// {D530E7A6-4EE8-40D1-8931-3D63B8605010}
IID_ICOMTestBoolean = NewGUID("{D530E7A6-4EE8-40D1-8931-3D63B8605010}")
// IID_ICOMEchoTestObject is for ICOMEchoTestObject interfaces.
//
// {6485B1EF-D780-4834-A4FE-1EBB51746CA3}
IID_ICOMEchoTestObject = NewGUID("{6485B1EF-D780-4834-A4FE-1EBB51746CA3}")
// IID_ICOMTestTypes is for ICOMTestTypes interfaces.
//
// {CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}
IID_ICOMTestTypes = NewGUID("{CCA8D7AE-91C0-4277-A8B3-FF4EDF28D3C0}")
// CLSID_COMEchoTestObject is for COMEchoTestObject class.
//
// {3C24506A-AE9E-4D50-9157-EF317281F1B0}
CLSID_COMEchoTestObject = NewGUID("{3C24506A-AE9E-4D50-9157-EF317281F1B0}")
// CLSID_COMTestScalarClass is for COMTestScalarClass class.
//
// {865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}
CLSID_COMTestScalarClass = NewGUID("{865B85C5-0334-4AC6-9EF6-AACEC8FC5E86}")
)
const hextable = "0123456789ABCDEF"
const emptyGUID = "{00000000-0000-0000-0000-000000000000}"
// GUID is Windows API specific GUID type.
//
// This exists to match Windows GUID type for direct passing for COM.
// Format is in xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx.
type GUID struct {
Data1 uint32
Data2 uint16
Data3 uint16
Data4 [8]byte
}
// NewGUID converts the given string into a globally unique identifier that is
// compliant with the Windows API.
//
// The supplied string may be in any of these formats:
//
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
//
// The conversion of the supplied string is not case-sensitive.
func NewGUID(guid string) *GUID {
d := []byte(guid)
var d1, d2, d3, d4a, d4b []byte
switch len(d) {
case 38:
if d[0] != '{' || d[37] != '}' {
return nil
}
d = d[1:37]
fallthrough
case 36:
if d[8] != '-' || d[13] != '-' || d[18] != '-' || d[23] != '-' {
return nil
}
d1 = d[0:8]
d2 = d[9:13]
d3 = d[14:18]
d4a = d[19:23]
d4b = d[24:36]
case 32:
d1 = d[0:8]
d2 = d[8:12]
d3 = d[12:16]
d4a = d[16:20]
d4b = d[20:32]
default:
return nil
}
var g GUID
var ok1, ok2, ok3, ok4 bool
g.Data1, ok1 = decodeHexUint32(d1)
g.Data2, ok2 = decodeHexUint16(d2)
g.Data3, ok3 = decodeHexUint16(d3)
g.Data4, ok4 = decodeHexByte64(d4a, d4b)
if ok1 && ok2 && ok3 && ok4 {
return &g
}
return nil
}
func decodeHexUint32(src []byte) (value uint32, ok bool) {
var b1, b2, b3, b4 byte
var ok1, ok2, ok3, ok4 bool
b1, ok1 = decodeHexByte(src[0], src[1])
b2, ok2 = decodeHexByte(src[2], src[3])
b3, ok3 = decodeHexByte(src[4], src[5])
b4, ok4 = decodeHexByte(src[6], src[7])
value = (uint32(b1) << 24) | (uint32(b2) << 16) | (uint32(b3) << 8) | uint32(b4)
ok = ok1 && ok2 && ok3 && ok4
return
}
func decodeHexUint16(src []byte) (value uint16, ok bool) {
var b1, b2 byte
var ok1, ok2 bool
b1, ok1 = decodeHexByte(src[0], src[1])
b2, ok2 = decodeHexByte(src[2], src[3])
value = (uint16(b1) << 8) | uint16(b2)
ok = ok1 && ok2
return
}
func decodeHexByte64(s1 []byte, s2 []byte) (value [8]byte, ok bool) {
var ok1, ok2, ok3, ok4, ok5, ok6, ok7, ok8 bool
value[0], ok1 = decodeHexByte(s1[0], s1[1])
value[1], ok2 = decodeHexByte(s1[2], s1[3])
value[2], ok3 = decodeHexByte(s2[0], s2[1])
value[3], ok4 = decodeHexByte(s2[2], s2[3])
value[4], ok5 = decodeHexByte(s2[4], s2[5])
value[5], ok6 = decodeHexByte(s2[6], s2[7])
value[6], ok7 = decodeHexByte(s2[8], s2[9])
value[7], ok8 = decodeHexByte(s2[10], s2[11])
ok = ok1 && ok2 && ok3 && ok4 && ok5 && ok6 && ok7 && ok8
return
}
func decodeHexByte(c1, c2 byte) (value byte, ok bool) {
var n1, n2 byte
var ok1, ok2 bool
n1, ok1 = decodeHexChar(c1)
n2, ok2 = decodeHexChar(c2)
value = (n1 << 4) | n2
ok = ok1 && ok2
return
}
func decodeHexChar(c byte) (byte, bool) {
switch {
case '0' <= c && c <= '9':
return c - '0', true
case 'a' <= c && c <= 'f':
return c - 'a' + 10, true
case 'A' <= c && c <= 'F':
return c - 'A' + 10, true
}
return 0, false
}
// String converts the GUID to string form. It will adhere to this pattern:
//
// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
//
// If the GUID is nil, the string representation of an empty GUID is returned:
//
// {00000000-0000-0000-0000-000000000000}
func (guid *GUID) String() string {
if guid == nil {
return emptyGUID
}
var c [38]byte
c[0] = '{'
putUint32Hex(c[1:9], guid.Data1)
c[9] = '-'
putUint16Hex(c[10:14], guid.Data2)
c[14] = '-'
putUint16Hex(c[15:19], guid.Data3)
c[19] = '-'
putByteHex(c[20:24], guid.Data4[0:2])
c[24] = '-'
putByteHex(c[25:37], guid.Data4[2:8])
c[37] = '}'
return string(c[:])
}
func putUint32Hex(b []byte, v uint32) {
b[0] = hextable[byte(v>>24)>>4]
b[1] = hextable[byte(v>>24)&0x0f]
b[2] = hextable[byte(v>>16)>>4]
b[3] = hextable[byte(v>>16)&0x0f]
b[4] = hextable[byte(v>>8)>>4]
b[5] = hextable[byte(v>>8)&0x0f]
b[6] = hextable[byte(v)>>4]
b[7] = hextable[byte(v)&0x0f]
}
func putUint16Hex(b []byte, v uint16) {
b[0] = hextable[byte(v>>8)>>4]
b[1] = hextable[byte(v>>8)&0x0f]
b[2] = hextable[byte(v)>>4]
b[3] = hextable[byte(v)&0x0f]
}
func putByteHex(dst, src []byte) {
for i := 0; i < len(src); i++ {
dst[i*2] = hextable[src[i]>>4]
dst[i*2+1] = hextable[src[i]&0x0f]
}
}
// IsEqualGUID compares two GUID.
//
// Not constant time comparison.
func IsEqualGUID(guid1 *GUID, guid2 *GUID) bool {
return guid1.Data1 == guid2.Data1 &&
guid1.Data2 == guid2.Data2 &&
guid1.Data3 == guid2.Data3 &&
guid1.Data4[0] == guid2.Data4[0] &&
guid1.Data4[1] == guid2.Data4[1] &&
guid1.Data4[2] == guid2.Data4[2] &&
guid1.Data4[3] == guid2.Data4[3] &&
guid1.Data4[4] == guid2.Data4[4] &&
guid1.Data4[5] == guid2.Data4[5] &&
guid1.Data4[6] == guid2.Data4[6] &&
guid1.Data4[7] == guid2.Data4[7]
}

20
vendor/github.com/go-ole/go-ole/iconnectionpoint.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
package ole
import "unsafe"
type IConnectionPoint struct {
IUnknown
}
type IConnectionPointVtbl struct {
IUnknownVtbl
GetConnectionInterface uintptr
GetConnectionPointContainer uintptr
Advise uintptr
Unadvise uintptr
EnumConnections uintptr
}
func (v *IConnectionPoint) VTable() *IConnectionPointVtbl {
return (*IConnectionPointVtbl)(unsafe.Pointer(v.RawVTable))
}

View File

@ -0,0 +1,21 @@
// +build !windows
package ole
import "unsafe"
func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
return int32(0)
}
func (v *IConnectionPoint) Advise(unknown *IUnknown) (uint32, error) {
return uint32(0), NewError(E_NOTIMPL)
}
func (v *IConnectionPoint) Unadvise(cookie uint32) error {
return NewError(E_NOTIMPL)
}
func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) (err error) {
return NewError(E_NOTIMPL)
}

View File

@ -0,0 +1,43 @@
// +build windows
package ole
import (
"syscall"
"unsafe"
)
func (v *IConnectionPoint) GetConnectionInterface(piid **GUID) int32 {
// XXX: This doesn't look like it does what it's supposed to
return release((*IUnknown)(unsafe.Pointer(v)))
}
func (v *IConnectionPoint) Advise(unknown *IUnknown) (cookie uint32, err error) {
hr, _, _ := syscall.Syscall(
v.VTable().Advise,
3,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(unknown)),
uintptr(unsafe.Pointer(&cookie)))
if hr != 0 {
err = NewError(hr)
}
return
}
func (v *IConnectionPoint) Unadvise(cookie uint32) (err error) {
hr, _, _ := syscall.Syscall(
v.VTable().Unadvise,
2,
uintptr(unsafe.Pointer(v)),
uintptr(cookie),
0)
if hr != 0 {
err = NewError(hr)
}
return
}
func (v *IConnectionPoint) EnumConnections(p *unsafe.Pointer) error {
return NewError(E_NOTIMPL)
}

View File

@ -0,0 +1,17 @@
package ole
import "unsafe"
type IConnectionPointContainer struct {
IUnknown
}
type IConnectionPointContainerVtbl struct {
IUnknownVtbl
EnumConnectionPoints uintptr
FindConnectionPoint uintptr
}
func (v *IConnectionPointContainer) VTable() *IConnectionPointContainerVtbl {
return (*IConnectionPointContainerVtbl)(unsafe.Pointer(v.RawVTable))
}

View File

@ -0,0 +1,11 @@
// +build !windows
package ole
func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error {
return NewError(E_NOTIMPL)
}
func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) error {
return NewError(E_NOTIMPL)
}

View File

@ -0,0 +1,25 @@
// +build windows
package ole
import (
"syscall"
"unsafe"
)
func (v *IConnectionPointContainer) EnumConnectionPoints(points interface{}) error {
return NewError(E_NOTIMPL)
}
func (v *IConnectionPointContainer) FindConnectionPoint(iid *GUID, point **IConnectionPoint) (err error) {
hr, _, _ := syscall.Syscall(
v.VTable().FindConnectionPoint,
3,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(point)))
if hr != 0 {
err = NewError(hr)
}
return
}

94
vendor/github.com/go-ole/go-ole/idispatch.go generated vendored Normal file
View File

@ -0,0 +1,94 @@
package ole
import "unsafe"
type IDispatch struct {
IUnknown
}
type IDispatchVtbl struct {
IUnknownVtbl
GetTypeInfoCount uintptr
GetTypeInfo uintptr
GetIDsOfNames uintptr
Invoke uintptr
}
func (v *IDispatch) VTable() *IDispatchVtbl {
return (*IDispatchVtbl)(unsafe.Pointer(v.RawVTable))
}
func (v *IDispatch) GetIDsOfName(names []string) (dispid []int32, err error) {
dispid, err = getIDsOfName(v, names)
return
}
func (v *IDispatch) Invoke(dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) {
result, err = invoke(v, dispid, dispatch, params...)
return
}
func (v *IDispatch) GetTypeInfoCount() (c uint32, err error) {
c, err = getTypeInfoCount(v)
return
}
func (v *IDispatch) GetTypeInfo() (tinfo *ITypeInfo, err error) {
tinfo, err = getTypeInfo(v)
return
}
// GetSingleIDOfName is a helper that returns single display ID for IDispatch name.
//
// This replaces the common pattern of attempting to get a single name from the list of available
// IDs. It gives the first ID, if it is available.
func (v *IDispatch) GetSingleIDOfName(name string) (displayID int32, err error) {
var displayIDs []int32
displayIDs, err = v.GetIDsOfName([]string{name})
if err != nil {
return
}
displayID = displayIDs[0]
return
}
// InvokeWithOptionalArgs accepts arguments as an array, works like Invoke.
//
// Accepts name and will attempt to retrieve Display ID to pass to Invoke.
//
// Passing params as an array is a workaround that could be fixed in later versions of Go that
// prevent passing empty params. During testing it was discovered that this is an acceptable way of
// getting around not being able to pass params normally.
func (v *IDispatch) InvokeWithOptionalArgs(name string, dispatch int16, params []interface{}) (result *VARIANT, err error) {
displayID, err := v.GetSingleIDOfName(name)
if err != nil {
return
}
if len(params) < 1 {
result, err = v.Invoke(displayID, dispatch)
} else {
result, err = v.Invoke(displayID, dispatch, params...)
}
return
}
// CallMethod invokes named function with arguments on object.
func (v *IDispatch) CallMethod(name string, params ...interface{}) (*VARIANT, error) {
return v.InvokeWithOptionalArgs(name, DISPATCH_METHOD, params)
}
// GetProperty retrieves the property with the name with the ability to pass arguments.
//
// Most of the time you will not need to pass arguments as most objects do not allow for this
// feature. Or at least, should not allow for this feature. Some servers don't follow best practices
// and this is provided for those edge cases.
func (v *IDispatch) GetProperty(name string, params ...interface{}) (*VARIANT, error) {
return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYGET, params)
}
// PutProperty attempts to mutate a property in the object.
func (v *IDispatch) PutProperty(name string, params ...interface{}) (*VARIANT, error) {
return v.InvokeWithOptionalArgs(name, DISPATCH_PROPERTYPUT, params)
}

19
vendor/github.com/go-ole/go-ole/idispatch_func.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// +build !windows
package ole
func getIDsOfName(disp *IDispatch, names []string) ([]int32, error) {
return []int32{}, NewError(E_NOTIMPL)
}
func getTypeInfoCount(disp *IDispatch) (uint32, error) {
return uint32(0), NewError(E_NOTIMPL)
}
func getTypeInfo(disp *IDispatch) (*ITypeInfo, error) {
return nil, NewError(E_NOTIMPL)
}
func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (*VARIANT, error) {
return nil, NewError(E_NOTIMPL)
}

202
vendor/github.com/go-ole/go-ole/idispatch_windows.go generated vendored Normal file
View File

@ -0,0 +1,202 @@
// +build windows
package ole
import (
"math/big"
"syscall"
"time"
"unsafe"
)
func getIDsOfName(disp *IDispatch, names []string) (dispid []int32, err error) {
wnames := make([]*uint16, len(names))
for i := 0; i < len(names); i++ {
wnames[i] = syscall.StringToUTF16Ptr(names[i])
}
dispid = make([]int32, len(names))
namelen := uint32(len(names))
hr, _, _ := syscall.Syscall6(
disp.VTable().GetIDsOfNames,
6,
uintptr(unsafe.Pointer(disp)),
uintptr(unsafe.Pointer(IID_NULL)),
uintptr(unsafe.Pointer(&wnames[0])),
uintptr(namelen),
uintptr(GetUserDefaultLCID()),
uintptr(unsafe.Pointer(&dispid[0])))
if hr != 0 {
err = NewError(hr)
}
return
}
func getTypeInfoCount(disp *IDispatch) (c uint32, err error) {
hr, _, _ := syscall.Syscall(
disp.VTable().GetTypeInfoCount,
2,
uintptr(unsafe.Pointer(disp)),
uintptr(unsafe.Pointer(&c)),
0)
if hr != 0 {
err = NewError(hr)
}
return
}
func getTypeInfo(disp *IDispatch) (tinfo *ITypeInfo, err error) {
hr, _, _ := syscall.Syscall(
disp.VTable().GetTypeInfo,
3,
uintptr(unsafe.Pointer(disp)),
uintptr(GetUserDefaultLCID()),
uintptr(unsafe.Pointer(&tinfo)))
if hr != 0 {
err = NewError(hr)
}
return
}
func invoke(disp *IDispatch, dispid int32, dispatch int16, params ...interface{}) (result *VARIANT, err error) {
var dispparams DISPPARAMS
if dispatch&DISPATCH_PROPERTYPUT != 0 {
dispnames := [1]int32{DISPID_PROPERTYPUT}
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
dispparams.cNamedArgs = 1
} else if dispatch&DISPATCH_PROPERTYPUTREF != 0 {
dispnames := [1]int32{DISPID_PROPERTYPUT}
dispparams.rgdispidNamedArgs = uintptr(unsafe.Pointer(&dispnames[0]))
dispparams.cNamedArgs = 1
}
var vargs []VARIANT
if len(params) > 0 {
vargs = make([]VARIANT, len(params))
for i, v := range params {
//n := len(params)-i-1
n := len(params) - i - 1
VariantInit(&vargs[n])
switch vv := v.(type) {
case bool:
if vv {
vargs[n] = NewVariant(VT_BOOL, 0xffff)
} else {
vargs[n] = NewVariant(VT_BOOL, 0)
}
case *bool:
vargs[n] = NewVariant(VT_BOOL|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*bool)))))
case uint8:
vargs[n] = NewVariant(VT_I1, int64(v.(uint8)))
case *uint8:
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8)))))
case int8:
vargs[n] = NewVariant(VT_I1, int64(v.(int8)))
case *int8:
vargs[n] = NewVariant(VT_I1|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint8)))))
case int16:
vargs[n] = NewVariant(VT_I2, int64(v.(int16)))
case *int16:
vargs[n] = NewVariant(VT_I2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int16)))))
case uint16:
vargs[n] = NewVariant(VT_UI2, int64(v.(uint16)))
case *uint16:
vargs[n] = NewVariant(VT_UI2|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint16)))))
case int32:
vargs[n] = NewVariant(VT_I4, int64(v.(int32)))
case *int32:
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int32)))))
case uint32:
vargs[n] = NewVariant(VT_UI4, int64(v.(uint32)))
case *uint32:
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint32)))))
case int64:
vargs[n] = NewVariant(VT_I8, int64(v.(int64)))
case *int64:
vargs[n] = NewVariant(VT_I8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int64)))))
case uint64:
vargs[n] = NewVariant(VT_UI8, int64(uintptr(v.(uint64))))
case *uint64:
vargs[n] = NewVariant(VT_UI8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint64)))))
case int:
vargs[n] = NewVariant(VT_I4, int64(v.(int)))
case *int:
vargs[n] = NewVariant(VT_I4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*int)))))
case uint:
vargs[n] = NewVariant(VT_UI4, int64(v.(uint)))
case *uint:
vargs[n] = NewVariant(VT_UI4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*uint)))))
case float32:
vargs[n] = NewVariant(VT_R4, *(*int64)(unsafe.Pointer(&vv)))
case *float32:
vargs[n] = NewVariant(VT_R4|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float32)))))
case float64:
vargs[n] = NewVariant(VT_R8, *(*int64)(unsafe.Pointer(&vv)))
case *float64:
vargs[n] = NewVariant(VT_R8|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*float64)))))
case *big.Int:
vargs[n] = NewVariant(VT_DECIMAL, v.(*big.Int).Int64())
case string:
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(v.(string))))))
case *string:
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*string)))))
case time.Time:
s := vv.Format("2006-01-02 15:04:05")
vargs[n] = NewVariant(VT_BSTR, int64(uintptr(unsafe.Pointer(SysAllocStringLen(s)))))
case *time.Time:
s := vv.Format("2006-01-02 15:04:05")
vargs[n] = NewVariant(VT_BSTR|VT_BYREF, int64(uintptr(unsafe.Pointer(&s))))
case *IDispatch:
vargs[n] = NewVariant(VT_DISPATCH, int64(uintptr(unsafe.Pointer(v.(*IDispatch)))))
case **IDispatch:
vargs[n] = NewVariant(VT_DISPATCH|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(**IDispatch)))))
case nil:
vargs[n] = NewVariant(VT_NULL, 0)
case *VARIANT:
vargs[n] = NewVariant(VT_VARIANT|VT_BYREF, int64(uintptr(unsafe.Pointer(v.(*VARIANT)))))
case []byte:
safeByteArray := safeArrayFromByteSlice(v.([]byte))
vargs[n] = NewVariant(VT_ARRAY|VT_UI1, int64(uintptr(unsafe.Pointer(safeByteArray))))
defer VariantClear(&vargs[n])
case []string:
safeByteArray := safeArrayFromStringSlice(v.([]string))
vargs[n] = NewVariant(VT_ARRAY|VT_BSTR, int64(uintptr(unsafe.Pointer(safeByteArray))))
defer VariantClear(&vargs[n])
default:
panic("unknown type")
}
}
dispparams.rgvarg = uintptr(unsafe.Pointer(&vargs[0]))
dispparams.cArgs = uint32(len(params))
}
result = new(VARIANT)
var excepInfo EXCEPINFO
VariantInit(result)
hr, _, _ := syscall.Syscall9(
disp.VTable().Invoke,
9,
uintptr(unsafe.Pointer(disp)),
uintptr(dispid),
uintptr(unsafe.Pointer(IID_NULL)),
uintptr(GetUserDefaultLCID()),
uintptr(dispatch),
uintptr(unsafe.Pointer(&dispparams)),
uintptr(unsafe.Pointer(result)),
uintptr(unsafe.Pointer(&excepInfo)),
0)
if hr != 0 {
excepInfo.renderStrings()
excepInfo.Clear()
err = NewErrorWithSubError(hr, excepInfo.description, excepInfo)
}
for i, varg := range vargs {
n := len(params) - i - 1
if varg.VT == VT_BSTR && varg.Val != 0 {
SysFreeString(((*int16)(unsafe.Pointer(uintptr(varg.Val)))))
}
if varg.VT == (VT_BSTR|VT_BYREF) && varg.Val != 0 {
*(params[n].(*string)) = LpOleStrToString(*(**uint16)(unsafe.Pointer(uintptr(varg.Val))))
}
}
return
}

19
vendor/github.com/go-ole/go-ole/ienumvariant.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
package ole
import "unsafe"
type IEnumVARIANT struct {
IUnknown
}
type IEnumVARIANTVtbl struct {
IUnknownVtbl
Next uintptr
Skip uintptr
Reset uintptr
Clone uintptr
}
func (v *IEnumVARIANT) VTable() *IEnumVARIANTVtbl {
return (*IEnumVARIANTVtbl)(unsafe.Pointer(v.RawVTable))
}

19
vendor/github.com/go-ole/go-ole/ienumvariant_func.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// +build !windows
package ole
func (enum *IEnumVARIANT) Clone() (*IEnumVARIANT, error) {
return nil, NewError(E_NOTIMPL)
}
func (enum *IEnumVARIANT) Reset() error {
return NewError(E_NOTIMPL)
}
func (enum *IEnumVARIANT) Skip(celt uint) error {
return NewError(E_NOTIMPL)
}
func (enum *IEnumVARIANT) Next(celt uint) (VARIANT, uint, error) {
return NewVariant(VT_NULL, int64(0)), 0, NewError(E_NOTIMPL)
}

View File

@ -0,0 +1,63 @@
// +build windows
package ole
import (
"syscall"
"unsafe"
)
func (enum *IEnumVARIANT) Clone() (cloned *IEnumVARIANT, err error) {
hr, _, _ := syscall.Syscall(
enum.VTable().Clone,
2,
uintptr(unsafe.Pointer(enum)),
uintptr(unsafe.Pointer(&cloned)),
0)
if hr != 0 {
err = NewError(hr)
}
return
}
func (enum *IEnumVARIANT) Reset() (err error) {
hr, _, _ := syscall.Syscall(
enum.VTable().Reset,
1,
uintptr(unsafe.Pointer(enum)),
0,
0)
if hr != 0 {
err = NewError(hr)
}
return
}
func (enum *IEnumVARIANT) Skip(celt uint) (err error) {
hr, _, _ := syscall.Syscall(
enum.VTable().Skip,
2,
uintptr(unsafe.Pointer(enum)),
uintptr(celt),
0)
if hr != 0 {
err = NewError(hr)
}
return
}
func (enum *IEnumVARIANT) Next(celt uint) (array VARIANT, length uint, err error) {
hr, _, _ := syscall.Syscall6(
enum.VTable().Next,
4,
uintptr(unsafe.Pointer(enum)),
uintptr(celt),
uintptr(unsafe.Pointer(&array)),
uintptr(unsafe.Pointer(&length)),
0,
0)
if hr != 0 {
err = NewError(hr)
}
return
}

18
vendor/github.com/go-ole/go-ole/iinspectable.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
package ole
import "unsafe"
type IInspectable struct {
IUnknown
}
type IInspectableVtbl struct {
IUnknownVtbl
GetIIds uintptr
GetRuntimeClassName uintptr
GetTrustLevel uintptr
}
func (v *IInspectable) VTable() *IInspectableVtbl {
return (*IInspectableVtbl)(unsafe.Pointer(v.RawVTable))
}

15
vendor/github.com/go-ole/go-ole/iinspectable_func.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
// +build !windows
package ole
func (v *IInspectable) GetIids() ([]*GUID, error) {
return []*GUID{}, NewError(E_NOTIMPL)
}
func (v *IInspectable) GetRuntimeClassName() (string, error) {
return "", NewError(E_NOTIMPL)
}
func (v *IInspectable) GetTrustLevel() (uint32, error) {
return uint32(0), NewError(E_NOTIMPL)
}

View File

@ -0,0 +1,72 @@
// +build windows
package ole
import (
"bytes"
"encoding/binary"
"reflect"
"syscall"
"unsafe"
)
func (v *IInspectable) GetIids() (iids []*GUID, err error) {
var count uint32
var array uintptr
hr, _, _ := syscall.Syscall(
v.VTable().GetIIds,
3,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(&count)),
uintptr(unsafe.Pointer(&array)))
if hr != 0 {
err = NewError(hr)
return
}
defer CoTaskMemFree(array)
iids = make([]*GUID, count)
byteCount := count * uint32(unsafe.Sizeof(GUID{}))
slicehdr := reflect.SliceHeader{Data: array, Len: int(byteCount), Cap: int(byteCount)}
byteSlice := *(*[]byte)(unsafe.Pointer(&slicehdr))
reader := bytes.NewReader(byteSlice)
for i := range iids {
guid := GUID{}
err = binary.Read(reader, binary.LittleEndian, &guid)
if err != nil {
return
}
iids[i] = &guid
}
return
}
func (v *IInspectable) GetRuntimeClassName() (s string, err error) {
var hstring HString
hr, _, _ := syscall.Syscall(
v.VTable().GetRuntimeClassName,
2,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(&hstring)),
0)
if hr != 0 {
err = NewError(hr)
return
}
s = hstring.String()
DeleteHString(hstring)
return
}
func (v *IInspectable) GetTrustLevel() (level uint32, err error) {
hr, _, _ := syscall.Syscall(
v.VTable().GetTrustLevel,
2,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(&level)),
0)
if hr != 0 {
err = NewError(hr)
}
return
}

21
vendor/github.com/go-ole/go-ole/iprovideclassinfo.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
package ole
import "unsafe"
type IProvideClassInfo struct {
IUnknown
}
type IProvideClassInfoVtbl struct {
IUnknownVtbl
GetClassInfo uintptr
}
func (v *IProvideClassInfo) VTable() *IProvideClassInfoVtbl {
return (*IProvideClassInfoVtbl)(unsafe.Pointer(v.RawVTable))
}
func (v *IProvideClassInfo) GetClassInfo() (cinfo *ITypeInfo, err error) {
cinfo, err = getClassInfo(v)
return
}

View File

@ -0,0 +1,7 @@
// +build !windows
package ole
func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) {
return nil, NewError(E_NOTIMPL)
}

View File

@ -0,0 +1,21 @@
// +build windows
package ole
import (
"syscall"
"unsafe"
)
func getClassInfo(disp *IProvideClassInfo) (tinfo *ITypeInfo, err error) {
hr, _, _ := syscall.Syscall(
disp.VTable().GetClassInfo,
2,
uintptr(unsafe.Pointer(disp)),
uintptr(unsafe.Pointer(&tinfo)),
0)
if hr != 0 {
err = NewError(hr)
}
return
}

34
vendor/github.com/go-ole/go-ole/itypeinfo.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
package ole
import "unsafe"
type ITypeInfo struct {
IUnknown
}
type ITypeInfoVtbl struct {
IUnknownVtbl
GetTypeAttr uintptr
GetTypeComp uintptr
GetFuncDesc uintptr
GetVarDesc uintptr
GetNames uintptr
GetRefTypeOfImplType uintptr
GetImplTypeFlags uintptr
GetIDsOfNames uintptr
Invoke uintptr
GetDocumentation uintptr
GetDllEntry uintptr
GetRefTypeInfo uintptr
AddressOfMember uintptr
CreateInstance uintptr
GetMops uintptr
GetContainingTypeLib uintptr
ReleaseTypeAttr uintptr
ReleaseFuncDesc uintptr
ReleaseVarDesc uintptr
}
func (v *ITypeInfo) VTable() *ITypeInfoVtbl {
return (*ITypeInfoVtbl)(unsafe.Pointer(v.RawVTable))
}

7
vendor/github.com/go-ole/go-ole/itypeinfo_func.go generated vendored Normal file
View File

@ -0,0 +1,7 @@
// +build !windows
package ole
func (v *ITypeInfo) GetTypeAttr() (*TYPEATTR, error) {
return nil, NewError(E_NOTIMPL)
}

21
vendor/github.com/go-ole/go-ole/itypeinfo_windows.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
// +build windows
package ole
import (
"syscall"
"unsafe"
)
func (v *ITypeInfo) GetTypeAttr() (tattr *TYPEATTR, err error) {
hr, _, _ := syscall.Syscall(
uintptr(v.VTable().GetTypeAttr),
2,
uintptr(unsafe.Pointer(v)),
uintptr(unsafe.Pointer(&tattr)),
0)
if hr != 0 {
err = NewError(hr)
}
return
}

57
vendor/github.com/go-ole/go-ole/iunknown.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package ole
import "unsafe"
type IUnknown struct {
RawVTable *interface{}
}
type IUnknownVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
}
type UnknownLike interface {
QueryInterface(iid *GUID) (disp *IDispatch, err error)
AddRef() int32
Release() int32
}
func (v *IUnknown) VTable() *IUnknownVtbl {
return (*IUnknownVtbl)(unsafe.Pointer(v.RawVTable))
}
func (v *IUnknown) PutQueryInterface(interfaceID *GUID, obj interface{}) error {
return reflectQueryInterface(v, v.VTable().QueryInterface, interfaceID, obj)
}
func (v *IUnknown) IDispatch(interfaceID *GUID) (dispatch *IDispatch, err error) {
err = v.PutQueryInterface(interfaceID, &dispatch)
return
}
func (v *IUnknown) IEnumVARIANT(interfaceID *GUID) (enum *IEnumVARIANT, err error) {
err = v.PutQueryInterface(interfaceID, &enum)
return
}
func (v *IUnknown) QueryInterface(iid *GUID) (*IDispatch, error) {
return queryInterface(v, iid)
}
func (v *IUnknown) MustQueryInterface(iid *GUID) (disp *IDispatch) {
unk, err := queryInterface(v, iid)
if err != nil {
panic(err)
}
return unk
}
func (v *IUnknown) AddRef() int32 {
return addRef(v)
}
func (v *IUnknown) Release() int32 {
return release(v)
}

19
vendor/github.com/go-ole/go-ole/iunknown_func.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// +build !windows
package ole
func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) {
return NewError(E_NOTIMPL)
}
func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) {
return nil, NewError(E_NOTIMPL)
}
func addRef(unk *IUnknown) int32 {
return 0
}
func release(unk *IUnknown) int32 {
return 0
}

58
vendor/github.com/go-ole/go-ole/iunknown_windows.go generated vendored Normal file
View File

@ -0,0 +1,58 @@
// +build windows
package ole
import (
"reflect"
"syscall"
"unsafe"
)
func reflectQueryInterface(self interface{}, method uintptr, interfaceID *GUID, obj interface{}) (err error) {
selfValue := reflect.ValueOf(self).Elem()
objValue := reflect.ValueOf(obj).Elem()
hr, _, _ := syscall.Syscall(
method,
3,
selfValue.UnsafeAddr(),
uintptr(unsafe.Pointer(interfaceID)),
objValue.Addr().Pointer())
if hr != 0 {
err = NewError(hr)
}
return
}
func queryInterface(unk *IUnknown, iid *GUID) (disp *IDispatch, err error) {
hr, _, _ := syscall.Syscall(
unk.VTable().QueryInterface,
3,
uintptr(unsafe.Pointer(unk)),
uintptr(unsafe.Pointer(iid)),
uintptr(unsafe.Pointer(&disp)))
if hr != 0 {
err = NewError(hr)
}
return
}
func addRef(unk *IUnknown) int32 {
ret, _, _ := syscall.Syscall(
unk.VTable().AddRef,
1,
uintptr(unsafe.Pointer(unk)),
0,
0)
return int32(ret)
}
func release(unk *IUnknown) int32 {
ret, _, _ := syscall.Syscall(
unk.VTable().Release,
1,
uintptr(unsafe.Pointer(unk)),
0,
0)
return int32(ret)
}

190
vendor/github.com/go-ole/go-ole/ole.go generated vendored Normal file
View File

@ -0,0 +1,190 @@
package ole
import (
"fmt"
"strings"
"unsafe"
)
// DISPPARAMS are the arguments that passed to methods or property.
type DISPPARAMS struct {
rgvarg uintptr
rgdispidNamedArgs uintptr
cArgs uint32
cNamedArgs uint32
}
// EXCEPINFO defines exception info.
type EXCEPINFO struct {
wCode uint16
wReserved uint16
bstrSource *uint16
bstrDescription *uint16
bstrHelpFile *uint16
dwHelpContext uint32
pvReserved uintptr
pfnDeferredFillIn uintptr
scode uint32
// Go-specific part. Don't move upper cos it'll break structure layout for native code.
rendered bool
source string
description string
helpFile string
}
// renderStrings translates BSTR strings to Go ones so `.Error` and `.String`
// could be safely called after `.Clear`. We need this when we can't rely on
// a caller to call `.Clear`.
func (e *EXCEPINFO) renderStrings() {
e.rendered = true
if e.bstrSource == nil {
e.source = "<nil>"
} else {
e.source = BstrToString(e.bstrSource)
}
if e.bstrDescription == nil {
e.description = "<nil>"
} else {
e.description = BstrToString(e.bstrDescription)
}
if e.bstrHelpFile == nil {
e.helpFile = "<nil>"
} else {
e.helpFile = BstrToString(e.bstrHelpFile)
}
}
// Clear frees BSTR strings inside an EXCEPINFO and set it to NULL.
func (e *EXCEPINFO) Clear() {
freeBSTR := func(s *uint16) {
// SysFreeString don't return errors and is safe for call's on NULL.
// https://docs.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysfreestring
_ = SysFreeString((*int16)(unsafe.Pointer(s)))
}
if e.bstrSource != nil {
freeBSTR(e.bstrSource)
e.bstrSource = nil
}
if e.bstrDescription != nil {
freeBSTR(e.bstrDescription)
e.bstrDescription = nil
}
if e.bstrHelpFile != nil {
freeBSTR(e.bstrHelpFile)
e.bstrHelpFile = nil
}
}
// WCode return wCode in EXCEPINFO.
func (e EXCEPINFO) WCode() uint16 {
return e.wCode
}
// SCODE return scode in EXCEPINFO.
func (e EXCEPINFO) SCODE() uint32 {
return e.scode
}
// String convert EXCEPINFO to string.
func (e EXCEPINFO) String() string {
if !e.rendered {
e.renderStrings()
}
return fmt.Sprintf(
"wCode: %#x, bstrSource: %v, bstrDescription: %v, bstrHelpFile: %v, dwHelpContext: %#x, scode: %#x",
e.wCode, e.source, e.description, e.helpFile, e.dwHelpContext, e.scode,
)
}
// Error implements error interface and returns error string.
func (e EXCEPINFO) Error() string {
if !e.rendered {
e.renderStrings()
}
if e.description != "<nil>" {
return strings.TrimSpace(e.description)
}
code := e.scode
if e.wCode != 0 {
code = uint32(e.wCode)
}
return fmt.Sprintf("%v: %#x", e.source, code)
}
// PARAMDATA defines parameter data type.
type PARAMDATA struct {
Name *int16
Vt uint16
}
// METHODDATA defines method info.
type METHODDATA struct {
Name *uint16
Data *PARAMDATA
Dispid int32
Meth uint32
CC int32
CArgs uint32
Flags uint16
VtReturn uint32
}
// INTERFACEDATA defines interface info.
type INTERFACEDATA struct {
MethodData *METHODDATA
CMembers uint32
}
// Point is 2D vector type.
type Point struct {
X int32
Y int32
}
// Msg is message between processes.
type Msg struct {
Hwnd uint32
Message uint32
Wparam int32
Lparam int32
Time uint32
Pt Point
}
// TYPEDESC defines data type.
type TYPEDESC struct {
Hreftype uint32
VT uint16
}
// IDLDESC defines IDL info.
type IDLDESC struct {
DwReserved uint32
WIDLFlags uint16
}
// TYPEATTR defines type info.
type TYPEATTR struct {
Guid GUID
Lcid uint32
dwReserved uint32
MemidConstructor int32
MemidDestructor int32
LpstrSchema *uint16
CbSizeInstance uint32
Typekind int32
CFuncs uint16
CVars uint16
CImplTypes uint16
CbSizeVft uint16
CbAlignment uint16
WTypeFlags uint16
WMajorVerNum uint16
WMinorVerNum uint16
TdescAlias TYPEDESC
IdldescType IDLDESC
}

27
vendor/github.com/go-ole/go-ole/safearray.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
// Package is meant to retrieve and process safe array data returned from COM.
package ole
// SafeArrayBound defines the SafeArray boundaries.
type SafeArrayBound struct {
Elements uint32
LowerBound int32
}
// SafeArray is how COM handles arrays.
type SafeArray struct {
Dimensions uint16
FeaturesFlag uint16
ElementsSize uint32
LocksAmount uint32
Data uint32
Bounds [16]byte
}
// SAFEARRAY is obsolete, exists for backwards compatibility.
// Use SafeArray
type SAFEARRAY SafeArray
// SAFEARRAYBOUND is obsolete, exists for backwards compatibility.
// Use SafeArrayBound
type SAFEARRAYBOUND SafeArrayBound

211
vendor/github.com/go-ole/go-ole/safearray_func.go generated vendored Normal file
View File

@ -0,0 +1,211 @@
// +build !windows
package ole
import (
"unsafe"
)
// safeArrayAccessData returns raw array pointer.
//
// AKA: SafeArrayAccessData in Windows API.
func safeArrayAccessData(safearray *SafeArray) (uintptr, error) {
return uintptr(0), NewError(E_NOTIMPL)
}
// safeArrayUnaccessData releases raw array.
//
// AKA: SafeArrayUnaccessData in Windows API.
func safeArrayUnaccessData(safearray *SafeArray) error {
return NewError(E_NOTIMPL)
}
// safeArrayAllocData allocates SafeArray.
//
// AKA: SafeArrayAllocData in Windows API.
func safeArrayAllocData(safearray *SafeArray) error {
return NewError(E_NOTIMPL)
}
// safeArrayAllocDescriptor allocates SafeArray.
//
// AKA: SafeArrayAllocDescriptor in Windows API.
func safeArrayAllocDescriptor(dimensions uint32) (*SafeArray, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArrayAllocDescriptorEx allocates SafeArray.
//
// AKA: SafeArrayAllocDescriptorEx in Windows API.
func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (*SafeArray, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArrayCopy returns copy of SafeArray.
//
// AKA: SafeArrayCopy in Windows API.
func safeArrayCopy(original *SafeArray) (*SafeArray, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArrayCopyData duplicates SafeArray into another SafeArray object.
//
// AKA: SafeArrayCopyData in Windows API.
func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) error {
return NewError(E_NOTIMPL)
}
// safeArrayCreate creates SafeArray.
//
// AKA: SafeArrayCreate in Windows API.
func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (*SafeArray, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArrayCreateEx creates SafeArray.
//
// AKA: SafeArrayCreateEx in Windows API.
func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (*SafeArray, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArrayCreateVector creates SafeArray.
//
// AKA: SafeArrayCreateVector in Windows API.
func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (*SafeArray, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArrayCreateVectorEx creates SafeArray.
//
// AKA: SafeArrayCreateVectorEx in Windows API.
func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (*SafeArray, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArrayDestroy destroys SafeArray object.
//
// AKA: SafeArrayDestroy in Windows API.
func safeArrayDestroy(safearray *SafeArray) error {
return NewError(E_NOTIMPL)
}
// safeArrayDestroyData destroys SafeArray object.
//
// AKA: SafeArrayDestroyData in Windows API.
func safeArrayDestroyData(safearray *SafeArray) error {
return NewError(E_NOTIMPL)
}
// safeArrayDestroyDescriptor destroys SafeArray object.
//
// AKA: SafeArrayDestroyDescriptor in Windows API.
func safeArrayDestroyDescriptor(safearray *SafeArray) error {
return NewError(E_NOTIMPL)
}
// safeArrayGetDim is the amount of dimensions in the SafeArray.
//
// SafeArrays may have multiple dimensions. Meaning, it could be
// multidimensional array.
//
// AKA: SafeArrayGetDim in Windows API.
func safeArrayGetDim(safearray *SafeArray) (*uint32, error) {
u := uint32(0)
return &u, NewError(E_NOTIMPL)
}
// safeArrayGetElementSize is the element size in bytes.
//
// AKA: SafeArrayGetElemsize in Windows API.
func safeArrayGetElementSize(safearray *SafeArray) (*uint32, error) {
u := uint32(0)
return &u, NewError(E_NOTIMPL)
}
// safeArrayGetElement retrieves element at given index.
func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error {
return NewError(E_NOTIMPL)
}
// safeArrayGetElement retrieves element at given index and converts to string.
func safeArrayGetElementString(safearray *SafeArray, index int32) (string, error) {
return "", NewError(E_NOTIMPL)
}
// safeArrayGetIID is the InterfaceID of the elements in the SafeArray.
//
// AKA: SafeArrayGetIID in Windows API.
func safeArrayGetIID(safearray *SafeArray) (*GUID, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArrayGetLBound returns lower bounds of SafeArray.
//
// SafeArrays may have multiple dimensions. Meaning, it could be
// multidimensional array.
//
// AKA: SafeArrayGetLBound in Windows API.
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (int32, error) {
return int32(0), NewError(E_NOTIMPL)
}
// safeArrayGetUBound returns upper bounds of SafeArray.
//
// SafeArrays may have multiple dimensions. Meaning, it could be
// multidimensional array.
//
// AKA: SafeArrayGetUBound in Windows API.
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (int32, error) {
return int32(0), NewError(E_NOTIMPL)
}
// safeArrayGetVartype returns data type of SafeArray.
//
// AKA: SafeArrayGetVartype in Windows API.
func safeArrayGetVartype(safearray *SafeArray) (uint16, error) {
return uint16(0), NewError(E_NOTIMPL)
}
// safeArrayLock locks SafeArray for reading to modify SafeArray.
//
// This must be called during some calls to ensure that another process does not
// read or write to the SafeArray during editing.
//
// AKA: SafeArrayLock in Windows API.
func safeArrayLock(safearray *SafeArray) error {
return NewError(E_NOTIMPL)
}
// safeArrayUnlock unlocks SafeArray for reading.
//
// AKA: SafeArrayUnlock in Windows API.
func safeArrayUnlock(safearray *SafeArray) error {
return NewError(E_NOTIMPL)
}
// safeArrayPutElement stores the data element at the specified location in the
// array.
//
// AKA: SafeArrayPutElement in Windows API.
func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) error {
return NewError(E_NOTIMPL)
}
// safeArrayGetRecordInfo accesses IRecordInfo info for custom types.
//
// AKA: SafeArrayGetRecordInfo in Windows API.
//
// XXX: Must implement IRecordInfo interface for this to return.
func safeArrayGetRecordInfo(safearray *SafeArray) (interface{}, error) {
return nil, NewError(E_NOTIMPL)
}
// safeArraySetRecordInfo mutates IRecordInfo info for custom types.
//
// AKA: SafeArraySetRecordInfo in Windows API.
//
// XXX: Must implement IRecordInfo interface for this to return.
func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) error {
return NewError(E_NOTIMPL)
}

337
vendor/github.com/go-ole/go-ole/safearray_windows.go generated vendored Normal file
View File

@ -0,0 +1,337 @@
// +build windows
package ole
import (
"unsafe"
)
var (
procSafeArrayAccessData = modoleaut32.NewProc("SafeArrayAccessData")
procSafeArrayAllocData = modoleaut32.NewProc("SafeArrayAllocData")
procSafeArrayAllocDescriptor = modoleaut32.NewProc("SafeArrayAllocDescriptor")
procSafeArrayAllocDescriptorEx = modoleaut32.NewProc("SafeArrayAllocDescriptorEx")
procSafeArrayCopy = modoleaut32.NewProc("SafeArrayCopy")
procSafeArrayCopyData = modoleaut32.NewProc("SafeArrayCopyData")
procSafeArrayCreate = modoleaut32.NewProc("SafeArrayCreate")
procSafeArrayCreateEx = modoleaut32.NewProc("SafeArrayCreateEx")
procSafeArrayCreateVector = modoleaut32.NewProc("SafeArrayCreateVector")
procSafeArrayCreateVectorEx = modoleaut32.NewProc("SafeArrayCreateVectorEx")
procSafeArrayDestroy = modoleaut32.NewProc("SafeArrayDestroy")
procSafeArrayDestroyData = modoleaut32.NewProc("SafeArrayDestroyData")
procSafeArrayDestroyDescriptor = modoleaut32.NewProc("SafeArrayDestroyDescriptor")
procSafeArrayGetDim = modoleaut32.NewProc("SafeArrayGetDim")
procSafeArrayGetElement = modoleaut32.NewProc("SafeArrayGetElement")
procSafeArrayGetElemsize = modoleaut32.NewProc("SafeArrayGetElemsize")
procSafeArrayGetIID = modoleaut32.NewProc("SafeArrayGetIID")
procSafeArrayGetLBound = modoleaut32.NewProc("SafeArrayGetLBound")
procSafeArrayGetUBound = modoleaut32.NewProc("SafeArrayGetUBound")
procSafeArrayGetVartype = modoleaut32.NewProc("SafeArrayGetVartype")
procSafeArrayLock = modoleaut32.NewProc("SafeArrayLock")
procSafeArrayPtrOfIndex = modoleaut32.NewProc("SafeArrayPtrOfIndex")
procSafeArrayUnaccessData = modoleaut32.NewProc("SafeArrayUnaccessData")
procSafeArrayUnlock = modoleaut32.NewProc("SafeArrayUnlock")
procSafeArrayPutElement = modoleaut32.NewProc("SafeArrayPutElement")
//procSafeArrayRedim = modoleaut32.NewProc("SafeArrayRedim") // TODO
//procSafeArraySetIID = modoleaut32.NewProc("SafeArraySetIID") // TODO
procSafeArrayGetRecordInfo = modoleaut32.NewProc("SafeArrayGetRecordInfo")
procSafeArraySetRecordInfo = modoleaut32.NewProc("SafeArraySetRecordInfo")
)
// safeArrayAccessData returns raw array pointer.
//
// AKA: SafeArrayAccessData in Windows API.
// Todo: Test
func safeArrayAccessData(safearray *SafeArray) (element uintptr, err error) {
err = convertHresultToError(
procSafeArrayAccessData.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&element))))
return
}
// safeArrayUnaccessData releases raw array.
//
// AKA: SafeArrayUnaccessData in Windows API.
func safeArrayUnaccessData(safearray *SafeArray) (err error) {
err = convertHresultToError(procSafeArrayUnaccessData.Call(uintptr(unsafe.Pointer(safearray))))
return
}
// safeArrayAllocData allocates SafeArray.
//
// AKA: SafeArrayAllocData in Windows API.
func safeArrayAllocData(safearray *SafeArray) (err error) {
err = convertHresultToError(procSafeArrayAllocData.Call(uintptr(unsafe.Pointer(safearray))))
return
}
// safeArrayAllocDescriptor allocates SafeArray.
//
// AKA: SafeArrayAllocDescriptor in Windows API.
func safeArrayAllocDescriptor(dimensions uint32) (safearray *SafeArray, err error) {
err = convertHresultToError(
procSafeArrayAllocDescriptor.Call(uintptr(dimensions), uintptr(unsafe.Pointer(&safearray))))
return
}
// safeArrayAllocDescriptorEx allocates SafeArray.
//
// AKA: SafeArrayAllocDescriptorEx in Windows API.
func safeArrayAllocDescriptorEx(variantType VT, dimensions uint32) (safearray *SafeArray, err error) {
err = convertHresultToError(
procSafeArrayAllocDescriptorEx.Call(
uintptr(variantType),
uintptr(dimensions),
uintptr(unsafe.Pointer(&safearray))))
return
}
// safeArrayCopy returns copy of SafeArray.
//
// AKA: SafeArrayCopy in Windows API.
func safeArrayCopy(original *SafeArray) (safearray *SafeArray, err error) {
err = convertHresultToError(
procSafeArrayCopy.Call(
uintptr(unsafe.Pointer(original)),
uintptr(unsafe.Pointer(&safearray))))
return
}
// safeArrayCopyData duplicates SafeArray into another SafeArray object.
//
// AKA: SafeArrayCopyData in Windows API.
func safeArrayCopyData(original *SafeArray, duplicate *SafeArray) (err error) {
err = convertHresultToError(
procSafeArrayCopyData.Call(
uintptr(unsafe.Pointer(original)),
uintptr(unsafe.Pointer(duplicate))))
return
}
// safeArrayCreate creates SafeArray.
//
// AKA: SafeArrayCreate in Windows API.
func safeArrayCreate(variantType VT, dimensions uint32, bounds *SafeArrayBound) (safearray *SafeArray, err error) {
sa, _, err := procSafeArrayCreate.Call(
uintptr(variantType),
uintptr(dimensions),
uintptr(unsafe.Pointer(bounds)))
safearray = (*SafeArray)(unsafe.Pointer(&sa))
return
}
// safeArrayCreateEx creates SafeArray.
//
// AKA: SafeArrayCreateEx in Windows API.
func safeArrayCreateEx(variantType VT, dimensions uint32, bounds *SafeArrayBound, extra uintptr) (safearray *SafeArray, err error) {
sa, _, err := procSafeArrayCreateEx.Call(
uintptr(variantType),
uintptr(dimensions),
uintptr(unsafe.Pointer(bounds)),
extra)
safearray = (*SafeArray)(unsafe.Pointer(sa))
return
}
// safeArrayCreateVector creates SafeArray.
//
// AKA: SafeArrayCreateVector in Windows API.
func safeArrayCreateVector(variantType VT, lowerBound int32, length uint32) (safearray *SafeArray, err error) {
sa, _, err := procSafeArrayCreateVector.Call(
uintptr(variantType),
uintptr(lowerBound),
uintptr(length))
safearray = (*SafeArray)(unsafe.Pointer(sa))
return
}
// safeArrayCreateVectorEx creates SafeArray.
//
// AKA: SafeArrayCreateVectorEx in Windows API.
func safeArrayCreateVectorEx(variantType VT, lowerBound int32, length uint32, extra uintptr) (safearray *SafeArray, err error) {
sa, _, err := procSafeArrayCreateVectorEx.Call(
uintptr(variantType),
uintptr(lowerBound),
uintptr(length),
extra)
safearray = (*SafeArray)(unsafe.Pointer(sa))
return
}
// safeArrayDestroy destroys SafeArray object.
//
// AKA: SafeArrayDestroy in Windows API.
func safeArrayDestroy(safearray *SafeArray) (err error) {
err = convertHresultToError(procSafeArrayDestroy.Call(uintptr(unsafe.Pointer(safearray))))
return
}
// safeArrayDestroyData destroys SafeArray object.
//
// AKA: SafeArrayDestroyData in Windows API.
func safeArrayDestroyData(safearray *SafeArray) (err error) {
err = convertHresultToError(procSafeArrayDestroyData.Call(uintptr(unsafe.Pointer(safearray))))
return
}
// safeArrayDestroyDescriptor destroys SafeArray object.
//
// AKA: SafeArrayDestroyDescriptor in Windows API.
func safeArrayDestroyDescriptor(safearray *SafeArray) (err error) {
err = convertHresultToError(procSafeArrayDestroyDescriptor.Call(uintptr(unsafe.Pointer(safearray))))
return
}
// safeArrayGetDim is the amount of dimensions in the SafeArray.
//
// SafeArrays may have multiple dimensions. Meaning, it could be
// multidimensional array.
//
// AKA: SafeArrayGetDim in Windows API.
func safeArrayGetDim(safearray *SafeArray) (dimensions *uint32, err error) {
l, _, err := procSafeArrayGetDim.Call(uintptr(unsafe.Pointer(safearray)))
dimensions = (*uint32)(unsafe.Pointer(l))
return
}
// safeArrayGetElementSize is the element size in bytes.
//
// AKA: SafeArrayGetElemsize in Windows API.
func safeArrayGetElementSize(safearray *SafeArray) (length *uint32, err error) {
l, _, err := procSafeArrayGetElemsize.Call(uintptr(unsafe.Pointer(safearray)))
length = (*uint32)(unsafe.Pointer(l))
return
}
// safeArrayGetElement retrieves element at given index.
func safeArrayGetElement(safearray *SafeArray, index int32, pv unsafe.Pointer) error {
return convertHresultToError(
procSafeArrayGetElement.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&index)),
uintptr(pv)))
}
// safeArrayGetElementString retrieves element at given index and converts to string.
func safeArrayGetElementString(safearray *SafeArray, index int32) (str string, err error) {
var element *int16
err = convertHresultToError(
procSafeArrayGetElement.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&index)),
uintptr(unsafe.Pointer(&element))))
str = BstrToString(*(**uint16)(unsafe.Pointer(&element)))
SysFreeString(element)
return
}
// safeArrayGetIID is the InterfaceID of the elements in the SafeArray.
//
// AKA: SafeArrayGetIID in Windows API.
func safeArrayGetIID(safearray *SafeArray) (guid *GUID, err error) {
err = convertHresultToError(
procSafeArrayGetIID.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&guid))))
return
}
// safeArrayGetLBound returns lower bounds of SafeArray.
//
// SafeArrays may have multiple dimensions. Meaning, it could be
// multidimensional array.
//
// AKA: SafeArrayGetLBound in Windows API.
func safeArrayGetLBound(safearray *SafeArray, dimension uint32) (lowerBound int32, err error) {
err = convertHresultToError(
procSafeArrayGetLBound.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(dimension),
uintptr(unsafe.Pointer(&lowerBound))))
return
}
// safeArrayGetUBound returns upper bounds of SafeArray.
//
// SafeArrays may have multiple dimensions. Meaning, it could be
// multidimensional array.
//
// AKA: SafeArrayGetUBound in Windows API.
func safeArrayGetUBound(safearray *SafeArray, dimension uint32) (upperBound int32, err error) {
err = convertHresultToError(
procSafeArrayGetUBound.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(dimension),
uintptr(unsafe.Pointer(&upperBound))))
return
}
// safeArrayGetVartype returns data type of SafeArray.
//
// AKA: SafeArrayGetVartype in Windows API.
func safeArrayGetVartype(safearray *SafeArray) (varType uint16, err error) {
err = convertHresultToError(
procSafeArrayGetVartype.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&varType))))
return
}
// safeArrayLock locks SafeArray for reading to modify SafeArray.
//
// This must be called during some calls to ensure that another process does not
// read or write to the SafeArray during editing.
//
// AKA: SafeArrayLock in Windows API.
func safeArrayLock(safearray *SafeArray) (err error) {
err = convertHresultToError(procSafeArrayLock.Call(uintptr(unsafe.Pointer(safearray))))
return
}
// safeArrayUnlock unlocks SafeArray for reading.
//
// AKA: SafeArrayUnlock in Windows API.
func safeArrayUnlock(safearray *SafeArray) (err error) {
err = convertHresultToError(procSafeArrayUnlock.Call(uintptr(unsafe.Pointer(safearray))))
return
}
// safeArrayPutElement stores the data element at the specified location in the
// array.
//
// AKA: SafeArrayPutElement in Windows API.
func safeArrayPutElement(safearray *SafeArray, index int64, element uintptr) (err error) {
err = convertHresultToError(
procSafeArrayPutElement.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&index)),
uintptr(unsafe.Pointer(element))))
return
}
// safeArrayGetRecordInfo accesses IRecordInfo info for custom types.
//
// AKA: SafeArrayGetRecordInfo in Windows API.
//
// XXX: Must implement IRecordInfo interface for this to return.
func safeArrayGetRecordInfo(safearray *SafeArray) (recordInfo interface{}, err error) {
err = convertHresultToError(
procSafeArrayGetRecordInfo.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&recordInfo))))
return
}
// safeArraySetRecordInfo mutates IRecordInfo info for custom types.
//
// AKA: SafeArraySetRecordInfo in Windows API.
//
// XXX: Must implement IRecordInfo interface for this to return.
func safeArraySetRecordInfo(safearray *SafeArray, recordInfo interface{}) (err error) {
err = convertHresultToError(
procSafeArraySetRecordInfo.Call(
uintptr(unsafe.Pointer(safearray)),
uintptr(unsafe.Pointer(&recordInfo))))
return
}

140
vendor/github.com/go-ole/go-ole/safearrayconversion.go generated vendored Normal file
View File

@ -0,0 +1,140 @@
// Helper for converting SafeArray to array of objects.
package ole
import (
"unsafe"
)
type SafeArrayConversion struct {
Array *SafeArray
}
func (sac *SafeArrayConversion) ToStringArray() (strings []string) {
totalElements, _ := sac.TotalElements(0)
strings = make([]string, totalElements)
for i := int32(0); i < totalElements; i++ {
strings[int32(i)], _ = safeArrayGetElementString(sac.Array, i)
}
return
}
func (sac *SafeArrayConversion) ToByteArray() (bytes []byte) {
totalElements, _ := sac.TotalElements(0)
bytes = make([]byte, totalElements)
for i := int32(0); i < totalElements; i++ {
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&bytes[int32(i)]))
}
return
}
func (sac *SafeArrayConversion) ToValueArray() (values []interface{}) {
totalElements, _ := sac.TotalElements(0)
values = make([]interface{}, totalElements)
vt, _ := safeArrayGetVartype(sac.Array)
for i := int32(0); i < totalElements; i++ {
switch VT(vt) {
case VT_BOOL:
var v bool
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_I1:
var v int8
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_I2:
var v int16
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_I4:
var v int32
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_I8:
var v int64
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_UI1:
var v uint8
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_UI2:
var v uint16
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_UI4:
var v uint32
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_UI8:
var v uint64
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_R4:
var v float32
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_R8:
var v float64
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v
case VT_BSTR:
v , _ := safeArrayGetElementString(sac.Array, i)
values[i] = v
case VT_VARIANT:
var v VARIANT
safeArrayGetElement(sac.Array, i, unsafe.Pointer(&v))
values[i] = v.Value()
v.Clear()
default:
// TODO
}
}
return
}
func (sac *SafeArrayConversion) GetType() (varType uint16, err error) {
return safeArrayGetVartype(sac.Array)
}
func (sac *SafeArrayConversion) GetDimensions() (dimensions *uint32, err error) {
return safeArrayGetDim(sac.Array)
}
func (sac *SafeArrayConversion) GetSize() (length *uint32, err error) {
return safeArrayGetElementSize(sac.Array)
}
func (sac *SafeArrayConversion) TotalElements(index uint32) (totalElements int32, err error) {
if index < 1 {
index = 1
}
// Get array bounds
var LowerBounds int32
var UpperBounds int32
LowerBounds, err = safeArrayGetLBound(sac.Array, index)
if err != nil {
return
}
UpperBounds, err = safeArrayGetUBound(sac.Array, index)
if err != nil {
return
}
totalElements = UpperBounds - LowerBounds + 1
return
}
// Release Safe Array memory
func (sac *SafeArrayConversion) Release() {
safeArrayDestroy(sac.Array)
}

33
vendor/github.com/go-ole/go-ole/safearrayslices.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// +build windows
package ole
import (
"unsafe"
)
func safeArrayFromByteSlice(slice []byte) *SafeArray {
array, _ := safeArrayCreateVector(VT_UI1, 0, uint32(len(slice)))
if array == nil {
panic("Could not convert []byte to SAFEARRAY")
}
for i, v := range slice {
safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(&v)))
}
return array
}
func safeArrayFromStringSlice(slice []string) *SafeArray {
array, _ := safeArrayCreateVector(VT_BSTR, 0, uint32(len(slice)))
if array == nil {
panic("Could not convert []string to SAFEARRAY")
}
// SysAllocStringLen(s)
for i, v := range slice {
safeArrayPutElement(array, int64(i), uintptr(unsafe.Pointer(SysAllocStringLen(v))))
}
return array
}

101
vendor/github.com/go-ole/go-ole/utility.go generated vendored Normal file
View File

@ -0,0 +1,101 @@
package ole
import (
"unicode/utf16"
"unsafe"
)
// ClassIDFrom retrieves class ID whether given is program ID or application string.
//
// Helper that provides check against both Class ID from Program ID and Class ID from string. It is
// faster, if you know which you are using, to use the individual functions, but this will check
// against available functions for you.
func ClassIDFrom(programID string) (classID *GUID, err error) {
classID, err = CLSIDFromProgID(programID)
if err != nil {
classID, err = CLSIDFromString(programID)
if err != nil {
return
}
}
return
}
// BytePtrToString converts byte pointer to a Go string.
func BytePtrToString(p *byte) string {
a := (*[10000]uint8)(unsafe.Pointer(p))
i := 0
for a[i] != 0 {
i++
}
return string(a[:i])
}
// UTF16PtrToString is alias for LpOleStrToString.
//
// Kept for compatibility reasons.
func UTF16PtrToString(p *uint16) string {
return LpOleStrToString(p)
}
// LpOleStrToString converts COM Unicode to Go string.
func LpOleStrToString(p *uint16) string {
if p == nil {
return ""
}
length := lpOleStrLen(p)
a := make([]uint16, length)
ptr := unsafe.Pointer(p)
for i := 0; i < int(length); i++ {
a[i] = *(*uint16)(ptr)
ptr = unsafe.Pointer(uintptr(ptr) + 2)
}
return string(utf16.Decode(a))
}
// BstrToString converts COM binary string to Go string.
func BstrToString(p *uint16) string {
if p == nil {
return ""
}
length := SysStringLen((*int16)(unsafe.Pointer(p)))
a := make([]uint16, length)
ptr := unsafe.Pointer(p)
for i := 0; i < int(length); i++ {
a[i] = *(*uint16)(ptr)
ptr = unsafe.Pointer(uintptr(ptr) + 2)
}
return string(utf16.Decode(a))
}
// lpOleStrLen returns the length of Unicode string.
func lpOleStrLen(p *uint16) (length int64) {
if p == nil {
return 0
}
ptr := unsafe.Pointer(p)
for i := 0; ; i++ {
if 0 == *(*uint16)(ptr) {
length = int64(i)
break
}
ptr = unsafe.Pointer(uintptr(ptr) + 2)
}
return
}
// convertHresultToError converts syscall to error, if call is unsuccessful.
func convertHresultToError(hr uintptr, r2 uintptr, ignore error) (err error) {
if hr != 0 {
err = NewError(hr)
}
return
}

15
vendor/github.com/go-ole/go-ole/variables.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
// +build windows
package ole
import (
"golang.org/x/sys/windows"
)
var (
modcombase = windows.NewLazySystemDLL("combase.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modole32 = windows.NewLazySystemDLL("ole32.dll")
modoleaut32 = windows.NewLazySystemDLL("oleaut32.dll")
moduser32 = windows.NewLazySystemDLL("user32.dll")
)

105
vendor/github.com/go-ole/go-ole/variant.go generated vendored Normal file
View File

@ -0,0 +1,105 @@
package ole
import "unsafe"
// NewVariant returns new variant based on type and value.
func NewVariant(vt VT, val int64) VARIANT {
return VARIANT{VT: vt, Val: val}
}
// ToIUnknown converts Variant to Unknown object.
func (v *VARIANT) ToIUnknown() *IUnknown {
if v.VT != VT_UNKNOWN {
return nil
}
return (*IUnknown)(unsafe.Pointer(uintptr(v.Val)))
}
// ToIDispatch converts variant to dispatch object.
func (v *VARIANT) ToIDispatch() *IDispatch {
if v.VT != VT_DISPATCH {
return nil
}
return (*IDispatch)(unsafe.Pointer(uintptr(v.Val)))
}
// ToArray converts variant to SafeArray helper.
func (v *VARIANT) ToArray() *SafeArrayConversion {
if v.VT != VT_SAFEARRAY {
if v.VT&VT_ARRAY == 0 {
return nil
}
}
var safeArray *SafeArray = (*SafeArray)(unsafe.Pointer(uintptr(v.Val)))
return &SafeArrayConversion{safeArray}
}
// ToString converts variant to Go string.
func (v *VARIANT) ToString() string {
if v.VT != VT_BSTR {
return ""
}
return BstrToString(*(**uint16)(unsafe.Pointer(&v.Val)))
}
// Clear the memory of variant object.
func (v *VARIANT) Clear() error {
return VariantClear(v)
}
// Value returns variant value based on its type.
//
// Currently supported types: 2- and 4-byte integers, strings, bools.
// Note that 64-bit integers, datetimes, and other types are stored as strings
// and will be returned as strings.
//
// Needs to be further converted, because this returns an interface{}.
func (v *VARIANT) Value() interface{} {
switch v.VT {
case VT_I1:
return int8(v.Val)
case VT_UI1:
return uint8(v.Val)
case VT_I2:
return int16(v.Val)
case VT_UI2:
return uint16(v.Val)
case VT_I4:
return int32(v.Val)
case VT_UI4:
return uint32(v.Val)
case VT_I8:
return int64(v.Val)
case VT_UI8:
return uint64(v.Val)
case VT_INT:
return int(v.Val)
case VT_UINT:
return uint(v.Val)
case VT_INT_PTR:
return uintptr(v.Val) // TODO
case VT_UINT_PTR:
return uintptr(v.Val)
case VT_R4:
return *(*float32)(unsafe.Pointer(&v.Val))
case VT_R8:
return *(*float64)(unsafe.Pointer(&v.Val))
case VT_BSTR:
return v.ToString()
case VT_DATE:
// VT_DATE type will either return float64 or time.Time.
d := uint64(v.Val)
date, err := GetVariantDate(d)
if err != nil {
return float64(v.Val)
}
return date
case VT_UNKNOWN:
return v.ToIUnknown()
case VT_DISPATCH:
return v.ToIDispatch()
case VT_BOOL:
return v.Val != 0
}
return nil
}

11
vendor/github.com/go-ole/go-ole/variant_386.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
// +build 386
package ole
type VARIANT struct {
VT VT // 2
wReserved1 uint16 // 4
wReserved2 uint16 // 6
wReserved3 uint16 // 8
Val int64 // 16
}

12
vendor/github.com/go-ole/go-ole/variant_amd64.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// +build amd64
package ole
type VARIANT struct {
VT VT // 2
wReserved1 uint16 // 4
wReserved2 uint16 // 6
wReserved3 uint16 // 8
Val int64 // 16
_ [8]byte // 24
}

11
vendor/github.com/go-ole/go-ole/variant_arm.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
// +build arm
package ole
type VARIANT struct {
VT VT // 2
wReserved1 uint16 // 4
wReserved2 uint16 // 6
wReserved3 uint16 // 8
Val int64 // 16
}

13
vendor/github.com/go-ole/go-ole/variant_arm64.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
//go:build arm64
// +build arm64
package ole
type VARIANT struct {
VT VT // 2
wReserved1 uint16 // 4
wReserved2 uint16 // 6
wReserved3 uint16 // 8
Val int64 // 16
_ [8]byte // 24
}

22
vendor/github.com/go-ole/go-ole/variant_date_386.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// +build windows,386
package ole
import (
"errors"
"syscall"
"time"
"unsafe"
)
// GetVariantDate converts COM Variant Time value to Go time.Time.
func GetVariantDate(value uint64) (time.Time, error) {
var st syscall.Systemtime
v1 := uint32(value)
v2 := uint32(value >> 32)
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}

20
vendor/github.com/go-ole/go-ole/variant_date_amd64.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
// +build windows,amd64
package ole
import (
"errors"
"syscall"
"time"
"unsafe"
)
// GetVariantDate converts COM Variant Time value to Go time.Time.
func GetVariantDate(value uint64) (time.Time, error) {
var st syscall.Systemtime
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(value), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}

22
vendor/github.com/go-ole/go-ole/variant_date_arm.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// +build windows,arm
package ole
import (
"errors"
"syscall"
"time"
"unsafe"
)
// GetVariantDate converts COM Variant Time value to Go time.Time.
func GetVariantDate(value uint64) (time.Time, error) {
var st syscall.Systemtime
v1 := uint32(value)
v2 := uint32(value >> 32)
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}

23
vendor/github.com/go-ole/go-ole/variant_date_arm64.go generated vendored Normal file
View File

@ -0,0 +1,23 @@
//go:build windows && arm64
// +build windows,arm64
package ole
import (
"errors"
"syscall"
"time"
"unsafe"
)
// GetVariantDate converts COM Variant Time value to Go time.Time.
func GetVariantDate(value uint64) (time.Time, error) {
var st syscall.Systemtime
v1 := uint32(value)
v2 := uint32(value >> 32)
r, _, _ := procVariantTimeToSystemTime.Call(uintptr(v1), uintptr(v2), uintptr(unsafe.Pointer(&st)))
if r != 0 {
return time.Date(int(st.Year), time.Month(st.Month), int(st.Day), int(st.Hour), int(st.Minute), int(st.Second), int(st.Milliseconds/1000), time.UTC), nil
}
return time.Now(), errors.New("Could not convert to time, passing current time.")
}

Some files were not shown because too many files have changed in this diff Show More