【问题标题】:Using C# as scripting language in C++在 C++ 中使用 C# 作为脚本语言
【发布时间】:2012-09-22 05:16:15
【问题描述】:

我正在尝试将 C# 作为脚本语言嵌入到我的 C++ 库中。我知道这可能不是一项微不足道的任务,所以我首先进行了一个最小的测试项目,看看这是否可能。

我设法创建了一个 C# 程序集,其中包含用于管理简单脚本并在运行时使用 CSharpCodeProvider 编译它们的代码。该程序集使用 C# 控制台应用程序作为测试项目进行了测试(假设 C# 源文件在内存中编译并在必要时执行)。

所以下一步是用 C++ 应用程序来桥接它。

我以某种方式设法使用 COM 做到了这一点。我将 C# dll、类和接口导出为 COM 对象,并使用 Regasm 生成了 tlb 文件。

我能够在 C++ 项目中创建这些类的实例并调用它们的方法,并且一切似乎都工作正常(例如显示 Windows.Forms 消息框),直到需要执行实际逻辑。它只是抛出不应抛出的异常,因为它已经与作为库主机的 C# 控制台应用程序一起使用。

因为我不知道我是否可以调试 COM 组件(好吧,单步执行这个 C# dll 的代码是不可能的 AFAIK),所以我添加了过多的消息框和 try-catch 块。

当我尝试从已编译的程序集中检索任何类型时,它似乎会中断。它抛出:Reflection.ReflectionTypeLoadException

另一方面,编译不会失败,也不会生成任何错误消息。

使用 C# 触发这些方法可以 100% 工作,只有在从 C++ 执行时才会以某种方式中断。

这可能是什么原因?或者可能有另一种方式来做我想做的事情?我确实发现在这种情况下使用 COM 是正确的,但是我不知道有哪些方面会导致此类代码中断?

编辑: 这是导出为 COM 的托管 C# 程序集: http://nopaste.info/9da70dbcbd.html

所以这些对象是从 C++ 应用程序访问的,如下所示: http://nopaste.info/50c4c9726a.html

并且脚本就像从IScript 派生的类并实现显示消息框的Execute 方法一样简单。

编辑 2:

有关抛出异常的详细信息如下: “无法加载文件或程序集 'lame2.scripting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=84d0df983ded76a1' 或其依赖项之一。Nie można odnaleźć określonego pliku。”

最后一句表示找不到文件。

但这很奇怪,因为在编译时我有以下行: compilerparams.ReferencedAssemblies.Add("D:\Interop\lame2.scripting\lame2.scripting\bin\Debug" + "\lame2.scripting.dll");

这是“脚本”的内容:

using System;
using System.Linq;
using System.Windows.Forms;
using System.Reflection;
using lame2.scripting;

namespace Olaboga
{
  public class TestScript : IScript
  {
    public TestScript() { }

    public void Execute(Event ev)
    {
      MessageBox.Show("Script that has been compiled from a source file has been invoked.   TestScript.Execute");
    }
  }
}

编辑 3:

终于在从内存编译切换到编译到临时文件后,它突然开始正常工作了。所以最后一个问题有人可以知道为什么它在内存中编译时无法工作,是否可以这样做。

【问题讨论】:

  • 你见过Roslyn
  • 感谢您的链接,但因为这似乎还没有准备好生产代码,我看不到这个更适合我需要的解决方案,然后使用 CSharpCodeProvider 和 dll 库中的反射说实话。
  • 我同意,但这是要发布到生产环境中的东西,所以最好了解它。祝你好运:]
  • 究竟在哪一行抛出异常?你能显示异常的完整堆栈跟踪吗? lame2.scripting 程序集相对于 c++ 控制台和主 .NET 程序集的位置是什么?是obj = (IScript)Activator.CreateInstance(script_type); 还是script_assembly.GetTypes()
  • GetTypes() 抛出异常。我正在使用程序集的绝对路径,即 D:\Interop\lame2.scripting\lame2.scripting\bin\Debug\lame2.scripting.dll"(当然我使用双斜杠)

标签: c# c++ com


【解决方案1】:

如果您可以将应用程序功能公开为 COM 对象模型,那么应该可以做到。您应该注意以 .NET 互操作层可以正确处理的方式对接口进行建模。

如果我理解正确,您在调试从 C# 脚本生成的 .NET 程序集时遇到问题,对吗?在这种情况下,您可以在编译程序集时使用以下编译器设置:

CompilerParameters cp = new CompilerParameters();

// Generate debug information.
cp.IncludeDebugInformation = true;

// Save the assembly as a physical file.
cp.GenerateInMemory = false;

// Set a temporary files collection. 
// The TempFileCollection stores the temporary files 
// generated during a build in the current directory, 
// and does not delete them after compilation.
cp.TempFiles = new TempFileCollection(".", true);

这将启用对生成代码的调试,因为您将拥有源代码、调试符号和程序集。但是,您多次提到“COM 组件”,但我没有看到任何 - 有一个 C++ 应用程序和 .NET 程序集 - 介于两者之间的东西不是真正的 COM 组件,而是“互操作魔法”,或多或少,只是在这两方之间编组。

如果您在调试包含您的实现的原始 .NET 程序集时遇到问题,请确保调试设置正确:

  • 如果您使用本机应用程序正常开始调试(F5),请转到Project Settings -> Debugging -> Debugger Type并将其设置为Mixed
  • 如果您将调试器附加到正在运行的进程,请在Attach to Process 表单中单击“附加到”文本框附近的Select... 按钮并选择您需要的那些(托管和本机)

这假设您确实有程序集的源代码和 .pdb 文件。

【讨论】:

  • 实际上我在调试托管 dll 本身(使用运行时编译器)时遇到了困难。我将发布到目前为止我所做的示例源代码。
  • 我添加了一些关于可能原因的信息。
  • 感谢您提供有关调试的信息。它就像一个魅力!虽然仍然存在异常问题,但它会使解决它变得容易得多。
  • 我决定接受这个答案,因为将它与编译到临时程序集相结合已经奏效,但请查找问题的最后编辑。
猜你喜欢
  • 2016-04-15
  • 1970-01-01
  • 2013-04-13
  • 1970-01-01
  • 2010-12-22
  • 2010-12-03
  • 2011-07-16
  • 1970-01-01
  • 2010-11-26
相关资源
最近更新 更多