如何使用Golang构建带有回调函数的C-shared库DLL

如何使用Golang构建带有回调函数的C-shared库DLL 你好! 我尝试用Go创建一个库(dll),并接收来自其他语言的函数作为回调使用。

package main

import "C"
import "fmt"

type externFunc func(int)

//export Connect
func Connect(callback externFunc) {
    fmt.Println("cb ",callback)

    for i:= 0; i<3;i++{

       // 这是从C#接收的函数/方法,并尝试从Go中执行
       callback(i)
    }
}

这是运行我的应用程序时出现的错误。

错误
unexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x627ca9ab]

接收回调的正确方法是什么?


更多关于如何使用Golang构建带有回调函数的C-shared库DLL的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于如何使用Golang构建带有回调函数的C-shared库DLL的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中创建C-shared库并处理回调函数时,需要遵循C的调用约定。你的代码有几个关键问题需要修正:

  1. 回调类型定义:必须使用C类型来定义回调函数签名
  2. 函数导出:需要正确的导出声明
  3. 内存安全:避免Go的垃圾回收影响C函数指针

以下是修正后的代码:

package main

/*
#include <stdint.h>

// 定义回调函数类型
typedef void (*callback_func)(int);
*/
import "C"
import (
	"fmt"
	"unsafe"
)

// 全局变量存储回调函数
var callback C.callback_func

//export Connect
func Connect(cb C.callback_func) {
	fmt.Println("Received callback from C#")
	
	// 存储回调函数避免被GC回收
	callback = cb
	
	// 通过C调用执行回调
	for i := 0; i < 3; i++ {
		C.callback_func(cb)(C.int(i))
	}
}

//export CallWithValue
func CallWithValue(value C.int) {
	if callback != nil {
		C.callback_func(callback)(value)
	}
}

//export SetCallback
func SetCallback(cb C.callback_func) {
	callback = cb
}

// 清理函数,避免内存泄漏
//export Cleanup
func Cleanup() {
	callback = nil
}

func main() {
	// 空main函数,C-shared库需要
}

编译命令:

go build -buildmode=c-shared -o mylib.dll

C#调用示例:

using System;
using System.Runtime.InteropServices;

class Program
{
    // 定义回调委托
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void CallbackDelegate(int value);
    
    // DLL导入
    [DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void Connect(CallbackDelegate callback);
    
    [DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void SetCallback(CallbackDelegate callback);
    
    [DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void CallWithValue(int value);
    
    [DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void Cleanup();
    
    // 回调函数实现
    public static void MyCallback(int value)
    {
        Console.WriteLine($"C# callback received: {value}");
    }
    
    static void Main()
    {
        // 创建委托实例
        CallbackDelegate callback = MyCallback;
        
        // 调用Go函数
        Connect(callback);
        
        // 或者分开设置和执行
        SetCallback(callback);
        CallWithValue(42);
        
        // 清理
        Cleanup();
    }
}

关键修正点:

  1. 使用C.callback_func类型而不是Go函数类型
  2. 通过C.callback_func(cb)(C.int(i))语法进行类型转换和调用
  3. 全局存储回调函数防止被垃圾回收
  4. 提供清理函数避免内存泄漏
  5. 确保C#侧使用[UnmanagedFunctionPointer(CallingConvention.Cdecl)]属性

错误原因分析:原始代码尝试直接调用Go类型的函数指针,这违反了C的调用约定,导致内存访问违规。必须通过C类型系统进行正确的类型转换和调用。

回到顶部