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

Go生成DLL后调用C回调函数参数异常问题求助

问题分析与解决

核心问题

你的问题根源在于Go与C的函数调用约定不匹配,以及函数指针的类型转换错误:

  1. C端使用了__stdcall调用约定,但Go默认函数类型为cdecl,直接转换会导致参数栈处理错误,出现随机值。
  2. 传递函数指针时的类型映射错误,导致Go无法正确解析C端的回调函数。

正确实现方案

方案1:直接处理__stdcall函数指针

Go代码(main.go)

package main

import "unsafe"

// 定义与C端__stdcall回调匹配的函数类型
type CallbackFunc func(int32)

//export HandleDatagramCallback
func HandleDatagramCallback(cb unsafe.Pointer) {
    // 将unsafe.Pointer转换为对应的回调函数类型
    fn := *(*CallbackFunc)(cb)
    // 调用回调并传递参数
    fn(123)
}

func main() {} // 生成DLL必须保留main函数

C代码

#include <windows.h>
#include <iostream>
using namespace std;

// 定义与Go端匹配的__stdcall回调类型
typedef void(__stdcall* CallbackFn)(int);
// 定义Go导出函数的类型
typedef void(__stdcall* GoCallbackHandler)(CallbackFn);

void __stdcall NewCallback(int size) {
    cout << "callback: " << endl;
    cout << "size:" << size << endl; // 现在可正确接收参数
}

int main() {
    HINSTANCE dll = LoadLibrary("webtransport.dll");
    if (!dll) {
        cout << "load dll failed!" << endl;
        return 0;
    }

    GoCallbackHandler handleCallback = (GoCallbackHandler)GetProcAddress(dll, "HandleDatagramCallback");
    if (!handleCallback) {
        cout << "could not locate the function HandleDatagramCallback" << endl;
        return 0;
    }

    // 直接传递回调函数指针(函数名本身就是指针,无需取地址)
    handleCallback(NewCallback);

    FreeLibrary(dll);
    return 0;
}

编译命令

go build -buildmode=c-shared -o webtransport.dll main.go

方案2:使用结构体传递(修正原有方案)

如果需要传递更多参数,可通过结构体实现,但必须保证Go与C的结构体定义完全一致:

Go代码

package main

import "unsafe"

// 与C结构体字段顺序、类型完全匹配
type CallbackStruct struct {
    CallbackFn unsafe.Pointer
}

// 定义__stdcall回调类型
type CallbackFunc func(int32)

//export HandleDatagramCallback
func HandleDatagramCallback(cb unsafe.Pointer) {
    // 转换为结构体
    cs := (*CallbackStruct)(cb)
    // 转换函数指针并调用
    fn := *(*CallbackFunc)(cs.CallbackFn)
    fn(456)
}

func main() {}

C代码

#include <windows.h>
#include <iostream>
using namespace std;

typedef void(__stdcall* CallbackFn)(int);
typedef struct {
    CallbackFn callbackfn; // 字段名、顺序必须与Go一致
} CallbackStruct;

typedef void(__stdcall* GoCallbackHandler)(CallbackStruct*);

void __stdcall NewCallback(int size) {
    cout << "callback: " << endl;
    cout << "size:" << size << endl;
}

int main() {
    HINSTANCE dll = LoadLibrary("webtransport.dll");
    if (!dll) {
        cout << "load dll failed!" << endl;
        return 0;
    }

    GoCallbackHandler handleCallback = (GoCallbackHandler)GetProcAddress(dll, "HandleDatagramCallback");
    if (!handleCallback) {
        cout << "could not locate the function HandleDatagramCallback" << endl;
        return 0;
    }

    CallbackStruct cb;
    cb.callbackfn = NewCallback;
    handleCallback(&cb);

    FreeLibrary(dll);
    return 0;
}

关键注意事项

  • 调用约定一致:C端用__stdcall时,Go端转换的函数类型必须匹配,否则参数栈会混乱。
  • 函数指针传递:C中函数名本身就是指针,无需额外取地址(&NewCallback是多余操作)。
  • 结构体内存布局:Go与C的结构体必须字段顺序、类型完全一致,否则会出现内存解析错误。
  • 保留main函数:Go生成DLL时必须保留main函数,否则编译会失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:23:18