65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Reflection Emit 的发布模式断点

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (5投票s)

2002年10月8日

2分钟阅读

viewsIcon

87394

downloadIcon

2

C#的发布模式不允许设置断点。此函数会发出一个 IL 中断指令,强制在调试器中设置断点。

引言

好吧,出于某种奇怪的原因,我需要查看发布模式和调试模式下生成的代码。为什么?因为我正在撰写一篇文章,介绍使用DebugTrace类别的差异,并且我想看看在发布模式下编译代码时,幕后真正发生了什么。此外,我认为即使 C# 是“托管的”,这并不意味着我在切换到发布版本时不会遇到奇怪的行为,并且将来我可能希望设置一个断点。

正如最近有人指出的,你无法在发布模式下设置断点,因为没有符号信息。这很荒谬,因为我仍然应该能够基于指令地址设置断点。然而,接受不可避免的事实,以下代码会在动态创建的模块中发出“Break”操作码,并调用该模块的函数。无论是在发布模式还是调试模式下,这都将导致应用程序在调试器下运行时中断。

这同时也是一个关于动态代码生成的有趣练习,展示了生成动态代码所需的一些令人头疼的步骤。尽管如此,我认为这是语言中一个非常棒的功能!

简单的方法

正如 Fredrik Tonn 在一条消息中指出的,通过在希望发生断点的位置插入System.Diagnostics.Debugger.Break(),可以轻松地在发布模式下添加断点。

还有什么可说的?就这么简单。

困难的方法

这需要动态生成一些 IL,它会调用 IL 操作码Break。以下代码记录了如何执行此操作。

/*
    *    Credits:
    *        Visual Studio Magazine
    *        Octobor 2002 Issue
    *        "Generate Assemblies With Reflection Emit" by Randy Holloway
    *        http://www.fawcette.com/vsm/2002_10/magazine/columns/blackbelt/ 
*/
// the interface serves as a stub to invoke the generated function
public interface IRMBP
{
    void Break();
}

public class ReleaseMode
{
    static public void Break()
    {
        Assembly asm=new ReleaseMode().GenerateBreak();
        IRMBP bp=null;
        bp=(IRMBP)asm.CreateInstance("RMBP");
        bp.Break();
    }

    private Assembly GenerateBreak()
    {
        // create an instance of the assembly name...
        AssemblyName asmbName=new AssemblyName();
        // ... and name it.
        asmbName.Name="ReleaseModeBreakPoint";

        // get an instance of the assembly builder,
        // indicating that the assembly can be saved and executed
        // If we don't specify "RunAndSave", we get the error
        // "cannot have a persistent module in a transient assembly"
        AssemblyBuilder asmbBuilder=
          AppDomain.CurrentDomain.DefineDynamicAssembly(asmbName, 
          AssemblyBuilderAccess.RunAndSave);

        // create a module builder, giving it the module name,
        // the filename, and whether or not symbolic information is written
        ModuleBuilder mdlBuilder=
          asmbBuilder.DefineDynamicModule("ReleaseModeBreakPoint", 
          "rmbp.dll", false);

        // create a type.  As in "public class RMBP"
        TypeBuilder typBuilder=mdlBuilder.DefineType("RMBP", 
                                      TypeAttributes.Public);

        // add an interface.  As in "public class RMBP : IRMBP"
        typBuilder.AddInterfaceImplementation(typeof(IRMBP));

        // create the "Break" method.  "public virtual void Break();"
        Type[] paramTypes=new Type[0];
        Type returnType=typeof(void);
        MethodBuilder mthdBuilder=typBuilder.DefineMethod("Break", 
           MethodAttributes.Public | MethodAttributes.Virtual, 
           returnType, paramTypes);

        // generate the code:
        ILGenerator ilg=mthdBuilder.GetILGenerator(); // get the generator
        ilg.Emit(OpCodes.Break); // issue a break statement
        ilg.Emit(OpCodes.Ret);    // return afterwards

        // get info about our Break method in the interface class IRMBP
        MethodInfo mthdInfo=typeof(IRMBP).GetMethod("Break");

        // associate our generated method with the interface method
        typBuilder.DefineMethodOverride(mthdBuilder, mthdInfo);

        // create the type
        typBuilder.CreateType();

        return asmbBuilder;
    }
}

用法

使用方法很简单。将语句ReleaseMode.Break();放在希望调试器中断的位置。一旦调试器中断,你可以单步执行两个return,然后检查程序的汇编代码。

使用反射发射在发布模式下设置断点 - CodeProject - 代码之家
© . All rights reserved.