无法将C语言DLL导入C#项目的问题排查
C#调用原生C DLL的常见问题解决
核心错误原因
你尝试添加引用的source.dll是原生C语言编译的动态链接库,它既不是.NET程序集,也不是COM组件,所以Visual Studio的「添加引用」功能无法识别它,这就是报错的直接原因。
正确的操作步骤
1. 移除无效的引用操作
立刻删掉你在C#项目中添加的source.dll引用,不要再通过「添加引用」的方式导入这个文件。
2. 确保DLL能被C#程序找到
把编译好的source.dll放到以下任意一个位置:
- 直接复制到C#项目的输出目录(比如
bin/Debug/net6.0、bin/Release/net6.0); - 将DLL添加到C#项目中,右键点击DLL文件→属性→「复制到输出目录」选择「如果较新则复制」;
- 在
[DllImport]特性中指定DLL的完整绝对路径,例如:[DllImport(@"C:\Projects\MyCProject\output\source.dll")]
3. 正确映射C结构体到C#
因为你的C函数用到了t_myStruct,必须在C#中定义内存布局完全一致的结构体,否则会出现内存访问错误:
- 用
[StructLayout(LayoutKind.Sequential)]保证结构体字段顺序和C一致; - 字段类型严格对应,必要时用
[MarshalAs]指定非托管类型的转换规则。
示例:
C端结构体:
typedef struct { int user_id; char username[32]; double score; } t_myStruct;
对应的C#结构体:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] struct MyStruct { public int user_id; // 对应C的char[32]数组 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string username; public double score; }
4. 确保[DllImport]的函数签名完全匹配
- 函数名必须和C DLL导出的名称一致(因为你加了
extern "C",不会有C++名字粉碎,所以直接用原函数名即可); - 指定正确的调用约定:C语言默认使用
cdecl调用约定,必须在[DllImport]中声明,否则会导致栈溢出; - 参数和返回值类型严格对应,指针类型在C#中用
ref/out(对应值类型指针)或IntPtr(对应void*)。
示例:
C端导出函数:
extern "C" __declspec(dllexport) void FuncA(t_myStruct* input, int* result) { // 函数实现 }
对应的C#声明:
[DllImport("source.dll", CallingConvention = CallingConvention.Cdecl)] static extern void FuncA(ref MyStruct input, ref int result);
5. 验证DLL的导出函数是否正确
可以用VS自带的dumpbin工具检查DLL是否正确导出了目标函数:
- 打开Visual Studio的「开发者命令提示符」;
- 切换到
source.dll所在目录; - 执行命令:
dumpbin /exports source.dll - 查看输出结果,确认
FuncA、FuncB出现在导出函数列表中。
额外注意事项
- 平台一致性:如果GCC编译的是32位DLL,C#项目必须设置为
x86平台;如果是64位DLL,C#项目设置为x64,不要用Any CPU; - 字符集匹配:如果C函数使用宽字符(
wchar_t*),C#结构体要设置CharSet = CharSet.Unicode; - 错误处理:原生DLL抛出的异常无法直接在C#中捕获,建议在C函数中返回错误码,由C#处理。
内容的提问来源于stack exchange,提问作者Erminio Ottone
相关产品推荐
相关产品推荐

