wmi

package module
v1.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2022 License: MIT Imports: 12 Imported by: 0

README

wmi

GoDoc Go Report Card

Package wmi provides a WMI Query Language (WQL) interface for Windows Management Instrumentation (WMI) on Windows.

This package uses COM API for WMI therefore it's only usable on the Windows machines.

Package reference is available at https://godoc.org/github.com/countymicro/wmi

Fork Features

Fork is fully compatibly with the original repo. If not - please open an issue.

New features introduced in fork:

  • Go 1.11 modules support :)
  • Improved decoder:
    • support all basic types: all integer types, float32, string, bool, uintptr and time.Time
    • support slices and pointers to all basic types
    • support decoding of structure fields (see events example)
    • support structure tags
    • support JSON-like interface for custom decoding
    • suitable for decoding properties of any go-ole IDispatch object
  • Ability to perform multiple queries in a single connection
  • SWbemServices.Get + auto dereference of REF fields
  • SWbemServices.ExecNotificationQuery support
  • More other improvements described in releases page

Example

Print names of the currently running processes

package main

import (
   "fmt"
   "log"

   "github.com/countymicro/wmi"
)

type win32Process struct {
   PID       uint32 `wmi:"ProcessId"`
   Name      string
   UserField int `wmi:"-"`
}

func main() {
   var dst []win32Process

   q := wmi.CreateQueryFrom(&dst, "Win32_Process", "")
   fmt.Println(q)

   if err := wmi.Query(q, &dst); err != nil {
   	log.Fatal(err)
   }
   for _, v := range dst {
   	fmt.Printf("%6d\t%s\n", v.PID, v.Name)
   }
}

A more sophisticated examples are located at in examples folder.

Benchmarks

Using DefaultClient, SWbemServices or SWbemServicesConnection differ in a number of setup calls doing to perform each query (from the most to the least).

Estimated overhead is shown below:

BenchmarkQuery_DefaultClient   5000  33529798 ns/op
BenchmarkQuery_SWbemServices   5000  32031199 ns/op
BenchmarkQuery_SWbemConnection 5000  30099403 ns/op

You could reproduce the results on your machine running:

go test -run=NONE -bench=Query -benchtime=120s

Versioning

Project uses semantic versioning 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.

Documentation

Rendered for windows/amd64

Overview

Package wmi provides a WMI Query Language (WQL) interface for Windows Management Instrumentation (WMI) on Windows.

This package uses COM API for WMI therefore it's only usable on the Windows machines.

This package has many .Query calls, the main rule of thumb for choosing the right one is "prefer SWbemServicesConnection if you bother about performance and just do wmi.Query if not". More detailed benchmarks are available in the repo: https://github.com/countymicro/wmi#benchmarks

More reference about WMI is available in Microsoft Docs: https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-reference)

Example (EnumerateRunningProcesses)
//go:build windows
// +build windows

package main

import (
	"fmt"
	"log"

	"github.com/countymicro/wmi"
)

// Structure with some fields from Win32_Process
// Ref: https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process
type win32Process struct {
	PID       uint32 `wmi:"ProcessId"` // Field name differ from Win32_Process
	Name      string // Same name as in Win32_Process structure
	UserField int    `wmi:"-"` // Shouldn't affect WMI fields
}

func main() {
	var dst []win32Process

	q := wmi.CreateQueryFrom(&dst, "Win32_Process", "")
	fmt.Println(q)

	if err := wmi.Query(q, &dst); err != nil {
		log.Fatal(err)
	}
	for _, v := range dst {
		fmt.Printf("%6d\t%s\n", v.PID, v.Name)
	}
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidEntityType is returned in case of unsupported destination type
	// given to the `Query` call.
	ErrInvalidEntityType = errors.New("wmi: invalid entity type")

	// ErrNilCreateObject is the error returned if CreateObject returns nil even
	// if the error was nil.
	ErrNilCreateObject = errors.New("wmi: create object returned nil")
)
View Source
var DefaultClient = &Client{}

DefaultClient is the default Client and is used by Query, QueryNamespace

