Windows – 如何在Golang中使用COM(组件对象模型)

我有一个
Windows DLL(XA_Session.dll)文件,但我不知道如何在golang中使用它.

这是一个DLL Viewer图片

我想使用ConnectServer COM方法.

这是我的代码

package main

import (
    "syscall"
    "fmt"
)

var (
    mod = syscall.NewLazyDLL("XA_Session.dll")
    proc = mod.NewProc("DllGetClassObject")
)

func main() {
    var bConnect bool
    bConnect = proc.ConnectServer("hts.ebestsec.co.kr", 20001)

    if bConnect {
        fmt.Println("Success")
    } else {
        fmt.Println("Fail")
    }
}

编译错误:

.\main.go:17: proc.ConnectServer undefined (type *syscall.LazyProc has no field or method ConnectServer)

最佳答案 我在
Direct3D9 Go wrapper中遇到了类似的问题,见
this thread,我可以从纯Go调用DirectX COM函数.

在您的代码中,您尝试调用proc.ConnectServer(…),但调用syscall.LazyProc的方法是使用其Call函数.看documentation for DllGetClassObject,签名是

HRESULT __stdcall DllGetClassObject(
  _In_  REFCLSID rclsid,
  _In_  REFIID   riid,
  _Out_ LPVOID   *ppv
);

这意味着您必须将这三个参数作为uintptrs传递给proc.Call(Call期望所有参数都是uintptrs).

package main

import "syscall"

var (
    xaSession      = syscall.NewLazyDLL("XA_Session.dll")
    getClassObject = xaSession.NewProc("DllGetClassObject")
)

func main() {
    // TODO set these variables to the appropriate values
    var rclsid, riid, ppv uintptr
    ret, _, _ := getClassObject.Call(rclsid, riid, ppv)
    // ret is the HRESULT value returned by DllGetClassObject, check it for errors
}

请注意,您需要正确设置参数值,CLSID和IID可能包含在库的附带C头文件中,我不知道这个XA_Session库.

在这种情况下,ppv将是指向您创建的COM对象的指针.要使用Go中的COM方法,您可以创建包装类型,前提是您知道它定义的所有COM方法及其正确的顺序.所有COM对象都支持QueryInterface,AddRef和Release函数,然后支持其他类型特定的方法.

假设您的XA_Session对象还支持这两个函数(同样,我不知道它真正支持的是什么,你必须查看它)

int ConnectServer(int id)
DisconnectServer()

那么你可以做什么把它包装在Go中如下:

package xasession

import (
    "syscall"
    "unsafe"
)

// NewXASession casts your ppv from above to a *XASession
func NewXASession(ppv uintptr) *XASession {
    return (*XASession)(unsafe.Pointer(ppv))
}

// XASession is the wrapper object on which to call the wrapper methods.
type XASession struct {
    vtbl *xaSessionVtbl
}

type xaSessionVtbl struct {
    // every COM object starts with these three
    QueryInterface uintptr
    AddRef         uintptr
    Release        uintptr
    // here are all additional methods of this COM object
    ConnectServer    uintptr
    DisconnectServer uintptr
}

func (obj *XASession) AddRef() uint32 {
    ret, _, _ := syscall.Syscall(
        obj.vtbl.AddRef,
        1,
        uintptr(unsafe.Pointer(obj)),
        0,
        0,
    )
    return uint32(ret)
}

func (obj *XASession) Release() uint32 {
    ret, _, _ := syscall.Syscall(
        obj.vtbl.Release,
        1,
        uintptr(unsafe.Pointer(obj)),
        0,
        0,
    )
    return uint32(ret)
}

func (obj *XASession) ConnectServer(id int) int {
    ret, _, _ := syscall.Syscall(
        obj.vtbl.ConnectServer, // function address
        2, // number of parameters to this function
        uintptr(unsafe.Pointer(obj)), // always pass the COM object address first
        uintptr(id), // then all function parameters follow
        0,
    )
    return int(ret)
}

func (obj *XASession) DisconnectServer() {
    syscall.Syscall(
        obj.vtbl.DisconnectServer,
        1,
        uintptr(unsafe.Pointer(obj)),
        0,
        0,
    )
}
点赞