You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用Go创建含回调函数的C共享库?程序崩溃问题求助

Go创建带回调的C共享库问题解决方案

可以用Go创建包含回调函数的C共享库,但你的代码因跨语言回调类型不匹配和调用传递方式错误导致崩溃,以下是问题分析和修正方案:

问题根源

  1. 回调类型不兼容:DLL中直接用Go的func(*C.char)接收回调,而从Loader传递的是C风格函数指针,两者内存布局和调用机制不同,直接调用会触发无效内存访问(报错地址0xffffffffffffffff就是典型的无效指针访问)。
  2. 指针传递错误:Loader中直接将C.HelloTo转为uintptr传递,没有按照C函数指针的要求进行正确转换,导致DLL拿到的指针无效。

修正后的代码

1. 共享库(test.dll)代码

package main

/*
// 定义C标准的回调函数类型
typedef void (*HelloCallback)(char*);
*/
import "C"
import "unsafe"

func main() {}

//export CALL_BACK
func CALL_BACK(f C.HelloCallback) {
    str := C.CString("FDUTCH")
    defer C.free(unsafe.Pointer(str)) // 必须释放C字符串,避免内存泄漏
    f(str)
}

2. 加载程序(loader.exe)代码

package main

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

/*
typedef void (*HelloCallback)(char*);
void HelloTo(char* name);
*/
import "C"

//export HelloTo
func HelloTo(name *C.char) {
    fmt.Printf("Hello To : %s !!!\n", C.GoString(name))
}

func main() {
    dll, err := syscall.LoadDLL("test.dll")
    if err != nil {
        log.Fatalln(err)
    }
    defer dll.Release() // 退出前释放DLL资源

    proc, err := dll.FindProc("CALL_BACK")
    if err != nil {
        log.Fatalln(err)
    }

    // 将Go导出函数转为C标准回调指针
    callback := C.HelloCallback(C.HelloTo)
    _, _, errno := proc.Call(uintptr(unsafe.Pointer(&callback)))
    if errno != 0 {
        log.Fatalln("CALL_BACK调用失败:", errno)
    }
}

关键注意事项

  • 必须用C风格回调类型:在cgo的注释块中定义typedef,让Go和C双方都识别统一的回调指针格式,不能直接用Go原生函数类型跨语言传递。
  • 内存管理:C.CString分配的内存属于C堆,必须用C.free手动释放,否则会造成内存泄漏。
  • 指针转换规范:传递回调指针时,需要用unsafe.Pointer做类型转换,确保符合C语言的指针传递规则。

内容的提问来源于stack exchange,提问作者FDUTCH

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 01:50:07