Go调用DLL

Go的标准包syscall提供了调用DLL所需的API。本例用到的API如下(仅列出重要部分):

// LoadDLL 和 MustLoadDLL 根据DLL名字 name 来加载DLL,返回值为 syscall.DLL 结构体指针。
// LoadDLL 返回 error 表示错误。MustLoadDLL 错误时调用 panic
func LoadDLL(name string) (*DLL, error)
func MustLoadDLL(name string) *DLL

// syscall.DLL 结构体表示 DLL 抽象
type DLL struct {
    // ...
}
// FindProc 和 MustFindProc 根据函数名字 name 加载 DLL 中的函数
// 返回值为 syscall.Proc 结构体指针。
// FindProc 返回 eror 表示错误。MustFindProc 错误时调用 panic
func (d *DLL) FindProc(name string) (proc *Proc, err error)
func (d *DLL) MustFindProc(name string) *Proc

// syscall.Proc 结构体表示 DLL 中的函数
type Proc struct {
    // ...
}
// Call 方法调用 syscall.Proc 表示的函数,参数为 uintptr 切片,返回值为 r1, r2 和 error
// C 函数只能有一个返回值,因此r2用不到。
// error表示错误,该错误永远不为nil,它是由GetLastError()构成的
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error)

// 将字符串转换为byte指针
func StringBytePtr(s string) *byte

下面是一个完整的例子:

首先实现一个DLL,定义了两个函数,一个从Go接收参数,一个返回参数给Go:

// test.h
#ifndef TEST_H
#define TEST_H

#ifdef TEST_DLL_EXPORT
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif

TEST_API void greet(char *name);

TEST_API char *name();

#endif
// test.c
#define TEST_DLL_EXPORT
#include "test.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void greet(char *name)
{
    printf("Hello, %s!\n", name);
}

char *name()
{
    char buf[] = "Gopher";
    char *n = malloc(strlen(buf) + 1);
    strcpy(n, buf);
    n[strlen(buf)] = '\0';
    return n;
}

下面是Go调用DLL的代码:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

func main() {
    dll := syscall.MustLoadDLL("test.dll")

    procGreet := dll.MustFindProc("greet")
    procGreet.Call(uintptr(unsafe.Pointer(syscall.StringBytePtr("Cynhard"))))

    procName := dll.MustFindProc("name")
    r, _, _ := procName.Call()
    // 获取C返回的指针。
    // 注意C返回的r为char*,对应的Go类型为*byte
    p := (*byte)(unsafe.Pointer(r))
    // 定义一个[]byte切片,用来存储C返回的字符串
    data := make([]byte, 0)
    // 遍历C返回的char指针,直到 '\0' 为止
    for *p != 0 {
        data = append(data, *p)  // 将得到的byte追加到末尾
        r += unsafe.Sizeof(byte(0))  // 移动指针,指向下一个char
        p = (*byte)(unsafe.Pointer(r))  // 获取指针的值,此时指针已经指向下一个char
    }
    name := string(data)  // 将data转换为字符串

    fmt.Printf("Hello, %s!\n", name)
}

注意从C中返回的指针是由malloc动态分配的,Go中不会对此指针进行引用计数,不会被垃圾回收,因此会造成内存泄漏。解决方法是在DLL中提供释放资源的接口,在Go中调用此接口。

注:本例中用到的低级编程知识请见:Go低级编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值