从 Visual C++ 调用 C# 代码

Calling C# code from visual C++

基本上我需要从 Visual C++ 代码调用 C# 代码。 在阅读了很多关于可能方式的文章之后,我决定使用 C++/CLI 机制。

最初我决定我将在 C++ 本机代码(dll 库项目)中使用一些函数,它们将调用 CLR 项目中的一些函数,这些函数将调用 C# 项目中的一些函数。

之后我想也许我可以摆脱桥梁项目(CLR 项目),因为它只会过渡到托管世界。我想我可以只创建我的本地项目,我可以向它添加一个 c++ 源文件,我可以只为该文件而不是整个项目启用 CLR 支持。 所以这意味着我的原生项目将只包含一个可以使用 C++/CLI 语法并代表 bridge 的文件。所有其他文件只是本机 C++ 源文件。 从设计的角度来看这是正确的吗?!

为了执行上述操作,我想我必须将托管的 C# dll 文件添加到 C++ 本机 dll 文件的附加#using Directories 属性。 问题是我不知道如何根据当前配置设置 C# dll 调试或发布版本的路径。

我还知道我无法将本机 C++ DLL 添加到 C# 项目引用。 但看起来我可以在本机 C++ 项目中添加一个 C# dll 作为引用。怎么会 ?!这甚至有效吗?! 如果我可以将 C# dll 添加到本机 C++ 项目引用,我是否需要设置那些#using 目录?!

我怀疑您是否可以让整个项目都采用非 CLI 方式,并且只在其中制作一个具有 CLI 功能的文件。总的来说,这种方法比较棘手。

我建议考虑使用 DllExportAttribute 导出 C# 函数:https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports

尝试ILASM 2.0https://msdn.microsoft.com/tr-tr/library/496e4ekx(v=vs.110).aspx

基本上,工作与 C++/CLI 中的导出相同。

// unmexports.il
// Compile with : ilasm unmexports.il /dll
.assembly extern mscorlib { auto }
.assembly UnmExports {}
.module UnmExports.dll
.method public static void Bar()
{
  .export [1] as bar
  ldstr "Hello from managed function"
  call void [mscorlib]System.Console::WriteLine(string)
  ret
}

注意:您可以使用 ILDASM 对 C# 和 C++/CLI 进行反编译,以查看如何导出它。 (如示例中)

I can enable CLR support just for that file and not the entire project

这样做与通过选择 CLR 项目模板开始并没有有效的区别。无论哪种方式,结果都是混合模式的 .NET 程序集,其中包含 MSIL 和本机代码。在项目级别执行此操作只是为项目中的 .cpp 文件设置 /clr 的默认值。您还可以通过覆盖单个 .cpp 文件的设置(现在禁用 /clr)来使用 CLR 项目模板执行此操作。最终结果都是一样的。

I think I must add the managed C# dll file to Additional #using Directories

是的,这是一种方法。也解决了你的查询,你可以在路径名中使用 $(Configuration) 宏。在编译时解析为 "Debug" 或 "Release",具体取决于您构建的配置。我要强调的是,这实际上不是必需的。编译器仅使用 C# 程序集中的元数据。只是声明。与 .h 文件完全相同,请注意您很少根据配置使 #include 不同。唯一的极端情况是当您在 C# 源代码中使用 #if DEBUG 到 include/exclude 代码时,这不是很常见。

But it looks like I can add a C# dll as a reference inside the native C++ project

每个 VS 版本都对此进行了修补,不能 100% 确定您在做什么。添加引用时实际上很少发生,它只是让编译器驱动程序添加 /FU compile option。如果 .cpp 文件不是用 /clr 编译的,那么它什么都不做,编译器只是完全忽略它。当您在源代码中使用 #using 而没有使用 /clr 时,它确实会大声抱怨。否则两者之间没有真正的区别,只是更容易用 /FU 控制文件的路径。

since it only makes the transition to managed world

在这里提醒一下是合适的,还有很多事情要做。你在这里做的事情叫做"reverse pinvoke",本机代码调用托管代码。 C++/CLI 的主要设计目的恰恰相反。它涉及更多,需要发生的重要事情是需要在第一次调用时加载和初始化 CLR。这都是自动化的,由使用 __declspec(dllexport) 时自动生成的存根提供。 Robert Giesecke 的 "Unmanaged Exports" 实用程序所依赖的魔法,您应该考虑的另一个选项。

但它是有限的。您不能公开对象模型,只能公开简单的函数。在函数调用中增加了开销,尽管它非常适度。而大问题,你不能轻易诊断错误。 CLR 和大多数 C# 代码都希望调用者知道如何处理异常。问题是您不是来自本机 C++ 代码。您在使用 __try/__except 方面有 一些 余地,但您无法获得有关异常的任何详细信息。这会将非常简单的问题(如 FileNotFoundException)变成完全无法诊断的崩溃。您可以通过将 Debugger Type 设置为 "Mixed" 来调试它,但是在您发布它之后您无法做任何有用的事情。非常丑陋的支持 phone 调用。

在没有这些问题的情况下完成相同任务的其他方法是在 C# 库中使用 [ComVisible(true)],允许在本机 C++ 代码中使用#import。并通过其托管接口自定义托管 CLR,这是支持用托管代码编写的插件的程序所使用的方法。像 AutoCAD 这样的 CAD 程序就是很好的例子。还有 Visual Studio.