在没有CGo的情况下从Go调用COM对象方法



我在Go中创建了一个Direct3D9包装器,它使用CGo与c中的COM对象进行接口。

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

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

我怎么能调用COM对象方法在一个DLL从纯Go没有CGo?

我知道go - ole库,它声明它调用没有CGo的COM接口,但我不能,从来源,看看我将如何为Direct3D9做同样的事情。一个简单的例子,只有相关的部分会有很大的帮助。

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

解决方案如下:

d3d9一般没有COM vtbl。例如,它没有IDispatch接口。所以你不能对d3d9使用go-ole。但是你可以这样做它用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

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

虽然MSDN大多数时候假设你使用一些为它们内置"胶水"的平台来处理COM对象(例如Visual c++ ™产品或类似的东西),事实上,使用普通的C&mdash可以处理COM对象;初学者可以在这里和这里查看。

研究这些资源,你可以了解到在"COM接口"上调用方法相当于正确地处理它的"VTBL"(V虚函数TaBLe)块,该块总是位于一个众所周知的位置,相对于由实例化COM对象的函数返回的"接口"东西"所掩盖的指针。

go-ole包在纯Go中实现了您在纯C中所做的事情,因此掌握了"在COM对象上调用方法我们需要对其VTBL进行操作"的知识,我们可以在该包中找到IDispatch支持的实现。所以我从这里开始。


我也会直接进入go-ole问题跟踪器,要求实现一段示例代码,该代码将显示如何调用COM对象处理程序上的方法,而不是从go-ole包调用函数。

最新更新