如何从 C# 调用 C++/CLI?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2211867/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I call C++/CLI from C#?
提问by master chief
I have a class implemented in C++ that's responsible for the arithmetic computation of the program, and an interface using WPF. I process the input with C# but then how can I use my C++ class?
我有一个用 C++ 实现的类,它负责程序的算术计算,以及一个使用 WPF 的接口。我使用 C# 处理输入,但是如何使用我的 C++ 类?
I've seen some comments about making a managed C++ wrapper class to interact with it, but I don't know where to start. Nor do I know how I'd go to compile it along with all the other code. I can't really find a tutorial on this, and the stuff google shows on managed C++ doesn't really seem helpful.
我已经看到一些关于制作托管 C++ 包装类以与之交互的评论,但我不知道从哪里开始。我也不知道如何将它与所有其他代码一起编译。我真的找不到这方面的教程,而且谷歌在托管 C++ 上显示的东西似乎真的没有帮助。
Anything out there to help me out? This doesn't seem unreasonable to me.
有什么可以帮助我的吗?这在我看来并非不合理。
EDITTried m3rLinEz solution but it's giving me a BadImageFormatException, I think it's because the DLL isn't generated. I did everything as told, dunno what happened. Any ideas?
编辑尝试了 m3rLinEz 解决方案,但它给了我一个 BadImageFormatException,我认为这是因为未生成 DLL。我照他说的做了,不知道发生了什么。有任何想法吗?
采纳答案by Gant
Have you take a look at C++/CLI?
你看过 C++/CLI 吗?
Let me give a very short example. Here is the source file from a Visual C++ -> CLR -> Class Library project. It basically get Windows username and return it.
让我举一个非常简短的例子。这是来自 Visual C++ -> CLR -> 类库项目的源文件。它基本上获取 Windows 用户名并返回它。
Please note that, in order to get this compiled, you have to go into project settings and mark "Additional Dependencies" as "Inherit from parent" because we are using those Windows libs (kernel32.lib, user32.lib, ..)
请注意,为了进行编译,您必须进入项目设置并将“附加依赖项”标记为“从父项继承”,因为我们正在使用这些 Windows 库(kernel32.lib、user32.lib、..)
// CSCPP.h
#pragma once
#include "windows.h"
using namespace System;
namespace CSCPP {
public ref class Class1
{
// TODO: Add your methods for this class here.
public:
String^ GetText(){
WCHAR acUserName[100];
DWORD nUserName = sizeof(acUserName);
if (GetUserName(acUserName, &nUserName)) {
String^ name = gcnew String(acUserName);
return String::Format("Hello {0} !", name);
}else{
return gcnew String("Error!");
}
}
};
}
Now created a new C# project and add reference to our first C++/CLI Class Library project. And then call the instance method.
现在创建了一个新的 C# 项目并添加对我们第一个 C++/CLI 类库项目的引用。然后调用实例方法。
namespace CSTester
{
class Program
{
static void Main(string[] args)
{
CSCPP.Class1 instance = new CSCPP.Class1();
Console.WriteLine(instance.GetText());
}
}
}
This gave the following result on my machine:
这在我的机器上给出了以下结果:
Hello m3rlinez !
你好 m3rlinez!
C++/CLI is basically a managed extension over C++ standard. It allows you to utilize CLR classes and data types in your C++/CLI project and also expose this to managed language. You can created a managed wrapper for your old C++ library using this. There are some weird syntaxes such as String^
to define reference type to CLR String. I find "Quick C++/CLI - Learn C++/CLI in less than 10 minutes"to be useful here.
C++/CLI 基本上是对 C++ 标准的托管扩展。它允许您在 C++/CLI 项目中使用 CLR 类和数据类型,并将其公开给托管语言。您可以使用它为旧的 C++ 库创建一个托管包装器。有一些奇怪的语法,例如String^
定义对 CLR 字符串的引用类型。我发现“快速 C++/CLI - 在不到 10 分钟内学习 C++/CLI”在这里很有用。
回答by Ash
I would create a standard (non COM/Managed) Dynamic Link Library as described hereand then use the DllImport attribute(platform invoke) in the c# code to access the exported functions.
我将按照此处所述创建一个标准(非 COM/托管)动态链接库,然后在 C# 代码中使用DllImport 属性(平台调用)来访问导出的函数。
The key point from that article:
那篇文章的关键点:
Note the __declspec(dllexport) modifier in the method declarations in this code. These modifiers enable the method to be exported by the DLL so that it can be used by other applications. For more information, see dllexport, dllimport.
请注意此代码中方法声明中的 __declspec(dllexport) 修饰符。这些修饰符使 DLL 可以导出该方法,以便其他应用程序可以使用它。有关详细信息,请参阅 dllexport、dllimport。
This is a lighter weight alternative to an actual COM interop wrapper and avoids issues such as registration etc (the DLL can simply be placed in the application directory) .
这是实际 COM 互操作包装器的轻量级替代方案,并避免了诸如注册等问题(DLL 可以简单地放置在应用程序目录中)。
Another alternative is It Just Works(IJW). This is probably a better choice if you have managed C++ code and need to access this from other .NET languages. But this is only an option if you are able/happy to convert your unmanaged C++ to managed C++ though.
另一种选择是它只是有效(IJW)。如果您有托管 C++ 代码并且需要从其他 .NET 语言访问它,这可能是一个更好的选择。但是,如果您能够/乐于将非托管 C++ 转换为托管 C++,这只是一个选项。
回答by Igor Zevaka
I would stay away from P/Invoke as it's pretty slow compared to IJW (It Just Works). The latter allows you to seamlessly interweave managed and and unmanaged c++. All you have to do is to create a managed c++ assembly, write a managed class that is visible from c# and call the unmanaged code out of that.
我会远离 P/Invoke,因为它与 IJW(It Just Works)相比非常慢。后者允许您无缝地交织托管和非托管 C++。您所要做的就是创建一个托管 C++ 程序集,编写一个从 C# 可见的托管类,然后从中调用非托管代码。
Uhm... OK. I was under the impression that P/Invoke calls were slower which they are inherintly not. However, by having explicit control over marshalling, you can make your C++/CLI version to perform better in a lot of the cases.
嗯……好吧。我的印象是 P/Invoke 调用速度较慢,而实际上并非如此。但是,通过对编组进行显式控制,您可以使 C++/CLI 版本在许多情况下表现更好。
Here is Microsoft's article about both mechanisms:
这是微软关于这两种机制的文章:
http://msdn.microsoft.com/en-us/library/ms235282.aspx
http://msdn.microsoft.com/en-us/library/ms235282.aspx
Advantages of IJW
- There is no need to write DLLImport attribute declarations for the unmanaged APIs the program uses. Just include the header file and link with the import library.
- The IJW mechanism is slightly faster (for example, the IJW stubs do not need to check for the need to pin or copy data items because that is done explicitly by the developer).
- It clearly illustrates performance issues. In this case, the fact that you are translating from a Unicode string to an ANSI string and that you have an attendant memory allocation and deallocation. In this case, a developer writing the code using IJW would realize that calling _putws and using PtrToStringChars would be better for performance.
- If you call many unmanaged APIs using the same data, marshaling it once and passing the marshaled copy is much more efficient than re-marshaling every time.
IJW的优势
- 无需为程序使用的非托管 API 编写 DLLImport 属性声明。只需包含头文件并链接到导入库。
- IJW 机制稍微快一些(例如,IJW 存根不需要检查是否需要固定或复制数据项,因为这是由开发人员明确完成的)。
- 它清楚地说明了性能问题。在这种情况下,您正在将 Unicode 字符串转换为 ANSI 字符串,并且您有一个伴随的内存分配和释放。在这种情况下,使用 IJW 编写代码的开发人员会意识到调用 _putws 和使用 PtrToStringChars 会更好地提高性能。
- 如果您使用相同的数据调用许多非托管 API,则将其编组一次并传递编组后的副本比每次都重新编组要高效得多。
There are aesthetic advantages as well:
还有美学优势:
- C# code looks like C# code without any interop'y weirdness.
You don't have to define
DLLImport
attribute, you don't have to define any of the data structures (also with p/invoke specific attributes) which could look like this:[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct DevMode { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName; }
- You don't have to convert all parameter primitive types into their .NET counterparts (there is table on this pagethat lists how managed types map to unmanaged types).
- You get to work with C++/CLI which is really fun to learn and is really polished. It's come a long way since VS 2003 and is now a fully featured .NET language. Microsoft documentation for it is pretty good, as is all the IJW information.
- Doing C++ interop in C++/CLI feels very natural as opposed to C#. This is completely subjective, but I would much rather prefer to do string marshalling in C++ that do
Marshal.PtrToString(ptr)
. - If exposing an API you would probably want to wrap up all P/Invoke stuff in another layer, so you don't have to deal with P/Invoke ugliness. This way you have the overhead of all the marshalling AND the C# layer around it. With C++/CLI the marshalling and the interop abstraction are in one place, and you can choose how much marshalling you need.
- C# 代码看起来像 C# 代码,没有任何互操作性。
您不必定义
DLLImport
属性,也不必定义任何可能如下所示的数据结构(也带有 p/invoke 特定属性):[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct DevMode { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName; }
- 您不必将所有参数原始类型转换为其对应的 .NET 类型(此页面上的表格列出了托管类型如何映射到非托管类型)。
- 您可以使用 C++/CLI 工作,这真的很有趣,而且非常好。自 VS 2003 以来,它已经走了很长一段路,现在是一种功能齐全的 .NET 语言。Microsoft 文档非常好,所有 IJW 信息也是如此。
- 与 C# 相比,在 C++/CLI 中进行 C++ 互操作感觉非常自然。这完全是主观的,但我更愿意在 C++ 中进行字符串编组,而
Marshal.PtrToString(ptr)
. - 如果公开 API,您可能希望将所有 P/Invoke 内容包装在另一层中,这样您就不必处理 P/Invoke 丑陋问题。这样你就有了所有编组的开销和它周围的 C# 层。使用 C++/CLI,编组和互操作抽象位于一处,您可以选择需要多少编组。
IMHO if you are calling an odd function in Windows SDK, go with P/Invoke. If you are exposing a moderately complex C++ API to the managed world, definitely C++/CLI.
恕我直言,如果您在 Windows SDK 中调用一个奇怪的函数,请使用 P/Invoke。如果您要向托管世界公开一个中等复杂的 C++ API,当然是 C++/CLI。
回答by Arve
There are at least three ways to call unmanaged code from managed in the same process:
在同一进程中从托管调用非托管代码至少有三种方式:
- C++/CLI
- Platform Invoke
- Wrap your C++ in a COM object
- C++/命令行界面
- 平台调用
- 将 C++ 包装在 COM 对象中
At work we use C++/CLI for this, it seems to work.
在工作中,我们为此使用 C++/CLI,它似乎有效。