Skip to content

Commit

Permalink
Correct threading model used by wmi calls
Browse files Browse the repository at this point in the history
WbemScripting is apartment threaded according to its registry entries
so switch multi-threaded OLE to apartment threaded enabling the global
lock to be removed.

Correct missing CoUninitialize call when CoInitializeEx returns S_FALSE
as documented here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms695279(v=vs.85).aspx

Also:
* Cleanup OleError checking in Query.
* Handle nil return from CreateObject by returning ErrNilCreateObject
  before it causes a panic. This has been seen in rare cases in production
  when built under go v1.6.
  • Loading branch information
stevenh authored and gbrayut committed Jul 26, 2016
1 parent ece5bbb commit 04850ec
Showing 1 changed file with 12 additions and 13 deletions.
25 changes: 12 additions & 13 deletions wmi.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (
"runtime"
"strconv"
"strings"
"sync"
"time"

"github.com/go-ole/go-ole"
Expand All @@ -45,9 +44,14 @@ var l = log.New(os.Stdout, "", log.LstdFlags)

var (
ErrInvalidEntityType = errors.New("wmi: invalid entity type")
lock sync.Mutex
// ErrNilCreateObject is the error returned if CreateObject returns nil even
// if the error was nil.
ErrNilCreateObject = errors.New("wmi: create object returned nil")
)

// S_FALSE is returned by CoInitializeEx if it was already called on this thread.
const S_FALSE = 0x00000001

// 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)
Expand Down Expand Up @@ -119,28 +123,23 @@ func (c *Client) Query(query string, dst interface{}, connectServerArgs ...inter
return ErrInvalidEntityType
}

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

err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED)
err := ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED)
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 {
oleCode := err.(*ole.OleError).Code()
if oleCode != ole.S_OK && oleCode != S_FALSE {
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()
}
defer ole.CoUninitialize()

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

Expand Down

0 comments on commit 04850ec

Please sign in to comment.