使用 Reflection Emit 的发布模式断点





4.00/5 (5投票s)
2002年10月8日
2分钟阅读

87394

2
C#的发布模式不允许设置断点。此函数会发出一个 IL 中断指令,强制在调试器中设置断点。
引言
好吧,出于某种奇怪的原因,我需要查看发布模式和调试模式下生成的代码。为什么?因为我正在撰写一篇文章,介绍使用Debug
和Trace
类别的差异,并且我想看看在发布模式下编译代码时,幕后真正发生了什么。此外,我认为即使 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
,然后检查程序的汇编代码。