Go生成DLL后调用C回调函数参数异常问题求助
问题分析与解决
核心问题
你的问题根源在于Go与C的函数调用约定不匹配,以及函数指针的类型转换错误:
- C端使用了
__stdcall调用约定,但Go默认函数类型为cdecl,直接转换会导致参数栈处理错误,出现随机值。 - 传递函数指针时的类型映射错误,导致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
相关产品推荐
相关产品推荐

