C#封送带C接口的C++ DLL:传递对象时遇入口点未找到问题
问题:C#调用C++ DLL时遭遇EntryPointNotFoundException错误
我尝试封装一个带C接口的C++测试DLL,这个DLL用CFFI从Python调用完全正常,但在C#里调用时遇到了问题。
我的C++ DLL代码:
#define AS_TYPE(Type, Obj) reinterpret_cast<Type *>(Obj) #define AS_CTYPE(Type, Obj) reinterpret_cast<const Type *>(Obj) struct mytype_s; typedef struct mytype_s mytype_t; namespace TestDLL { class Test { public: Test() { return; } ~Test() { return; } int test_function() const { return 314159; } }; } mytype_t *test_new() { return AS_TYPE(mytype_t, new TestDLL::Test()); } int test_function(const mytype_t *mytype) { return AS_CTYPE(TestDLL::Test, mytype)->test_function(); }
我的C#调用代码:
[DllImport(dllPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr test_new(); [DllImport(dllPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern int test_function(IntPtr mytype); IntPtr mytype; mytype = test_new(); Console.WriteLine("Alloc complete"); int result; result = test_function(mytype); Console.WriteLine("test: {0}", result);
遇到的错误:
Alloc complete Unhandled Exception: System.EntryPointNotFoundException: Unable to find an entry point named 'test_function' in DLL
我想知道这是因为mytype_t的封送方式不对,还是C++到C#传递对象时有更复杂的问题?
解答:
这个问题和mytype_t的封送方式完全无关,核心原因是C++编译器的名字修饰(Name Mangling)导致的。
C为了支持函数重载,会给导出的函数名称加上额外的修饰信息,而C语言不会做这件事。你的代码里虽然写了C风格的函数接口,但编译器还是会把它们当成C函数处理,导致导出的函数名不再是test_function,而是一串被修饰后的名字(比如类似?test_function@@YAHPBUsmytype_s@@@Z这样的),这就导致C#找不到对应的入口点了。
解决方法很简单,你需要告诉编译器把这些导出函数按照C语言的规则来处理:
修改后的C++代码:
在导出的C风格函数外面加上extern "C"块:
#define AS_TYPE(Type, Obj) reinterpret_cast<Type *>(Obj) #define AS_CTYPE(Type, Obj) reinterpret_cast<const Type *>(Obj) struct mytype_s; typedef struct mytype_s mytype_t; namespace TestDLL { class Test { public: Test() { return; } ~Test() { return; } int test_function() const { return 314159; } }; } // 用extern "C"包裹C接口函数,避免名字修饰 extern "C" { mytype_t *test_new() { return AS_TYPE(mytype_t, new TestDLL::Test()); } int test_function(const mytype_t *mytype) { return AS_CTYPE(TestDLL::Test, mytype)->test_function(); } }
另外,你还可以检查一下你的DLL导出表,确认函数名是否正确——可以用工具比如dumpbin /exports yourdll.dll(Windows SDK自带)来查看导出的函数名称,这样就能直观看到名字是否被修饰了。
至于你担心的mytype_t封送问题,其实你的处理是对的:因为mytype_t在C#里只是一个不透明的指针,用IntPtr来传递完全没问题,不需要做额外的封送处理。
内容的提问来源于stack exchange,提问作者port80
相关产品推荐
相关产品推荐

