在没有 CGo 的情况下从 Go 调用 COM 对象方法
Call COM object method from Go without CGo
我在 Go 中创建了一个 Direct3D9 wrapper,它使用 CGo 与 C 中的 COM 对象进行交互。
我想在 Windows 下摆脱对 C 编译器的依赖,这样用户就不必安装 MinGW 或 Cygwin 来使用 Go 中的 DirectX。
问题是 d3d9.dll 不公开 C 函数而是使用 COM。唯一可以在加载 DLL(带 syscall.LoadLibrary("d3d9.dll")
)后直接调用的函数是 Direct3DCreate9
。此 returns 一个将所有功能公开为方法的 COM 对象。
如何在没有 CGo 的情况下从纯 Go 调用 DLL 中的 COM 对象方法?
我知道 Go-OLE 库声明它在没有 CGo 的情况下调用 COM 接口,但我无法从源代码中了解我将如何为 Direct3D9 做同样的事情。一个只有相关部分的简单示例会有很大帮助。
(不是一个全面的答案,因为我没有时间实际测试这个,但仍然......)
虽然 MSDN 大部分时间都假设您使用某些内置 "glue" 的平台(例如 Visual C++™ 产品或类似产品)来处理 COM 对象,但实际上它是可能的使用纯 C 来处理 COM 对象——初学者请看 [=14=]。
研究这些资源,您可以了解到在 "COM interface" 上调用方法相当于正确使用其 "VTBL"(V 虚拟函数 TaBLe) 块总是位于相对于返回的 "interface" "thing" 伪装的指针的众所周知的位置通过实例化 COM 对象的函数。
go-ole
包在普通 Go 中实现了你在普通 C 中所做的事情,所以有了 "to call methods on a COM objects we need to operate on its VTBL" 的知识,我们可以在其中找到 IDispatch
support 的实现包裹。所以我会从这里开始。
我还会直接进入 go-ole
问题跟踪器,要求实现一段示例代码,该代码将显示如何在通过调用函数以外的方式获取的 COM 对象的处理程序上调用方法 go-ole
包。
我问了 go-ole 的人,就像@kostix 建议的那样。
解决方法如下:
d3d9 doesn't have generally COM vtbl. for example, it doesn't have
IDispatch interface. So you can't use go-ole for d3d9. But you can do
it with writing all interface in 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())
}
我在 Go 中创建了一个 Direct3D9 wrapper,它使用 CGo 与 C 中的 COM 对象进行交互。
我想在 Windows 下摆脱对 C 编译器的依赖,这样用户就不必安装 MinGW 或 Cygwin 来使用 Go 中的 DirectX。
问题是 d3d9.dll 不公开 C 函数而是使用 COM。唯一可以在加载 DLL(带 syscall.LoadLibrary("d3d9.dll")
)后直接调用的函数是 Direct3DCreate9
。此 returns 一个将所有功能公开为方法的 COM 对象。
如何在没有 CGo 的情况下从纯 Go 调用 DLL 中的 COM 对象方法?
我知道 Go-OLE 库声明它在没有 CGo 的情况下调用 COM 接口,但我无法从源代码中了解我将如何为 Direct3D9 做同样的事情。一个只有相关部分的简单示例会有很大帮助。
(不是一个全面的答案,因为我没有时间实际测试这个,但仍然......)
虽然 MSDN 大部分时间都假设您使用某些内置 "glue" 的平台(例如 Visual C++™ 产品或类似产品)来处理 COM 对象,但实际上它是可能的使用纯 C 来处理 COM 对象——初学者请看 [=14=]。
研究这些资源,您可以了解到在 "COM interface" 上调用方法相当于正确使用其 "VTBL"(V 虚拟函数 TaBLe) 块总是位于相对于返回的 "interface" "thing" 伪装的指针的众所周知的位置通过实例化 COM 对象的函数。
go-ole
包在普通 Go 中实现了你在普通 C 中所做的事情,所以有了 "to call methods on a COM objects we need to operate on its VTBL" 的知识,我们可以在其中找到 IDispatch
support 的实现包裹。所以我会从这里开始。
我还会直接进入 go-ole
问题跟踪器,要求实现一段示例代码,该代码将显示如何在通过调用函数以外的方式获取的 COM 对象的处理程序上调用方法 go-ole
包。
我问了 go-ole 的人,就像@kostix 建议的那样。
解决方法如下:
d3d9 doesn't have generally COM vtbl. for example, it doesn't have IDispatch interface. So you can't use go-ole for d3d9. But you can do it with writing all interface in 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())
}