Com 从Go查询WMI

Com 从Go查询WMI,com,go,wmi,Com,Go,Wmi,我想从Go运行WMI查询。还有很多路要走。我的理解是,在某个地方一定有一些DLL,通过正确的调用,它将返回一些我可以解析和使用的数据。我宁愿避免调用C或C++,特别是因为我猜想这些是Windows API本身的包装器。 我已经检查了dumpbin.exe/exports c:\windows\system32\wmi.dll的输出,下面的条目看起来很有希望: WmiQueryAllDataA(转发到wmiclnt.WmiQueryAllDataA) 但是我不知道从这里该怎么办。这个函数接受什么参

我想从Go运行WMI查询。还有很多路要走。我的理解是,在某个地方一定有一些DLL,通过正确的调用,它将返回一些我可以解析和使用的数据。我宁愿避免调用C或C++,特别是因为我猜想这些是Windows API本身的包装器。 我已经检查了
dumpbin.exe/exports c:\windows\system32\wmi.dll
的输出,下面的条目看起来很有希望:

WmiQueryAllDataA(转发到wmiclnt.WmiQueryAllDataA)

但是我不知道从这里该怎么办。这个函数接受什么参数?它的回报是什么?搜索
WmiQueryAllDataA
没有帮助。该名称仅出现在
c:\program files(x86)\windows kits\8.1\include\shared\wmistr.h
的注释中,但没有函数签名

有更好的方法吗?还有其他DLL吗?我错过什么了吗?我应该使用C包装器吗

使用.NET Reflector在Linqpad中运行WMI查询显示了
WmiNetUtilsHelper:ExecQueryWmi
(和
\u f
版本)的使用,但两者都没有可查看的实现


<强>更新:使用在接受的答案中使用解决方案的包。

< P>欢迎进入C++的“面向对象编程”的精彩世界。 在github上创建了一个,我曾经创建过一个快速示例程序。“这个存储库是为实验而创建的,应该被认为是不稳定的。”这给人们灌输了各种各样的信心

我遗漏了很多错误检查。相信我,当我说,你会想把它加回去的

package main

import (
        "github.com/mattn/go-ole"
        "github.com/mattn/go-ole/oleutil"
)

func main() {
    // init COM, oh yeah
    ole.CoInitialize(0)
    defer ole.CoUninitialize()

    unknown, _ := oleutil.CreateObject("WbemScripting.SWbemLocator")
    defer unknown.Release()

    wmi, _ := unknown.QueryInterface(ole.IID_IDispatch)
    defer wmi.Release()

    // service is a SWbemServices
    serviceRaw, _ := oleutil.CallMethod(wmi, "ConnectServer")
    service := serviceRaw.ToIDispatch()
    defer service.Release()

    // result is a SWBemObjectSet
    resultRaw, _ := oleutil.CallMethod(service, "ExecQuery", "SELECT * FROM Win32_Process")
    result := resultRaw.ToIDispatch()
    defer result.Release()

    countVar, _ := oleutil.GetProperty(result, "Count")
    count := int(countVar.Val)

    for i :=0; i < count; i++ {
        // item is a SWbemObject, but really a Win32_Process
        itemRaw, _ := oleutil.CallMethod(result, "ItemIndex", i)
        item := itemRaw.ToIDispatch()
        defer item.Release()

        asString, _ := oleutil.GetProperty(item, "Name")

        println(asString.ToString())
    }
}
我不会在提升或禁用UAC的情况下运行它,但某些WMI提供程序需要特权用户


我也不是100%的认为这不会泄露一点,你会想深入研究的。COM对象是按引用计数的,因此
defer
应该非常适合那里(只要该方法不是疯狂的长时间运行),但是go ole可能有一些我没有注意到的神奇之处。

我在一年后发表评论,但是(并在下面为后代发布)

