2011-05-04 1 views
4

대학에서의 임무는 Reflection.Emit을 사용하여 간단한 프록시 생성기/인터셉터 메커니즘을 구현하는 것이 었습니다. 다음 프로그램을 생각해 냈습니다.Reflection.Emit을 통한 프록시 생성은 디버깅으로 시작될 때만 작동합니다.

디버그 모드 [F5] (디버그 -> 디버깅 시작)에서 Visual Studio 내에서 정상적으로 작동하지만 디버깅하지 않고 시작했을 때 대부분의 경우 디버그 [Ctrl + F5] (디버그 -> 디버깅하지 않고 시작)과 충돌합니다.

이 두 모드의 차이점은 무엇입니까? (은 디버그 <> 릴리스 모드 참조). 이 문제는 여러 컴퓨터/설정 (Win XP SP3 32 비트 및 64 비트, Windows 7 32 비트)에서 발생합니다.

Click pastebin.

// The proxy generator; I assume that the error is buried along the lines emitting the IL code 
public static class ProxyGenerator 
{ 
    public static T Create<T>(object obj, IInterception interception) 
    { 
     Type type = obj.GetType(); 

     TypeBuilder proxy = DefineProxy(type); 

     FieldBuilder wrappedField = DefinePrivateField(proxy, "wrappedObject", type); 
     FieldBuilder interceptionField = DefinePrivateField(proxy, "interception", interception.GetType()); 

     DefineConstructor(proxy, wrappedField, interceptionField); 
     DefineInterfaceMethods(type, proxy, wrappedField, interceptionField); 

     return (T) Activator.CreateInstance(proxy.CreateType(), obj, interception); 
    } 

    private static TypeBuilder DefineProxy(Type type) 
    { 
     var assemblyName = new AssemblyName {Name = "GeneratedProxyAssembly"}; 
     AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
      assemblyName, AssemblyBuilderAccess.Run); 

     ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("GeneratedProxyModule"); 

     return moduleBuilder.DefineType(
      type.Name + "Proxy", 
      type.Attributes, 
      typeof (object), 
      type.GetInterfaces()); 
    } 

    private static FieldBuilder DefinePrivateField(TypeBuilder typeBuilder, string fieldName, Type fieldType) 
    { 
     return typeBuilder.DefineField(fieldName, fieldType, FieldAttributes.Private); 
    } 

    private static void DefineConstructor(TypeBuilder typeBuilder, params FieldBuilder[] parameters) 
    { 
     ConstructorBuilder ctor = typeBuilder.DefineConstructor(
      MethodAttributes.Public, CallingConventions.Standard, parameters.Select(f => f.FieldType).ToArray()); 

     // Emit constructor 
     ILGenerator g = ctor.GetILGenerator(); 

     // Load "this" pointer and call base constructor 
     g.Emit(OpCodes.Ldarg_0); 
     g.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0])); 

     // Store parameters in private fields 
     for (int i = 0; i < parameters.Length; i++) 
     { 
      // Load "this" pointer and parameter and store paramater in private field 
      g.Emit(OpCodes.Ldarg_0); 
      g.Emit(OpCodes.Ldarg, i + 1); 
      g.Emit(OpCodes.Stfld, parameters[i]); 
     } 

     // Return 
     g.Emit(OpCodes.Ret); 
    } 

    private static void DefineInterfaceMethods(Type type, TypeBuilder proxy, FieldInfo wrappedField, FieldInfo interceptionField) 
    { 
     // Loop through all interface methods 
     foreach (MethodInfo interfaceMethod in type.GetInterfaces().SelectMany(i => i.GetMethods())) 
     { 
      MethodInfo method = type.GetMethod(interfaceMethod.Name); 

      MethodBuilder methodBuilder = proxy.DefineMethod(
       method.Name, 
       method.Attributes, 
       method.ReturnType, 
       method.GetParameters().Select(p => p.ParameterType).ToArray()); 

      // Emit method 
      ILGenerator g = methodBuilder.GetILGenerator(); 

      // Intercept before 
      EmitMethodCallOnMember(g, interceptionField, "Before", false); 

      // Delegate method call 
      EmitMethodCallOnMember(g, wrappedField, method.Name, true); 

      // Intercept after 
      EmitMethodCallOnMember(g, interceptionField, "After", false); 

      // Return 
      g.Emit(OpCodes.Ret); 
     } 
    } 

    private static void EmitMethodCallOnMember(ILGenerator g, FieldInfo field, string methodName, bool delegateParameters) 
    { 
     // Load "this" pointer to get address of field 
     g.Emit(OpCodes.Ldarg_0); 
     g.Emit(OpCodes.Ldflda, field); 

     MethodInfo method = field.FieldType.GetMethod(methodName); 
     if (delegateParameters) 
     { 
      // Load method parameters 
      for (int i = 0; i < method.GetParameters().Length; i++) 
      { 
       g.Emit(OpCodes.Ldarg, i + 1); 
      } 
     } 

     // Emit call 
     g.Emit(OpCodes.Call, method); 
    } 
} 

