我在VS中创建了新的Win32项目,并为此目的选择了动态库(* .dll)。
我在主文件中定义了一些导出功能:
__declspec(dllexport)
int TestCall(void)
{
int value = 4 / 2;
std::cout << typeid(value).name() << std::endl;
return value;
}
__declspec(dllexport)
void SwapMe(int *first, int *second)
{
int tmp = *first;
*first = *second;
*second = tmp;
}
当我看到dumpin / exports时,我得到了:
ordinal hint RVA name
1 0 00001010 ?SwapMe@@YAXPEAH0@Z
2 1 00001270 ?TestCall@@YAHXZ
我正在调用C#这样的版本:
[DllImport(@"lib1.dll", EntryPoint = "?TestCall@@YAHXZ",
CallingConvention = CallingConvention.Cdecl)]
static extern int TestCall();
这不是使用导出方法的正确形式。我在C ++ DLL项目中为export-methods生成这样的名称失败了吗?
答案 0 :(得分:5)
这是正常的,C ++编译器将名称修饰应用于函数。 C ++语言支持函数重载,就像C#一样。因此,您可以编写Foo(int)
和Foo(double)
函数。显然,它们不能同时作为名为“Foo”的函数导出,客户端代码不知道要调用哪一个。因此额外的字符对名称进行编码,使其对于重载是唯一的。
您可以通过声明函数extern "C"
来关闭它,C语言不支持重载,因此不需要相同类型的装饰。
但如果你不这样做,那实际上会更好。因为它也是捕捉错误的绝佳方式。就像更改C ++代码中的函数声明但忘记修改C#代码中的pinvoke声明一样。现在,您将可以轻松诊断“未找到Entrypoint”异常,而不是非描述性且非常难以诊断AccessViolationException。这不一定要在C ++代码中引发,堆栈不平衡也会导致C#代码崩溃。查找装饰名称有点痛苦,但通过要求链接器创建映射文件(/ MAP选项)来改进它。
答案 1 :(得分:2)
使用extern“C”指定链接以避免名称错位:
extern "C" __declspec(dllexport) int TestCall(void);
extern "C" __declspec(dllexport) void SwapMe(int *first, int *second);