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

问题描述 投票:0回答:2

我在 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 做同样的事情。一个仅包含相关部分的简单示例会有很大帮助。

windows dll go com
2个回答
6
投票

我问了 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())
}

(c) 马特恩


4
投票

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

虽然 MSDN 大多数时候假设您使用某些具有内置“粘合剂”的平台(例如 Visual C++™ 产品或类似的东西)来处理 COM 对象,但实际上可以使用以下方法来处理 COM 对象:普通 C — 对于初学者来说,请查看 herehere

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

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


我还会直接进入

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

© www.soinside.com 2019 - 2024. All rights reserved.