从Go查询WMI

 依一勇婷16_639 发布于 2023-02-13 08:18

我想从Go运行WMI查询.有一些方法可以从Go 调用DLL函数.我的理解是必须有一些DLL,通过正确的调用,将返回一些我可以解析和使用的数据.我宁愿避免调用C或C++,特别是因为我猜它们是Windows API本身的包装器.

我已经检查了输出dumpbin.exe /exports c:\windows\system32\wmi.dll,以下条目看起来很有希望:

WmiQueryAllDataA (forwarded to wmiclnt.WmiQueryAllDataA)

但是我不知道该怎么做.这个函数有什么参数?它返回了什么?搜索WmiQueryAllDataA没有帮助.并且该名称仅出现在注释中c:\program files (x86)\windows kits\8.1\include\shared\wmistr.h,但没有功能签名.

有更好的方法吗?还有另一个DLL吗?我错过了什么吗?我应该只使用C包装吗?

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

更新:使用github.com/StackExchange/wmi包,该包使用接受答案中的解决方案.

2 个回答
  • 我在一年后发表评论,但是github上有一个解决方案(以下发布给后人).

    // +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()
    }
    

    2023-02-13 08:21 回答
  • 从C++成为"年轻的新贵"开始,欢迎来到COM的精彩世界,C语言中的面向对象编程.

    在github上,mattn将Go中的一个小包装器组合在一起,我曾经把它放在一起快速示例程序." 这个存储库是为实验而创建的,应该被认为是不稳定的. "这充满了各种信心.

    我遗漏了很多错误检查.相信我,当我说,你会想要添加它.

    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())
        }
    }
    

    真正的肉是对ExecQuery的调用,我碰巧从可用的类中获取Win32_Process,因为它易于理解和打印.

    在我的机器上,这打印:

    System 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
    

    我没有升级或禁用UAC,但是一些WMI提供商需要特权用户.

    我也不是100%,这不会泄漏一点,你会想深入研究.COM对象是引用计数的,所以defer应该非常适合那里(如果方法不是长时间疯狂运行)但是go-ole可能有一些我没注意到的魔法.

    2023-02-13 08:21 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有