1

我在我的 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;
}

当我查看转储/出口时,我得到了:

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 项目中的导出方法生成这样的名称失败了?

4

2 回答 2

6

这是正常的,C++ 编译器将名称修饰应用于函数。C++ 语言支持函数重载,就像 C# 一样。所以你可以写一个Foo(int)和一个Foo(double)函数。显然,它们不能都导出为名为“Foo”的函数,客户端代码不知道要调用哪一个。因此,额外的字符对名称进行编码,使其对于重载是唯一的。

您可以通过声明 function 来关闭它extern "C",C 语言不支持重载,因此不需要相同类型的装饰。

但实际上,如果你不这样做会更好。因为它也是捕捉错误的好方法。就像更改 C++ 代码中的函数声明但忘记修改 C# 代码中的 pinvoke 声明一样。您现在将获得一个易于诊断的“找不到入口点”异常,而不是一个非描述性且非常难以诊断的 AccessViolationException。不一定必须在 C++ 代码中提出,堆栈不平衡也可能使您的 C# 代码崩溃。然而,查找修饰名称有点痛苦,通过要求链接器创建一个映射文件(/MAP 选项)来改进它。

于 2012-12-29T20:14:57.337 回答
2

使用 extern "C" 指定链接以避免名称损坏:

extern "C" __declspec(dllexport) int TestCall(void);
extern "C" __declspec(dllexport) void SwapMe(int *first, int *second); 
于 2012-12-29T20:05:30.267 回答