从 Go 调用 COM 对象方法,无需使用 CGo

gon*_*utz 6 windows com dll go

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

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

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

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

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

gon*_*utz 5

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

这是解决方案:

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())
}
Run Code Online (Sandbox Code Playgroud)

(c) 马特恩