编写要从C#调用的C ++吗? [英] Writing C++ intended to be called from C#?

查看:83
本文介绍了编写要从C#调用的C ++吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我这样做是为了学习,我不怕说我不知道​​我在这里做什么.值得一提的是,在这种情况下,我对C ++不太了解.

So I am doing this as a learning moment and I'm not afraid to say I have no idea what I'm doing here. It might also be worth mentioning that I don't know much about C++ in this scenario.

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

In C#, I've used DllImport plenty of times to bring in stuff from user32.dll or other DLLs that I haven't written, but I'm looking to better understand how the other half (the C++ half) is implemented to make this happen.

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

The C++ code I have is simple and just to verify that the call went through successfully:

#include <iostream>

using namespace std;

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

我不知道__declspec(dllexport)的重要性是什么,但是我已经在夫妇 网站与其重要性无关.

I don't know what the importance of __declspec(dllexport) is, but I've seen it on a couple websites that didn't touch much on its importance.

我的C#与以前做过的DllImport并没有太大不同:

My C# isn't very different than previous DllImports I've done before:

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

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

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

I'm compiled the C++ DLL and put it in the C# project and it's copied to the bin folder. When I run the C# project I get an EntryPointNotFoundException at the call to HelloWorld() inside the main function.

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

My guess is that I need to either change the C++ code or the compilation flags of the C++ project. Currently "Use of MFC" is set to "Use Standard Windows Libraries" and there's no use of ATL or CLR. Any help would be greatly appreciated.

推荐答案

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

C++ is a language that supports overloading. In other words, you can have more than one version of HelloWorld(). You could also export HelloWorld(int), a distinct version. It is also a language that requires a linker. In order to not confuzzle the linker about the same name for different functions, the compiler decorates the name of the function. Aka "name mangling".

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

The tool you want to use to troubleshoot problems like this is Dumpbin.exe. Run it from the Visual Studio Command Prompt on your DLL with the /exports option. You'll see this:

ordinal hint RVA      name

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

一堆gobbledegook,导出的名称显示在括号中.注意?在名称的前面和@@ YAXXZ后面,这就是CLR无法找到导出函数的原因.带有int参数的函数将导出为?HelloWorld @@ YAXH @ Z(尝试).

Bunch of gobbledegook, the exported name is shown in parentheses. Note the ? in the front and @@YAXXZ after the name, that's why the CLR cannot find the exported function. A function that takes an int argument will be exported as ?HelloWorld@@YAXH@Z (try it).

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

The [DllImport] directive supports this, you can use EntryPoint property to give the exported name. Or you can tell the C++ compiler that it should generate code that a C compiler can use. Put extern "C" in front of the declaration and the C++ compiler will suppress the name decoration. And won't support function overloads anymore of course. Dumpbin.exe now shows this:

ordinal hint RVA      name

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

请注意,该名称不是静态的"HelloWorld",而是 still ,名称前面有一个下划线.这是一种装饰,有助于发现调用约定中的错误.在32位代码中,有5种不同的方法来调用函数. DLL,__ cdecl,__ stdcall和__thiscall这三个是DLL共有的.对于常规的免费函数,C ++编译器默认为__cdecl.

Note that the name is still not plain "HelloWorld", there's an underscore in front of the name. That's a decoration that helps catch mistakes with the calling convention. In 32-bit code there are 5 distinct ways to call a function. Three of which are common with DLLs, __cdecl, __stdcall and __thiscall. The C++ compiler defaults to __cdecl for regular free functions.

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

This is also a property of the [DllImport] attribute, the CallingConvention property. The default that's used if it isn't specified is CallingConvention.StdCall. Which matches the calling convention for many DLLs, particularly the Windows ones, but doesn't match the C++ compiler's default so you still have a problem. Simply use the property or declare your C++ function like this:

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

Dumpbin.exe的输出现在看起来像:

And the Dumpbin.exe output now looks like:

ordinal hint RVA      name

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

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

Note the added @0, it describes the size of the stack activation frame. In other words, how many bytes worth of arguments are passed. This helps catch a declaration mistake at link time, such mistakes are extremely difficult to diagnose at runtime.

现在您可以像最初使用的那样使用[DllImport]属性,pinvoke编组器非常聪明,可以整理出实际功能的装饰.您可以使用ExactSpelling和EntryPoint属性来帮助它,它会更快一些,但您不会注意到.

You can now use the [DllImport] attribute as you originally had it, the pinvoke marshaller is smart enough to sort out the decoration of the actual function. You can help it with the ExactSpelling and EntryPoint properties, it will be slightly quicker but nothing you'd ever notice.

最后一个问题:__declspec(dllexport)只是向编译器提示,您打算从DLL导出函数.它将产生一些额外的代码,这些代码可以帮助使导出的函数调用更快(CLR不使用任何东西).并将指令传递给链接器该功能需要导出.导出功能也可以使用.def文件来完成,但这很难做到.

First question last: __declspec(dllexport) is just a hint to the compiler that you intend to export the function from a DLL. It will generate a wee bit of extra code that can help making the exported function call faster (nothing the CLR uses). And passes an instruction to the linker that the function needs to be exported. Exporting functions can also be done with a .def file but that's doing it the hard way.

这篇关于编写要从C#调用的C ++吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