PostSharp修改CIL后添加指令触发InvalidProgramException问题求助
Let's break down exactly why this happens and how to fix it:
Core Root Cause
When PostSharp weaves its aspect into your method, it generates IL that includes hardcoded offset values for branch instructions (like switch, leave.s, br.s) and defines strict boundaries for try/catch blocks. When you insert a nop (or any instruction), you shift all subsequent instructions by the size of the inserted code, which invalidates every hardcoded offset in the method. The CLR's IL validator detects these invalid offsets and throws InvalidProgramException.
Detailed Breakdown of the Problem
Looking at the PostSharp-transformed IL, here are the key elements that break when you modify the IL manually:
Switch Instruction with Hardcoded Offsets
Theswitch ( IL_00bf, IL_00c1, IL_00bf, IL_00c1, IL_00ca)line uses absolute byte offsets to define where each case should jump. Inserting a 1-bytenopat the start means all these offsets now point to the wrong location (they're 1 byte too early). The CLR can't resolve the jump targets, so it flags the program as invalid.Try/Catch Block Boundaries
PostSharp wraps your original method logic in atryblock, withleave.sinstructions that jump to specific offsets to exit the block. Inserting anopshifts the end of thetryblock and the target ofleave.sinstructions, breaking the exception handling structure the CLR expects.Branch Target Consistency
Even simple branches likebr.s IL_0079rely on precise relative offsets. Shifting instructions changes these relative distances, making the branch land on an invalid instruction boundary or the middle of an opcode.
Solutions to Fix This
1. Use PostSharp's Official Weaving APIs (Recommended)
Instead of manually modifying IL, leverage PostSharp's built-in mechanisms to add your logic:
- If you need to track object state changes, use
FieldInterceptionAspectto intercept field/property setters directly. - To add code before/after method execution, extend
MethodBoundaryAspect(which is what your existing aspect uses) and override the appropriate methods (OnEntry,OnSuccess, etc.). - For more advanced IL manipulation, use PostSharp's
IAspectProviderorIAdviceProviderinterfaces—these APIs automatically handle offset recalculation and IL validation for you.
2. Manually Recalculate All Branch Offsets (If You Must Modify IL Directly)
If you absolutely need to modify the IL manually, you'll have to:
- Traverse your
ILInstructionlist and calculate the exact byte offset of each instruction (accounting for opcode size and operand length). - For every branch instruction (
br,br.s,leave,leave.s,switch, etc.), update its operand data to point to the new, correct offset of the target instruction. - After generating the new IL byte array, run
peverify.exeon the modified assembly to catch any remaining validation errors.
3. Avoid Manual IL Modification Altogether
Given your goal of tracking object state changes, consider alternative approaches that don't require IL hacking:
- Use a proxy library (like Castle DynamicProxy) to wrap objects and track changes.
- Implement a change-tracking pattern directly in your classes (e.g., using a base class that records property changes).
内容的提问来源于stack exchange,提问作者Amon

