【问题标题】:Call COM object method from Go without CGo从没有 CGo 的 Go 调用 COM 对象方法
【发布时间】:2017-01-02 21:01:23
【问题描述】:

我在 Go 中创建了一个 Direct3D9 wrapper,它使用 CGo 与 C 中的 COM 对象进行交互。

我想摆脱对 Windows 下 C 编译器的依赖,这样用户就不必安装 MinGW 或 Cygwin 来使用 Go 中的 DirectX。

问题在于 d3d9.dll 不公开 C 函数,而是使用 COM。加载 DLL(使用syscall.LoadLibrary("d3d9.dll"))后可以直接调用的唯一函数是Direct3DCreate9。这将返回一个 COM 对象,该对象将所有功能公开为方法。

如何在没有 CGo 的纯 Go 中调用 DLL 中的 COM 对象方法?

我知道 Go-OLE 库,它声明它在没有 CGo 的情况下调用 COM 接口,但我无法从源代码中看到我将如何为 Direct3D9 做同样的事情。一个仅包含相关部分的简单示例会很有帮助。

【问题讨论】:

  • 嗯,Direct3DCreate9() 绝对是一个 C 函数。它是一个工厂函数,因为 DirectX 实际上并不使用 COM,所以需要它。其余部分肯定类似于 COM,这种interface-based programming 风格在微软很常见。它也不是 IDispatch 和go-ole 下的自动化样式。在没有图书馆帮助的情况下这样做,好吧,没有太多意义吗?

标签: windows dll go com


【解决方案1】:

我问了来自 go-ole 的人,就像 @kostix 建议的那样。

解决办法如下:

d3d9 通常没有 COM vtbl。例如,它没有 IDispatch 接口。所以你不能将 go-ole 用于 d3d9。但你可以做 它与在go中编写所有接口。

package main

import (
    "fmt"
    "log"
    "syscall"
    "unsafe"
)

const (
    D3D9_SDK_VERSION = 32
)

var (
    libd3d9             = syscall.NewLazyDLL("d3d9.dll")
    procDirect3DCreate9 = libd3d9.NewProc("Direct3DCreate9")
)

type IDirect3D struct {
    lpVtbl *IDirect3DVtbl
}

type IDirect3DVtbl struct {
    QueryInterface uintptr
    AddRef         uintptr
    Release        uintptr

    RegisterSoftwareDevice      uintptr
    GetAdapterCount             uintptr
    GetAdapterIdentifier        uintptr
    GetAdapterModeCount         uintptr
    EnumAdapterModes            uintptr
    GetAdapterDisplayMode       uintptr
    CheckDeviceType             uintptr
    CheckDeviceFormat           uintptr
    CheckDeviceMultiSampleType  uintptr
    CheckDepthStencilMatch      uintptr
    CheckDeviceFormatConversion uintptr
    GetDeviceCaps               uintptr
    GetAdapterMonitor           uintptr
    CreateDevice                uintptr
}

func (v *IDirect3D) AddRef() int32 {
    ret, _, _ := syscall.Syscall(
        v.lpVtbl.AddRef,
        1,
        uintptr(unsafe.Pointer(v)),
        0,
        0)
    return int32(ret)
}

func (v *IDirect3D) Release() int32 {
    ret, _, _ := syscall.Syscall(
        v.lpVtbl.Release,
        1,
        uintptr(unsafe.Pointer(v)),
        0,
        0)
    return int32(ret)
}

func (v *IDirect3D) GetAdapterCount() uint32 {
    ret, _, _ := syscall.Syscall(
        v.lpVtbl.GetAdapterCount,
        1,
        uintptr(unsafe.Pointer(v)),
        0,
        0)
    return uint32(ret)
}

func main() {
    v, r, err := procDirect3DCreate9.Call(uintptr(D3D9_SDK_VERSION))
    if r != 0 && err != nil {
        log.Fatal(err)
    }
    d3d := *((**IDirect3D)(unsafe.Pointer(&v)))

    d3d.AddRef()
    defer d3d.Release()

    fmt.Println(d3d.GetAdapterCount())
}

(c) mattn

【讨论】:

    【解决方案2】:

    (不是一个全面的答案,因为我没有时间实际测试这个,但仍然......)

    虽然 MSDN 大部分时间都假定您使用具有内置“胶水”的平台(例如 Visual C++™ 产品或类似产品)来处理 COM 对象,但实际上可以使用 COM使用纯 C 语言的对象——首先看herehere

    研究这些资源,您可以了解到在“COM 接口”上调用方法相当于正确使用其“VTBL”(V虚拟函数Ta BLe) 块,它总是位于一个众所周知的位置,相对于由实例化 COM 对象的函数返回的“接口”“事物”伪装的指针。

    go-ole 包在普通 Go 中实现了你在普通 C 中会做的事情,因此有了“在 COM 对象上调用方法,我们需要对其 VTBL 进行操作”的知识,我们可以找到实现IDispatch support 在那个包里。所以我会从那里开始。


    我还会直接进入go-ole 问题跟踪器,要求实现一段示例代码,该代码将展示如何调用通过从go-ole 包调用函数以外的方式获取的 COM 对象处理程序上的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-23
      • 2020-10-23
      • 1970-01-01
      • 2011-03-05
      • 2013-12-20
      • 2014-09-29
      • 2014-03-05
      相关资源
      最近更新 更多