在C++中使用C#作为脚本语言

4
我正在尝试将C#作为脚本语言嵌入到我的C++库中。我意识到这可能不是一项简单的任务,所以我首先进行了最小测试项目,以查看是否可能。
我已经成功创建了一个包含管理简单脚本和在运行时编译它们的代码的C#程序集,使用CSharpCodeProvider进行测试。该程序集已在C#控制台应用程序中进行了测试(给定的C#源文件在内存中编译并在必要时执行)。
因此,下一步是将其与C++应用程序桥接起来。
我通过COM设法做到了这一点。我将我的C# dll、类和接口导出为COM对象,并使用Regasm生成了tlb文件。
我能够在C++项目中创建这些类的实例并调用它们的方法,一切似乎都工作正常(例如显示Windows.Forms消息框),直到需要执行实际逻辑时才会抛出不应该抛出的异常。由于我不知道是否可以调试COM组件(我认为无法进入此C# dll的代码),因此我添加了大量消息框和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,版本=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吗? - MoonKnight
谢谢提供链接,但看起来这似乎还没有准备好用于生产代码,并且就实际需求而言,我认为使用CSharpCodeProvider和从dll库内部进行反射仍然是更合适的解决方案。 - Adrian Lis
我同意,但这是可以发布到生产环境中的东西,所以最好要意识到它。祝你好运:] - MoonKnight
异常抛出的确切行数是哪一行?您能展示异常的完整堆栈跟踪吗?相对于您的 C++ 控制台和主要 .NET 程序集,您的 lame2.scripting 组件的位置在哪里?是 obj = (IScript)Activator.CreateInstance(script_type); 还是 script_assembly.GetTypes() - Zdeslav Vojkovic
GetTypes() 抛出了一个异常。我正在使用绝对路径到程序集,它是 D:\Interop\lame2.scripting\lame2.scripting\bin\Debug\lame2.scripting.dll"(当然我使用双斜杠)。 - Adrian Lis
显示剩余3条评论
1个回答

4
如果你可以将应用程序功能暴露为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组件,而是 'interop magic',或多或少地只是在这两个方面之间传递内容。
如果您无法调试包含您的实现的原始.NET程序集,请确保Debug设置正确:
- 如果您使用本机应用程序正常启动调试(F5),请转到“项目设置->调试器类型”,并将其设置为“Mixed”。 - 如果您将调试器附加到正在运行的进程,则在“附加到进程”窗体中,单击“选择...”按钮,在“Attach to”文本框旁边选择所需的内容(托管和本机)。
这假定您确实拥有程序集的源代码和.pdb文件。

实际上,我在调试使用运行时编译器的托管 DLL 本身方面遇到了困难。我将发布一个示例源代码,展示我目前所做的工作。 - Adrian Lis
我已添加了一些关于可能原因的信息。 - Zdeslav Vojkovic
谢谢你提供的有关调试的信息。它非常好用!不过仍然有异常问题,但这将使解决问题更加容易。 - Adrian Lis
我已决定接受这个答案,因为将其与编译成临时汇编相结合已经起作用,但请查看问题的最后编辑。 - Adrian Lis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接