问题描述

我有一个 Windows DLL (XA_Session.dll) 文件,但我不知道如何在 golang 中使用它.

I have a Windows DLL (XA_Session.dll) file but I don't know how to use it in golang.

这是一个 DLL 查看器图片

This is a DLL Viewer picture

ConnectServer
ConnectServer

这是我的代码

package main

import (
    "syscall"
    "fmt"
)

var (
    mod = syscall.NewLazyDLL("XA_Session.dll")
    proc = mod.NewProc("DllGetClassObject")
)

func main() {
    var bConnect bool
    bConnect = proc.ConnectServer("hts.ebestsec.co.kr", 20001)

    if bConnect {
        fmt.Println("Success")
    } else {
        fmt.Println("Fail")
    }
}

编译错误:

.main.go:17: proc.ConnectServer 未定义(类型 *syscall.LazyProc 没有字段或方法 ConnectServer)

.main.go:17: proc.ConnectServer undefined (type *syscall.LazyProc has no field or method ConnectServer)

推荐答案

我的 Direct3D9 Go 包装器中遇到了类似的问题,请参阅我所在的 这个线程能够从纯 Go 调用 DirectX COM 函数.

I had a similar problem in my Direct3D9 Go wrapper, see this thread, where I was able to call DirectX COM functions from pure Go.

proc.ConnectServer(...)syscall.LazyProcCall功能.查看 DllGetClassObject 文档,签名是

In your code you try to call proc.ConnectServer(...) but the way to call a syscall.LazyProc is with its Call function. Looking at the documentation for DllGetClassObject, the signature is

HRESULT __stdcall DllGetClassObject(
  _In_  REFCLSID rclsid,
  _In_  REFIID   riid,
  _Out_ LPVOID   *ppv
);

这意味着您必须将这三个参数作为 uintptr 传递给 proc.Call(Call 期望所有参数都是 uintptrs).

This means you have to pass these three parameters to proc.Call as uintptrs (Call expects all arguments to be uintptrs).

package main

import "syscall"

var (
    xaSession      = syscall.NewLazyDLL("XA_Session.dll")
    getClassObject = xaSession.NewProc("DllGetClassObject")
)

func main() {
    // TODO set these variables to the appropriate values
    var rclsid, riid, ppv uintptr
    ret, _, _ := getClassObject.Call(rclsid, riid, ppv)
    // ret is the HRESULT value returned by DllGetClassObject, check it for errors
}

注意,需要正确设置参数值,CLSID和IID可能包含在库的随附C头文件中,我不知道这个XA_Session库.

Note that you need to set the parameter values correctly, the CLSID and IID may be contained in the accompanying C header file for the library, I don't know this XA_Session library.

在这种情况下,ppv 将是指向您创建的 COM 对象的指针.要使用 Go 中的 COM 方法,您可以创建包装器类型,前提是您知道它定义的所有 COM 方法及其正确顺序.所有 COM 对象都支持 QueryInterfaceAddRefRelease 函数,然后是附加的、类型特定的方法.

The ppv will in this case be a pointer to the COM object that you created. To use COM methods from Go, you can create wrapper types, given you know all the COM methods defined by it and their correct order. All COM objects support the QueryInterface, AddRef and Release functions and then additional, type specific methods.

假设您的 XA_Session 对象还支持这两个功能(再次,我不知道它真正支持什么,您必须查找)

Let's say your XA_Session object additionally supports these two functions (again, I don't know what it really supports, you have to look that up)

int ConnectServer(int id)
DisconnectServer()

那么你可以在 Go 中封装它:

then what you can do to wrap that in Go is the following:

package xasession

import (
    "syscall"
    "unsafe"
)

// NewXASession casts your ppv from above to a *XASession
func NewXASession(ppv uintptr) *XASession {
    return (*XASession)(unsafe.Pointer(ppv))
}

// XASession is the wrapper object on which to call the wrapper methods.
type XASession struct {
    vtbl *xaSessionVtbl
}

type xaSessionVtbl struct {
    // every COM object starts with these three
    QueryInterface uintptr
    AddRef         uintptr
    Release        uintptr
    // here are all additional methods of this COM object
    ConnectServer    uintptr
    DisconnectServer uintptr
}

func (obj *XASession) AddRef() uint32 {
    ret, _, _ := syscall.Syscall(
        obj.vtbl.AddRef,
        1,
        uintptr(unsafe.Pointer(obj)),
        0,
        0,
    )
    return uint32(ret)
}

func (obj *XASession) Release() uint32 {
    ret, _, _ := syscall.Syscall(
        obj.vtbl.Release,
        1,
        uintptr(unsafe.Pointer(obj)),
        0,
        0,
    )
    return uint32(ret)
}

func (obj *XASession) ConnectServer(id int) int {
    ret, _, _ := syscall.Syscall(
        obj.vtbl.ConnectServer, // function address
        2, // number of parameters to this function
        uintptr(unsafe.Pointer(obj)), // always pass the COM object address first
        uintptr(id), // then all function parameters follow
        0,
    )
    return int(ret)
}

func (obj *XASession) DisconnectServer() {
    syscall.Syscall(
        obj.vtbl.DisconnectServer,
        1,
        uintptr(unsafe.Pointer(obj)),
        0,
        0,
    )
}

这篇关于如何在 Golang 中使用 COM(组件对象模型)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!