15

简而言之:如何在生成程序的调试会话期间调试生成的代码?(见下面的代码)

我面临以下问题:我想从生成它的应用程序中调试到动态生成/编译的代码。我提供了一个过于简单的例子来澄清它。这个例子不需要调试!我的真实应用程序生成了更多的行和代码,这些行和代码确实证明了调试是合理的,相信我:-) 我想知道是否有一种方法可以调试或在HelloWorld. 进入 InvokeMethod 调用不起作用。也许解决方案涉及在调用站点对生成的程序集进行代码修改。

我已经看了很多问题(例如在 Visual Studio .NET中调试动态加载的程序集),但没有一个对解决问题有帮助(如果完全可以解决?)

我从http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=118 获取代码作为基础并修复了过时的调用。除此之外,我在内存中即时生成了程序集,并且调用运行良好。我显式地生成了一个带有调试信息的程序集,这给了我希望:如果无法调试,为什么会有选项?

using System;
using System.Text;
using System.IO;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;

namespace DynamicAssembly
{
    class CreateCompileExecute
    {
        [STAThread]
        static void Main(string[] args)
        {
            // Creates a text file to store the new class
            StringBuilder builder = new StringBuilder();
            builder.AppendLine("using System;");
            builder.AppendLine("namespace CSharpFriendsRocks");
            builder.AppendLine("{");
            builder.AppendLine("class CSharpFriends");
            builder.AppendLine("{");
            builder.AppendLine("public CSharpFriends() {" +
                " Console.WriteLine(\"The CSharpFriends type is constructed\"); }");
            builder.AppendLine("public void HelloWorld() {" +
                " Console.WriteLine(\"Hello World - CSharpFriends.Com Rocks.\"); }");
            builder.AppendLine("}");
            builder.AppendLine("}");

            // Create the C# compiler
            CSharpCodeProvider csCompiler = new CSharpCodeProvider();

            // input params for the compiler
            CompilerParameters compilerParams = new CompilerParameters();
            compilerParams.OutputAssembly = "CSharpFriends.dll";
            compilerParams.GenerateInMemory = true;
            compilerParams.IncludeDebugInformation = true;
            compilerParams.ReferencedAssemblies.Add("system.dll");
            compilerParams.GenerateExecutable = false; // generate the DLL

            // Run the compiler and build the assembly
            CompilerResults results = csCompiler.CompileAssemblyFromSource(
                compilerParams, builder.ToString());

            // Load the generated assembly into the ApplicationDomain 
            Assembly asm = results.CompiledAssembly;
            Type t = asm.GetType("CSharpFriendsRocks.CSharpFriends");

            // BindingFlags enumeration specifies flags that control binding and 
            // the way in which the search for members and types is conducted by reflection. 
            // The following specifies the Access Control of the bound type
            BindingFlags bflags = BindingFlags.DeclaredOnly | BindingFlags.Public
                        | BindingFlags.NonPublic | BindingFlags.Instance;

            // Construct an instance of the type and invoke the member method
            Object obj = t.InvokeMember("HelloWorld", bflags |
                BindingFlags.CreateInstance, null, null, null);

            // Call the method
            t.InvokeMember("HelloWorld", bflags | BindingFlags.InvokeMethod,
                    null, obj, null);
        }
    }
}
4

2 回答 2

14

在发现我的问题是How to debug/break in codedom compiled code的副本后,我终于找到了一种解决方法,这对我来说并不明显。bbmud 在那里给出了一个很好的提示,可以让调试器正常工作,但没有说明如何进入代码。我添加了对包含我想在脚本中实现的接口的一些程序集的引用:

compilerParams.ReferencedAssemblies.Add(typeof(IPlugin).Assembly.Location);
compilerParams.GenerateExecutable = false; // generate the DLL

// if you want to debug, this is needed...
compilerParams.GenerateInMemory = false;
compilerParams.TempFiles = new TempFileCollection(Environment.
      GetEnvironmentVariable("TEMP"), true);

现在,当我考虑CSharpFriends成为 的实现时IPlugin,我可以通过转换上面的内容来获得接口obj

IPlugin script = obj as IPlugin;

然后调试对接口方法或属性的调用就像往常一样简单!添加的技巧

 System.Diagnostics.Debugger.Break();

脚本代码内部也运行良好,但需要更改脚本。由于应用程序内部的代码总是需要根据某种机制(属性或接口的反射)知道脚本内部有哪些类型的方法,因此使用两者都知道的接口对我来说是一个非常可接受的解决方案。

我希望它可以帮助别人。

于 2009-10-20T14:14:55.660 回答
0

Visual Studio 2010 在调试器中温和地处理这个问题。升级后我很惊讶。我希望它有所帮助。

于 2010-05-14T09:36:53.777 回答