View Source
var (
	// ErrAlreadyRunning is returned when NotificationQuery is already running.
	ErrAlreadyRunning = errors.New("already running")
)
View Source
var (
	// ErrConnectionClosed is returned for methods called on the closed
	// SWbemServicesConnection.
	ErrConnectionClosed = errors.New("SWbemServicesConnection has been closed")
)

Functions

func CreateQuery

func CreateQuery(src interface{}, where string) string

CreateQuery returns a WQL query string that queries all columns of @src.

@src could be T, *T, []T, or *[]T;

@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.

type Win32_Product struct {
	Name            string
	InstallLocation string
}
var dst []Win32_Product
query := wmi.CreateQuery(&dst, "WHERE InstallLocation != null")

func CreateQueryFrom

func CreateQueryFrom(src interface{}, from, where string) string

CreateQuery returns a WQL query string that queries all columns of @src from class @from with condition @where (optional).

N.B. The call is the same as `CreateQuery` but uses @from instead of structure name as a class name.

func Query

func Query(query string, dst interface{}, connectServerArgs ...interface{}) error

Query runs the WQL query and appends the values to dst.

More info about result unmarshalling is available in `Decoder.Unmarshal` doc.

By default, the local machine and default namespace are used. These can be changed using connectServerArgs. See a reference below for details.

https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemlocator-connectserver

Query is a wrapper around DefaultClient.Query.

func QueryNamespace

func QueryNamespace(query string, dst interface{}, namespace string) error

QueryNamespace invokes Query with the given namespace on the local machine.

Types

type Client

type Client struct {
	// Embedded Decoder for backward-compatibility.
	Decoder

	// SWbemServiceClient is an optional SWbemServices object that can be
	// initialized and then reused across multiple queries. If it is null
	// then the method will initialize a new temporary client each time.
	SWbemServicesClient *SWbemServices
}

A Client is an WMI query client.

Its zero value (`DefaultClient`) is a usable client.

Client provides an ability to modify result decoding params by modifying embedded `.Decoder` properties.

Important: Using zero-value Client does not speed up your queries comparing to using `wmi.Query` method. Refer to benchmarks in repo README.md for more info about the speed.

func (*Client) Query

func (c *Client) Query(query string, dst interface{}, connectServerArgs ...interface{}) (err error)

Query runs the WQL query and appends the values to dst.

More info about result unmarshalling is available in `Decoder.Unmarshal` doc.

By default, the local machine and default namespace are used. These can be changed using connectServerArgs. See a reference below for details.

https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemlocator-connectserver

type Decoder

type Decoder struct {
	// NonePtrZero specifies if nil values for fields which aren't pointers
	// should be returned as the field types zero value.
	//
	// Setting this to true allows structs without pointer fields to be used
	// without the risk failure should a nil value returned from WMI.
	NonePtrZero bool

	// PtrNil specifies if nil values for pointer fields should be returned
	// as nil.
	//
	// Setting this to true will set pointer fields to nil where WMI
	// returned nil, otherwise the types zero value will be returned.
	PtrNil bool

	// AllowMissingFields specifies that struct fields not present in the
	// query result should not result in an error.
	//
	// Setting this to true allows custom queries to be used with full
	// struct definitions instead of having to define multiple structs.
	AllowMissingFields bool

	// Dereferencer specifies an interface to resolve reference fields.
	// Dereferencer will be invoked on the fields tagged with ",ref" tag, e.g.
	//     Field Type `wmi:"FieldName,ref"
	//
	// Such fields will be resolved to the string that will be passed to
	// `Dereference` call. Resulting object will be used to fill the actual
	// field value.
	//
	// Dereferencer is automatically set by all query calls. Setting it to nil
	// will cause all fields tagged as references to return resolution error.
	Dereferencer Dereferencer
}

Decoder handles "decoding" of `ole.IDispatch` objects into the given structure. See `Decoder.Unmarshal` for more info.

func (Decoder) Unmarshal

func (d Decoder) Unmarshal(src *ole.IDispatch, dst interface{}) (err error)

