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

时间:2016-06-13 04:06:57

标签: windows dll go com

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

这是一个DLL查看器图片

我想使用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(类型* syscall.LazyProc没有字段或方法ConnectServer)

2 个答案:

答案 0 :(得分:2)

我的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
);

这意味着您必须将这三个参数传递给proc.Call uintptr s(Call期望所有参数都为uintptr s。

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对象都支持QueryInterfaceAddRefRelease函数,然后是其他类型特定的方法。

假设你的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,
    )
}

答案 1 :(得分:0)

https://github.com/go-adsi/adsi库具有ADSI COM对象的实现。它使用go-ole和comutil库。 对于其他COM对象用例来说,这可能是一个很好的起点。