C#调用Go生成的DLL报错:找不到SampleMethod入口点
C#调用Go生成DLL时的入口点未找到问题
已验证的C DLL调用流程
- 编写C代码文件
cmdll.c,执行命令cl -LD cmdll.c编译生成Cmdll.dll - 编写C#代码文件
cm.cs调用该DLL,执行csc -platform:x64 cm.cs编译后运行正常
Go DLL调用尝试及异常
- 编写Go代码
lib.go,通过命令go build -ldflags="-s -w" -o lib.dll -buildmode=c-shared lib.go编译生成lib.dll和lib.h - 编写C代码
goDll.c,结合生成的文件执行命令gcc -shared -pthread -o goDll.dll goDll.c lib.dll -lWinMM -lntdll -lWS2_32编译得到goDll.dll - 修改C#代码调用
goDll.dll,编译运行时抛出异常:
未处理的异常: System.EntryPointNotFoundException: 无法在 DLL 'goDll.dll' 中找到名为 'SampleMethod' 的入口点。 在 MainClass.SampleMethod(Int32 i) 在 MainClass.Main()
问题原因及解决办法
核心原因
- GCC名字修饰:GCC编译C代码生成DLL时,默认会对函数名进行修饰(如添加前缀/后缀),导致C#调用时使用的原函数名与DLL实际导出的名字不匹配。
- 中间层未正确导出函数:如果
goDll.c作为中间转发层没有显式标记要导出的函数,C#无法识别到入口点。 - Go函数导出名不匹配:Go编译为c-shared时,导出的函数名会在
lib.h中声明,中间层C代码必须准确引用该名称,否则无法正确转发。
具体解决方案
1. 显式导出中间层函数
在goDll.c中使用__declspec(dllexport)标记要导出的函数,并通过extern "C"避免名字修饰(即使是C代码也适用):
#include "lib.h" // 确保函数名不被修饰并显式导出 extern "C" __declspec(dllexport) int SampleMethod(int i) { // 注意:这里要和lib.h中Go导出的函数名完全一致 return SampleMethod(i); }
2. 核对Go导出的函数名
打开生成的lib.h文件,确认Go代码中导出的函数实际名称。比如Go代码中如果写:
//export SampleMethod func SampleMethod(i int) int { return i * 2 }
lib.h中会生成对应的声明,中间层C代码必须准确调用这个名称。
3. 检查DLL导出表
用工具查看goDll.dll的实际导出函数名:
- VS环境下执行:
dumpbin /exports goDll.dll - GCC环境下执行:
nm -D goDll.dll
如果导出的是修饰后的名字(如_SampleMethod@4),需要在C#的DllImport中指定EntryPoint:
[DllImport("goDll.dll", EntryPoint = "_SampleMethod@4", CallingConvention = CallingConvention.Cdecl)] public static extern int SampleMethod(int i);
4. 统一编译架构
确保所有步骤都使用x64架构:
- Go编译时添加环境变量:
set GOARCH=amd64(Windows)或export GOARCH=amd64(Linux/WSL) - GCC编译时添加
-m64参数:gcc -m64 -shared -pthread -o goDll.dll goDll.c lib.dll -lWinMM -lntdll -lWS2_32 - C#编译保持
csc -platform:x64 cm.cs
内容的提问来源于stack exchange,提问作者Hasan A Yousef
相关产品推荐
相关产品推荐