Unmarshal loads `ole.IDispatch` into a struct pointer. N.B. Unmarshal supports only limited subset of structure field types:

  • all signed and unsigned integers
  • uintptr
  • time.Time
  • string
  • bool
  • float32
  • a pointer to one of types above
  • a slice of one of thus types
  • structure types.

To unmarshal more complex struct consider implementing `wmi.Unmarshaler`. For such types Unmarshal just calls `.UnmarshalOLE` on the @src object .

To unmarshal COM-object into a struct, Unmarshal tries to fetch COM-object properties for each public struct field using as a property name either field name itself or the name specified in "wmi" field tag.

By default any field missed in the COM-object leads to the error. To allow skipping such fields set `.AllowMissingFields` to `true`.

Unmarshal does some "smart" type conversions between integer types (including unsigned ones), so you could receive e.g. `uint32` into `uint` if you don't care about the size.

Unmarshal allows to specify special COM-object property name or skip a field using structure field tags, e.g.

  // Will be filled from property `Frequency_Object`
  FrequencyObject int wmi:"Frequency_Object"`

  // Will be skipped during unmarshalling.
  MyHelperField   int wmi:"-"`

  // Will be unmarshalled by CIM reference.
  // See `Dereferencer` for more info.
	 Field  Type `wmi:"FieldName,ref"
	 Field2 Type `wmi:",ref"

Unmarshal prefers tag value over the field name, but ignores any name collisions. So for example all the following fields will be resolved to the same value.

Field  int
Field1 int `wmi:"Field"`
Field2 int `wmi:"Field"`

type Dereferencer

type Dereferencer interface {
	Dereference(referencePath string) (*ole.VARIANT, error)
}

Dereferencer is anything that can fetch WMI objects using its object path. Used to retrieve object from CIM reference strings, e.g. from `Win32_LoggedOnUser`.

Ref:

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wmio/58e803a6-25f6-4ba6-abdc-b39e1daa66fc
https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-loggedonuser

type ErrFieldMismatch

type ErrFieldMismatch struct {
	FieldType reflect.Type
	FieldName string
	Reason    string
}

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. FieldType is the type of the struct pointed to by the destination argument.

func (ErrFieldMismatch) Error

func (e ErrFieldMismatch) Error() string

type NotificationQuery

type NotificationQuery struct {
	Decoder

	sync.Mutex
	// contains filtered or unexported fields
}

NotificationQuery represents subscription to the WMI events. For more info see https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemservices-execnotificationquery

func NewNotificationQuery

func NewNotificationQuery(eventCh interface{}, query string) (*NotificationQuery, error)

NewNotificationQuery creates a NotificationQuery from the given WQL @query string. The method just creates the object and does no WMI calls, so all WMI errors (query syntax, connection, etc.) will be returned on query start.

@eventCh should be a channel of structures or structure pointers. The structure type should satisfy limitations described in `Decoder.Unmarshal`.

Returns error if @eventCh is not `chan T` nor `chan *T`.

func (*NotificationQuery) SetConnectServerArgs

func (q *NotificationQuery) SetConnectServerArgs(args ...interface{})

SetConnectServerArgs sets `SWbemLocator.ConnectServer` args. Args are directly passed to `ole` call and support most of primitive types. Should be called before query being started.

Args reference: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemlocator-connectserver Passing details: https://github.com/go-ole/go-ole/blob/master/idispatch_windows.go#L60

func (*NotificationQuery) SetNotificationTimeout

func (q *NotificationQuery) SetNotificationTimeout(t time.Duration)

SetNotificationTimeout specifies a time query could send waiting for the next event at the worst case. Waiting for the next event locks notification thread so in other words @t specifies a time for notification thread to react to the `Stop()` command at the worst.

Default NotificationTimeout is 1s. It could be safely changed after the query `Start()`.

Setting it to negative Duration makes that interval infinite.

func (*NotificationQuery) StartNotifications

func (q *NotificationQuery) StartNotifications() (err error)

StartNotifications connects to the WMI service and starts receiving notifications generated by the query.

