chore: release v0.21.3
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package cat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
ole "github.com/go-ole/go-ole"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
// Last-resort activation path for OmniRig, used only when the normal
|
||||
// CreateObject fails.
|
||||
//
|
||||
// OmniRig is a 32-bit program and its installer splits its COM identity across
|
||||
// registry views — on a working machine the ProgID sits in the 64-bit view while
|
||||
// the CLSID and its LocalServer32 exist only under WOW6432Node. That LOOKS like
|
||||
// it should break a 64-bit client, and it was my first theory for "OmniRig not
|
||||
// found"; measuring it on a machine with exactly that layout disproved it. COM
|
||||
// resolves an out-of-process server across views by itself, and the plain
|
||||
// CreateObject succeeds.
|
||||
//
|
||||
// What this still covers is the case where the ProgID is not visible to us at all
|
||||
// (a partial or 32-bit-only registration), where CreateObject has nothing to
|
||||
// resolve. Here we read the CLSID from BOTH views ourselves and activate the
|
||||
// 32-bit local server explicitly. CLSCTX_ACTIVATE_32_BIT_SERVER is the documented
|
||||
// flag for that; go-ole hard-codes CLSCTX_SERVER and keeps CoCreateInstance
|
||||
// unexported, hence the direct call.
|
||||
//
|
||||
// It costs nothing when the normal path works, and the reason it ran at all is
|
||||
// logged — so if it ever rescues a real installation we will see it.
|
||||
const clsctxActivate32BitServer = 0x40000
|
||||
|
||||
var (
|
||||
modole32 = syscall.NewLazyDLL("ole32.dll")
|
||||
procCoCreateInst32 = modole32.NewProc("CoCreateInstance")
|
||||
)
|
||||
|
||||
// omnirigCLSIDFromRegistry reads OmniRig's CLSID from the ProgID key, looking in
|
||||
// the 64-bit view first and then the 32-bit one. Returned as a GUID ready for
|
||||
// CoCreateInstance.
|
||||
func omnirigCLSIDFromRegistry(progID string) (*ole.GUID, error) {
|
||||
for _, access := range []uint32{registry.QUERY_VALUE, registry.QUERY_VALUE | registry.WOW64_32KEY} {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Classes\`+progID+`\CLSID`, access)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
s, _, err := k.GetStringValue("")
|
||||
k.Close()
|
||||
if err != nil || s == "" {
|
||||
continue
|
||||
}
|
||||
if g := ole.NewGUID(s); g != nil {
|
||||
return g, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no CLSID registered for %s in either registry view", progID)
|
||||
}
|
||||
|
||||
// createOmniRig32 activates OmniRig's 32-bit out-of-process server explicitly,
|
||||
// bypassing the 64-bit registry lookup that CoCreateInstance would otherwise do.
|
||||
func createOmniRig32(progID string) (*ole.IDispatch, error) {
|
||||
clsid, err := omnirigCLSIDFromRegistry(progID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var unk *ole.IUnknown
|
||||
hr, _, _ := procCoCreateInst32.Call(
|
||||
uintptr(unsafe.Pointer(clsid)),
|
||||
0, // no aggregation
|
||||
uintptr(ole.CLSCTX_LOCAL_SERVER|clsctxActivate32BitServer),
|
||||
uintptr(unsafe.Pointer(ole.IID_IUnknown)),
|
||||
uintptr(unsafe.Pointer(&unk)))
|
||||
if hr != 0 {
|
||||
return nil, fmt.Errorf("CoCreateInstance(32-bit local server) failed: %w", ole.NewError(hr))
|
||||
}
|
||||
disp, err := unk.QueryInterface(ole.IID_IDispatch)
|
||||
unk.Release()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query IDispatch: %w", err)
|
||||
}
|
||||
return disp, nil
|
||||
}
|
||||
Reference in New Issue
Block a user