一尘不染

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

go

我在Go中创建了Direct3D9包装器,该包装器使用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做同样的事情。一个仅包含相关部分的简单示例将大有帮助。


阅读 262

收藏
2020-07-02

共1个答案

一尘不染

我问go-ole的家伙,就像@kostix建议的那样。

解决方法如下:

d3d9通常没有COM vtbl。例如,它没有IDispatch接口。因此,您不能对d3d9使用go-ole。但是您可以通过编写所有接口来实现。

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)马特恩

2020-07-02