Errors are usually happen on initialization phase (connect to WMI, query execution, first result unmarshalling) so you could assume that "it's either starts and going to give me notifications or fails fast enough".

func (*NotificationQuery) Stop

func (q *NotificationQuery) Stop()

Stop stops the running query waiting until everything is released. It could take some time for query to receive a stop signal. See `SetNotificationTimeout` for more info.

type SWbemServices

type SWbemServices struct {
	sync.Mutex
	Decoder
	// contains filtered or unexported fields
}

SWbemServices is used to access wmi on a different machines or namespaces (with different `SWbemServices ConnectServer` args) using the single object.

If you need to query the single namespace on a single server prefer using SWbemServicesConnection instead.

func InitializeSWbemServices deprecated

func InitializeSWbemServices(c *Client, connectServerArgs ...interface{}) (*SWbemServices, error)

InitializeSWbemServices will return a new SWbemServices object that can be used to query WMI.

Deprecated: Use NewSWbemServices instead.

func NewSWbemServices

func NewSWbemServices() (s *SWbemServices, err error)

NewSWbemServices creates SWbemServices instance.

func (*SWbemServices) Close

func (s *SWbemServices) Close() error

Close will clear and release all of the SWbemServices resources.

func (*SWbemServices) ConnectServer

func (s *SWbemServices) ConnectServer(args ...interface{}) (c *SWbemServicesConnection, err error)

ConnectSWbemServices creates SWbemServices connection to the server defined by @args.

Ref: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemlocator-connectserver

func (*SWbemServices) Query

func (s *SWbemServices) Query(query string, dst interface{}, connectServerArgs ...interface{}) (err error)

Query runs the WQL query using a SWbemServicesConnection instance and appends the values to dst.

More info about result unmarshalling is available in `Decoder.Unmarshal` doc.

By default, the local machine and default namespace are used. These can be changed using connectServerArgs. See Ref. for more info.

Ref: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemlocator-connectserver

type SWbemServicesConnection

type SWbemServicesConnection struct {
	sync.Mutex
	Decoder
	// contains filtered or unexported fields
}

SWbemServicesConnection is used to access SWbemServices methods of the single server.

Ref: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemservices

func ConnectSWbemServices

func ConnectSWbemServices(connectServerArgs ...interface{}) (conn *SWbemServicesConnection, err error)

ConnectSWbemServices creates SWbemServices connection to the server defined by @connectServerArgs. Actually it just creates `SWbemLocator` and invokes `SWbemServices ConnectServer` method. Args are passed to the method as it.

Ref: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemlocator-connectserver

func (*SWbemServicesConnection) Close

func (s *SWbemServicesConnection) Close() error

Close will clear and release all of the SWbemServicesConnection resources.

func (*SWbemServicesConnection) Dereference

func (s *SWbemServicesConnection) Dereference(referencePath string) (v *ole.VARIANT, err error)

Dereference performs `SWbemServices.Get` on the given path, but returns the low level result itself not performing unmarshalling.

func (*SWbemServicesConnection) Get

func (s *SWbemServicesConnection) Get(path string, dst interface{}) (err error)

Get retrieves a single instance of a managed resource (or class definition) based on an object @path. The result is unmarshalled into @dst. @dst should be a pointer to the structure type.

More info about result unmarshalling is available in `Decoder.Unmarshal` doc.

Get method reference: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemservices-get

func (*SWbemServicesConnection) Query

func (s *SWbemServicesConnection) Query(query string, dst interface{}) error

Query runs the WQL query using a SWbemServicesConnection instance and appends the values to dst.

More info about result unmarshalling is available in `Decoder.Unmarshal` doc.

Query is performed using `SWbemServices.ExecQuery` method.

Ref: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/swbemservices-execquery

type Unmarshaler

type Unmarshaler interface {
	UnmarshalOLE(d Decoder, src *ole.IDispatch) error
}

Unmarshaler is the interface implemented by types that can unmarshal COM object of themselves.

N.B. Unmarshaler currently can't be implemented to non structure types!

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL