【问题标题】:How to use COM (Component Object Model) in Golang如何在 Golang 中使用 COM(组件对象模型)
【发布时间】:2016-10-13 09:29:15
【问题描述】:

我有一个 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 未定义(类型 *syscall.LazyProc 没有字段或方法 ConnectServer)

【问题讨论】:

  • 您错误地使用了proc。它就像函数 DllGetClassObject 的包装器,而不是 DllGetClassObject 返回的类对象。它没有这种方法。
  • 我应该改变什么? @V.Kravchenko
  • 我猜想在 go 中使用 com 是相当困难的。尝试更深入地研究 com 并查看这里github.com/go-ole/go-ole
  • COM 与仅调用加载的 DLL 的导出函数不同,所以我支持@V.Kravchenko,您可能应该使用专用包来处理 COM。至于调用导出的函数,请查看您的 Go 源代码——特别是 src/syscall 目录下名称与 *_windows.go 匹配的文件:它们包含许多使用 NewProc()syscall.Syscall*() 函数的示例。
  • 因此,恐怕要将 C++ 编译器生成的 DLL 与 Go 实际接口,您需要将该 DLL 包装在另一个中 - 用 C++ 编写并公开一个普通的 C 接口(通过extern "C" { ... }" 标准机制)包装这些 C++ 方法或使用 SWIG 之类的工具为 Go 创建“桥”代码。 2) 使用 COM 时,您不直接加载提供 COM 对象的 DLL,而是使用适当的 Win32 API 调用间接为您实例化 COM 对象——通过其名称或 GUID 标识符。然后也间接操作它。这就是go-ole 所做的。

标签: windows dll go com


【解决方案1】:

我在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,因为uintptrs(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 对象都支持 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,
    )
}

【讨论】:

    【解决方案2】:

    https://github.com/go-adsi/adsi 库已实现 ADSI COM 对象。它使用 go-ole 和 comutil 库。 这可能是其他 COM 对象用例的良好起点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-09
      • 2012-08-28
      • 2013-08-23
      • 2011-07-26
      • 2012-11-10
      • 2016-10-13
      • 1970-01-01
      相关资源
      最近更新 更多