gpt4 book ai didi

c# - 编写 C++ 旨在从 C# 调用?

转载 作者:搜寻专家 更新时间:2023-10-31 00:01:29 24 4
gpt4 key购买 nike

所以我这样做是为了学习,我敢说我不知道​​自己在做什么。可能还值得一提的是,我对这种情况下的 C++ 知之甚少。

在 C# 中,我已经多次使用 DllImport 从 user32.dll 或其他我未编写的 DLL 中引入内容,但我希望更好地理解另一个half(C++ half)的实现就是为了实现这一点。

我的 C++ 代码很简单,只是为了验证调用是否成功:

#include <iostream>

using namespace std;

__declspec(dllexport) void HelloWorld() {
cout << "Hello, World" << endl;
}

我不知道 __declspec(dllexport) 的重要性是什么,但我在 couple 上看到过它websites这并没有触及它的重要性。

我的 C# 与我以前完​​成的 DllImport 没有太大区别:

[DllImport("TestDLL.dll")]
static extern void HelloWorld();

static void Main(string[] args) {
HelloWorld();
}

我编译了 C++ DLL 并将其放入 C# 项目并将其复制到 bin 文件夹。当我运行 C# 项目时,我在调用主函数内的 HelloWorld() 时得到一个 EntryPointNotFoundException

我的猜测是我需要更改 C++ 代码或 C++ 项目的编译标志。当前“使用 MFC”设置为“使用标准 Windows 库”并且不使用 ATL 或 CLR。任何帮助将不胜感激。

最佳答案

C++ 是一种支持重载的语言。换句话说,您可以拥有多个版本的 HelloWorld()。您还可以导出一个不同的版本 HelloWorld(int)。它也是一种需要链接器的语言。为了不让链接器混淆不同函数的相同名称,编译器修饰函数的名称。又名“名称修改”。

要用来解决此类问题的工具是 Dumpbin.exe。使用/exports 选项从 DLL 上的 Visual Studio 命令提示符运行它。你会看到这个:

ordinal hint RVA      name

1 0 000110EB ?HelloWorld@@YAXXZ = @ILT+230(?HelloWorld@@YAXXZ)

一堆gobbledegook,导出的名称显示在括号中。注意 ?在前面,@@YAXXZ 在名字后面,这就是 CLR 找不到导出函数的原因。采用 int 参数的函数将导出为 ?HelloWorld@@YAXH@Z(尝试一下)。

[DllImport] 指令支持这一点,您可以使用 EntryPoint 属性给导出的名称。或者您可以告诉 C++ 编译器它应该生成 C 编译器可以使用的代码。将 extern "C" 放在声明的前面,C++ 编译器将抑制名称修饰。并且当然不再支持函数重载。 Dumpbin.exe 现在显示:

ordinal hint RVA      name

1 0 00011005 HelloWorld = @ILT+0(_HelloWorld)

请注意,名称​​仍然不是普通的“HelloWorld”,名称前面有一个下划线。这是一种有助于捕捉调用约定错误的装饰。在 32 位代码中,有 5 种不同的方法来调用函数。其中三个与 DLL 通用,__cdecl、__stdcall 和 __thiscall。对于常规自由函数,C++ 编译器默认为 __cdecl。

这也是 [DllImport] 属性的属性,即 CallingConvention 属性。如果未指定,则使用的默认值为 CallingConvention.StdCall。它与许多 DLL 的调用约定相匹配,尤其是 Windows 的调用约定,但与 C++ 编译器的默认值不匹配,因此您仍然遇到问题。只需使用该属性或像这样声明您的 C++ 函数:

extern "C" __declspec(dllexport) 
void __stdcall HelloWorld() {
// etc..
}

Dumpbin.exe 输出现在如下所示:

ordinal hint RVA      name

1 0 000110B9 _HelloWorld@0 = @ILT+180(_HelloWorld@0)

注意添加的@0,它描述了堆栈激活帧的大小。换句话说,传递了多少字节的参数。这有助于在链接时捕获声明错误,此类错误在运行时极难诊断。

您现在可以像原来那样使用 [DllImport] 属性,pinvoke 编码器足够聪明,可以整理出实际函数的装饰。您可以使用 ExactSpelling 和 EntryPoint 属性来帮助它,它会稍微快一些,但您不会注意到任何事情。

最后一个问题:__declspec(dllexport) 只是向编译器提示您打算从 DLL 导出函数。它将生成一小部分额外代码,有助于加快导出函数调用的速度(CLR 不使用任何代码)。并向链接器传递一条指令,说明该函数需要被导出。也可以使用 .def 文件导出函数,但这样做比较麻烦。

关于c# - 编写 C++ 旨在从 C# 调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9849541/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com