// Some infrastructure 
public interface IInterception 
{ 
    void Before(); 
    void After(); 
} 

public class LogInterception : IInterception 
{ 
    public void Before() 
    { 
     Console.WriteLine("Before ... "); 
    } 

    public void After() 
    { 
     Console.WriteLine("... After"); 
    } 
} 

public interface ITest 
{ 
    string DoSomething(string s1, string s2); 
} 

public class Test : ITest 
{ 
    public string DoSomething(string s1, string s2) 
    { 
     Console.WriteLine("... doing something ..."); 
     return s1 + s2; 
    } 
} 

// The test program, expected output is down below 

internal class Program 
{ 
    internal static void Main(string[] args) 
    { 
     var test = new Test(); 
     var proxy = ProxyGenerator.Create<ITest>(test, new LogInterception()); 

     Console.WriteLine(test.DoSomething("Hello", " World")); 
     Console.WriteLine("----------------------------------------"); 
     Console.WriteLine(proxy.DoSomething("Hello", " World")); 

     Console.ReadKey(); 
    } 
} 

또 다른 질문 : 이러한 문제를 좁히는 가장 좋은 방법은 무엇입니까? 생성 된 어셈블리를 디스크에 저장하고 Reflector에 결과 dll을 열려고했지만 비어있는 것으로 보입니다.

위에서 언급했듯이 디버그 모드에서 시작할 때 프로그램이 작동하는 것으로 보이며 다음 출력을 인쇄합니다.

... doing something ... 
Hello World 
---------------------------------------- 
Before ... 
... doing something ... 
... After 
Hello World 

감사합니다.

+0

어떤 예외가 있습니까? 디버그 및 릴리스 모드 모두에서 코드가 잘 작동합니다. – oxilumin

+0

앞에서 언급했듯이 디버깅 모드 나 릴리즈 모드는 아니며 '디버깅 시작'및 '디버깅하지 않고 시작'이라고합니다. –

+0

확인. 나는 그 문제를 발견했다고 생각한다. 이제는 디버그 모드에서 왜 작동하는지 정말 모르겠다. – oxilumin

답변

6

프로젝트 설정 탭에서 x86 모드를 명시 적으로 설정하십시오.

x64 또는 AnyCpu 모드에서 프로그램을 실행할 때만 치명적인 예외가 발생했습니다.

아, 알아 냈습니다. LdfldaLdfld으로 바꾸십시오. 디버거 없이도 잘 작동합니다 (방금 .exe를 실행했습니다). Ldfldaref 또는 out 키워드를 사용하여 메소드에 매개 변수로 전달하는 입력란 용입니다.

+0

"플랫폼 대상"설정을 참조합니까? 만약 당신이 :'x86'이 이미 선택되었습니다. –

+0

방금 ​​확인한 결과 문제가 모든 사용 가능한 플랫폼에서 사라집니다. :) - 감사합니다! –