/+构建窗口
/*
包wmi为Windows上的wmi提供WQL接口。
打印正在运行的进程名称的示例代码:
类型Win32_进程结构{
名称字符串
}
func main(){
var dst[]Win32_进程
q:=wmi.CreateQuery(&dst,“”)
错误:=wmi.Query(q和dst)
如果错误!=零{
log.Fatal(错误)
}
对于i,v:=范围dst{
println(i,v.Name)
}
}
*/
包wmi
进口(
“字节”
“错误”
“fmt”
“日志”
“操作系统”
“反映”
“运行时”
“strconv”
“字符串”
“同步”
“时间”
“github.com/mattn/go-ole”
“github.com/mattn/go-ole/oleutil”
)
var l=log.New(os.Stdout,“,log.LstdFlags)
变量(
errInvalidIdentityType=错误。新建(“wmi:无效实体类型”)
锁同步
)
//QueryNamespace使用本地计算机上的给定命名空间调用查询。
func QueryNamespace(查询字符串、dst接口{}、命名空间字符串)错误{
返回查询(查询、dst、nil、命名空间)
}
//查询运行WQL查询并将值附加到dst。
//
//对于某些结构类型S,dst的类型必须为*[]S或*[]S。在中选择的字段
//查询在dst中必须具有相同的名称。支持的类型都是有符号和
//无符号整数、time.time、string、bool或指向其中一个的指针。
//不支持数组类型。
//
//默认情况下,使用本地计算机和默认名称空间。这些可能是
//使用connectServerArgs进行了更改。看见
// http://msdn.microsoft.com/en-us/library/aa393720.aspx 详情请参阅。
func查询(查询字符串,dst接口{},connectServerArgs…接口{})错误{
dv:=反射值(dst)
如果dv.Kind()!=reflect.Ptr | dv.IsNil(){
返回errInvalidIdentityType
}
dv=dv.Elem()
mat,elemType:=checkMultiArg(dv)
如果mat==multiArgTypeInvalid{
返回errInvalidIdentityType
}
lock.lock()
延迟锁定。解锁()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
错误:=ole.CoInitializeEx(0,ole.Conit\u多线程)
如果错误!=零{
oleerr:=错误。(*ole.OleError)
//S_FALSE=0x00000001//CoInitializeEx已在此线程上调用
如果oleerr.Code()!=ole.S_正常&&oleerr.Code()!=0x00000001{
返回错误
}
}否则{
//仅当线程之前未初始化时才调用ConInitialize。
//这将允许基于go ole的其他go包一起播放
//用这个图书馆。
延迟ole.conInitialize()
}
未知,错误:=oleutil.CreateObject(“WbemScripting.SWbemLocator”)
如果错误!=零{
返回错误
}
延迟未知。释放()
wmi,错误:=未知的.QueryInterface(ole.IID_IDispatch)
如果错误!=零{
返回错误
}
延迟wmi.Release()
//服务是一种SWbemServices
serviceRaw,err:=oleutil.CallMethod(wmi,“ConnectServer”,connectServerArgs…)
如果错误!=零{
返回错误
}
服务:=serviceRaw.ToIDispatch()
延迟serviceRaw.Clear()
//结果是一个SWBemObjectSet
resultRaw,err:=oleutil.CallMethod(服务,“ExecQuery”,查询)
如果错误!=零{
返回错误
}
结果:=resultRaw.ToIDispatch()
延迟resultRaw.Clear()
计数,错误:=oleInt64(结果为“计数”)
如果错误!=零{
返回错误
}
//使用计数容量初始化切片
Set(reflect.MakeSlice(dv.Type(),0,int(count)))
var错误字段不匹配错误
对于i:=int64(0);iSystem Idle Process
System
smss.exe
csrss.exe
wininit.exe
services.exe
lsass.exe
svchost.exe
svchost.exe
atiesrxx.exe
svchost.exe
svchost.exe
svchost.exe
svchost.exe
svchost.exe
spoolsv.exe
svchost.exe
AppleOSSMgr.exe
AppleTimeSrv.exe
... and so on
go.exe
main.exe
// +build windows

/*
Package wmi provides a WQL interface for WMI on Windows.

Example code to print names of running processes:

    type Win32_Process struct {
        Name string
    }

    func main() {
        var dst []Win32_Process
        q := wmi.CreateQuery(&dst, "")
        err := wmi.Query(q, &dst)
        if err != nil {
            log.Fatal(err)
        }
        for i, v := range dst {
            println(i, v.Name)
        }
    }

*/
package wmi

import (
    "bytes"
    "errors"
    "fmt"
    "log"
    "os"
    "reflect"
    "runtime"
    "strconv"
    "strings"
    "sync"
    "time"

    "github.com/mattn/go-ole"
    "github.com/mattn/go-ole/oleutil"
)

var l = log.New(os.Stdout, "", log.LstdFlags)

var (
    ErrInvalidEntityType = errors.New("wmi: invalid entity type")
    lock                 sync.Mutex
)

// QueryNamespace invokes Query with the given namespace on the local machine.
func QueryNamespace(query string, dst interface{}, namespace string) error {
    return Query(query, dst, nil, namespace)
}

// Query runs the WQL query and appends the values to dst.
//
// dst must have type *[]S or *[]*S, for some struct type S. Fields selected in
// the query must have the same name in dst. Supported types are all signed and
// unsigned integers, time.Time, string, bool, or a pointer to one of those.
// Array types are not supported.
//
// By default, the local machine and default namespace are used. These can be
// changed using connectServerArgs. See
// http://msdn.microsoft.com/en-us/library/aa393720.aspx for details.
func Query(query string, dst interface{}, connectServerArgs ...interface{}) error {
    dv := reflect.ValueOf(dst)
    if dv.Kind() != reflect.Ptr || dv.IsNil() {
        return ErrInvalidEntityType
    }
    dv = dv.Elem()
    mat, elemType := checkMultiArg(dv)
    if mat == multiArgTypeInvalid {
        return ErrInvalidEntityType
    }

    lock.Lock()
    defer lock.Unlock()
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()

    err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
    if err != nil {
        oleerr := err.(*ole.OleError)
        // S_FALSE           = 0x00000001 // CoInitializeEx was already called on this thread
        if oleerr.Code() != ole.S_OK && oleerr.Code() != 0x00000001 {
            return err
        }
    } else {
        // Only invoke CoUninitialize if the thread was not initizlied before.
        // This will allow other go packages based on go-ole play along
        // with this library.
        defer ole.CoUninitialize()
    }

    unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
    if err != nil {
        return err
    }
    defer unknown.Release()

    wmi, err := unknown.QueryInterface(ole.IID_IDispatch)
    if err != nil {
        return err
    }
    defer wmi.Release()

    // service is a SWbemServices
    serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", connectServerArgs...)
    if err != nil {
        return err
    }
    service := serviceRaw.ToIDispatch()
    defer serviceRaw.Clear()

    // result is a SWBemObjectSet
    resultRaw, err := oleutil.CallMethod(service, "ExecQuery", query)
    if err != nil {
        return err
    }
    result := resultRaw.ToIDispatch()
    defer resultRaw.Clear()

    count, err := oleInt64(result, "Count")
    if err != nil {
        return err
    }

    // Initialize a slice with Count capacity
    dv.Set(reflect.MakeSlice(dv.Type(), 0, int(count)))

    var errFieldMismatch error
    for i := int64(0); i < count; i++ {
        err := func() error {
            // item is a SWbemObject, but really a Win32_Process
            itemRaw, err := oleutil.CallMethod(result, "ItemIndex", i)
            if err != nil {
                return err
            }
            item := itemRaw.ToIDispatch()
            defer itemRaw.Clear()

            ev := reflect.New(elemType)
            if err = loadEntity(ev.Interface(), item); err != nil {
                if _, ok := err.(*ErrFieldMismatch); ok {
                    // We continue loading entities even in the face of field mismatch errors.
                    // If we encounter any other error, that other error is returned. Otherwise,
                    // an ErrFieldMismatch is returned.
                    errFieldMismatch = err
                } else {
                    return err
                }
            }
            if mat != multiArgTypeStructPtr {
                ev = ev.Elem()
            }
            dv.Set(reflect.Append(dv, ev))
            return nil
        }()
        if err != nil {
            return err
        }
    }
    return errFieldMismatch
}

// ErrFieldMismatch is returned when a field is to be loaded into a different
// type than the one it was stored from, or when a field is missing or
// unexported in the destination struct.
// StructType is the type of the struct pointed to by the destination argument.
type ErrFieldMismatch struct {
    StructType reflect.Type
    FieldName  string
    Reason     string
}

func (e *ErrFieldMismatch) Error() string {
    return fmt.Sprintf("wmi: cannot load field %q into a %q: %s",
        e.FieldName, e.StructType, e.Reason)
}

var timeType = reflect.TypeOf(time.Time{})

// loadEntity loads a SWbemObject into a struct pointer.
func loadEntity(dst interface{}, src *ole.IDispatch) (errFieldMismatch error) {
    v := reflect.ValueOf(dst).Elem()
    for i := 0; i < v.NumField(); i++ {
        f := v.Field(i)
        isPtr := f.Kind() == reflect.Ptr
        if isPtr {
            ptr := reflect.New(f.Type().Elem())
            f.Set(ptr)
            f = f.Elem()
        }
        n := v.Type().Field(i).Name
        if !f.CanSet() {
            return &ErrFieldMismatch{
                StructType: f.Type(),
                FieldName:  n,
                Reason:     "CanSet() is false",
            }
        }
        prop, err := oleutil.GetProperty(src, n)
        if err != nil {
            errFieldMismatch = &ErrFieldMismatch{
                StructType: f.Type(),
                FieldName:  n,
                Reason:     "no such struct field",
            }
            continue
        }
        defer prop.Clear()

        switch val := prop.Value().(type) {
        case int, int64:
            var v int64
            switch val := val.(type) {
            case int:
                v = int64(val)
            case int64:
                v = val
            default:
                panic("unexpected type")
            }
            switch f.Kind() {
            case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                f.SetInt(v)
            case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
                f.SetUint(uint64(v))
            default:
                return &ErrFieldMismatch{
                    StructType: f.Type(),
                    FieldName:  n,
                    Reason:     "not an integer class",
                }
            }
        case string:
            iv, err := strconv.ParseInt(val, 10, 64)
            switch f.Kind() {
            case reflect.String:
                f.SetString(val)
            case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                if err != nil {
                    return err
                }
                f.SetInt(iv)
            case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
                if err != nil {
                    return err
                }
                f.SetUint(uint64(iv))
            case reflect.Struct:
                switch f.Type() {
                case timeType:
                    if len(val) == 25 {
                        mins, err := strconv.Atoi(val[22:])
                        if err != nil {
                            return err
                        }
                        val = val[:22] + fmt.Sprintf("%02d%02d", mins/60, mins%60)
                    }
                    t, err := time.Parse("20060102150405.000000-0700", val)
                    if err != nil {
                        return err
                    }
                    f.Set(reflect.ValueOf(t))
                }
            }
        case bool:
            switch f.Kind() {
            case reflect.Bool:
                f.SetBool(val)
            default:
                return &ErrFieldMismatch{
                    StructType: f.Type(),
                    FieldName:  n,
                    Reason:     "not a bool",
                }
            }
        default:
            typeof := reflect.TypeOf(val)
            if isPtr && typeof == nil {
                break
            }
            return &ErrFieldMismatch{
                StructType: f.Type(),
                FieldName:  n,
                Reason:     fmt.Sprintf("unsupported type (%T)", val),
            }
        }
    }
    return errFieldMismatch
}

type multiArgType int

const (
    multiArgTypeInvalid multiArgType = iota
    multiArgTypeStruct
    multiArgTypeStructPtr
)

// checkMultiArg checks that v has type []S, []*S for some struct type S.
//
// It returns what category the slice's elements are, and the reflect.Type
// that represents S.
func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
    if v.Kind() != reflect.Slice {
        return multiArgTypeInvalid, nil
    }
    elemType = v.Type().Elem()
    switch elemType.Kind() {
    case reflect.Struct:
        return multiArgTypeStruct, elemType
    case reflect.Ptr:
        elemType = elemType.Elem()
        if elemType.Kind() == reflect.Struct {
            return multiArgTypeStructPtr, elemType
        }
    }
    return multiArgTypeInvalid, nil
}

