Com 从Go查询WMI
我想从Go运行WMI查询。还有很多路要走。我的理解是,在某个地方一定有一些DLL,通过正确的调用,它将返回一些我可以解析和使用的数据。我宁愿避免调用C或C++,特别是因为我猜想这些是Windows API本身的包装器。 我已经检查了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) 但是我不知道从这里该怎么办。这个函数接受什么参
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)
}