func oleInt64(item *ole.IDispatch, prop string) (int64, error) {
    v, err := oleutil.GetProperty(item, prop)
    if err != nil {
        return 0, err
    }
    defer v.Clear()

    i := int64(v.Val)
    return i, nil
}

// CreateQuery returns a WQL query string that queries all columns of src. where
// is an optional string that is appended to the query, to be used with WHERE
// clauses. In such a case, the "WHERE" string should appear at the beginning.
func CreateQuery(src interface{}, where string) string {
    var b bytes.Buffer
    b.WriteString("SELECT ")
    s := reflect.Indirect(reflect.ValueOf(src))
    t := s.Type()
    if s.Kind() == reflect.Slice {
        t = t.Elem()
    }
    if t.Kind() != reflect.Struct {
        return ""
    }
    var fields []string
    for i := 0; i < t.NumField(); i++ {
        fields = append(fields, t.Field(i).Name)
    }
    b.WriteString(strings.Join(fields, ", "))
    b.WriteString(" FROM ")
    b.WriteString(t.Name())
    b.WriteString(" " + where)
    return b.String()
}
package main

import (
    "log"

    "github.com/go-ole/go-ole"
    "github.com/go-ole/go-ole/oleutil"
)

func main() {
    ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
    defer ole.CoUninitialize()

    unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
    if err != nil {
        log.Panic(err)
    }
    defer unknown.Release()

    wmi, err := unknown.QueryInterface(ole.IID_IDispatch)
    if err != nil {
        log.Panic(err)
    }
    defer wmi.Release()

    // Connect to namespace
    // root/PanasonicPC = winmgmts:\\.\root\PanasonicPC
    serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", nil, "root/PanasonicPC")
    if err != nil {
        log.Panic(err)
    }
    service := serviceRaw.ToIDispatch()
    defer serviceRaw.Clear()

    // Get class
    setBiosRaw, err := oleutil.CallMethod(service, "Get", "SetBIOS4Conf")
    if err != nil {
        log.Panic(err)
    }
    setBios := setBiosRaw.ToIDispatch()
    defer setBiosRaw.Clear()

    // Run method
    resultRaw, err := oleutil.CallMethod(setBios, "AccessAuthorization", "letmein")
    resultVal := resultRaw.Value().(int32)

    log.Println("Return Code:", resultVal)
}