2016-10-20 6 views
1

을 DynamicMethod를 만듭니다. 이 바이트에서 동적 메서드를 만들고 실행하려고합니다. 여기에 내가 뭘하려고 오전의 예 :내가 DynamicMethod와 놀아 다음을 수행하는 것을 목표로하고 작업에서 <T> 지침

class Program 
{ 
    static void Main(string[] args) 
    { 
     //Create action and execute 
     Action<string> myAction = s => 
     { 
      Console.WriteLine("Hello " + s); 
     }; 
     myAction("World"); 
     //Get IL bytes 
     byte[] ilBytes = myAction.GetMethodInfo().GetMethodBody().GetILAsByteArray(); 
     DynamicMethod dynamicCallback = new DynamicMethod("myAction", typeof(void), new Type[] { typeof(string) }); 
     DynamicILInfo dynamicIlInfo = dynamicCallback.GetDynamicILInfo(); 
     dynamicIlInfo.SetCode(ilBytes, 100); 
     dynamicCallback.Invoke(null, new object[] { "World" }); 
    } 
} 

dynamicCallback.Invoke(null, new object[] { "World" }) 우리 얻을 "던져 예외 :가 mscorlib.dll에서 'System.BadImageFormatException'를"호출.

내가 가장 잘 모르겠다는 점은 SetCode()의 두 번째 인수로 사용해야하는 것이고, 'maxStackSize'로 사용해야하는 것은 무엇입니까? 초기 액션과 동일한 값을 설정하려면 어떻게해야합니까? 그러나 이것이 이것이 예외의 이유는 아니라고 생각합니다.

IL 바이트에서 동적 방법을 올바르게 만들려면 어떻게해야합니까?


솔루션 여기

내가 두디 켈 레티 (Keleti)가 제공하는 완벽한 솔루션을 요약하고 싶습니다 :

static void Main(string[] args) 
{ 
    Action<string> myAction = s => 
    { 
     Console.WriteLine("Hello " + s); 
    }; 
    MethodInfo method = myAction.GetMethodInfo(); 
    object target = myAction.Target; 

    DynamicMethod dm = new DynamicMethod(
     method.Name, 
     method.ReturnType, 
     new[] {method.DeclaringType}. 
      Concat(method.GetParameters(). 
       Select(pi => pi.ParameterType)).ToArray(), 
     method.DeclaringType, 
     skipVisibility: true); 

    DynamicILInfo ilInfo = dm.GetDynamicILInfo(); 
    var body = method.GetMethodBody(); 
    SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper(); 
    foreach (LocalVariableInfo lvi in body.LocalVariables) 
    { 
     sig.AddArgument(lvi.LocalType, lvi.IsPinned); 
    } 
    ilInfo.SetLocalSignature(sig.GetSignature()); 
    byte[] code = body.GetILAsByteArray(); 
    ILReader reader = new ILReader(method); 
    DynamicMethodHelper.ILInfoGetTokenVisitor visitor = new DynamicMethodHelper.ILInfoGetTokenVisitor(ilInfo, code); 
    reader.Accept(visitor); 
    ilInfo.SetCode(code, body.MaxStackSize); 

    dm.Invoke(target, new object[] { target, "World" }); 

    Console.ReadLine(); //Just to see the result 
} 

참고 : DynamicMethodHelper는 클래스 A blog post에 하이 보 루오에 의해 개발되고 설명되어 있지만, 또한 here에 직접 다운로드 할 수 있습니다.

+0

리플렉션을 사용하여 maxStackSize 값을 가져올 수 없다고 생각합니다. 그러나 실제로, 그것은 여기의 문제가 아닙니다. 문제는'Console.WriteLine' 호출이 메타 데이터 토큰 (아마도 MethodRef)으로 인코딩되고 메타 데이터 토큰이 그것을 선언하는 모듈의 유효 범위에서만 유효하다는 것입니다. 'DynamicILInfo.GetTokenFor' 함수를 살펴보면, 다른 메타 데이터 항목과'DynamicMethod'에 유효한 유효 토큰을 가져옵니다. – thehennyy

+0

@thehennyy 성공하지 않으려 고 시도했습니다. 편집을 참조하십시오. – Sjoerd222888

+0

일리노이 바이트 배열의 오래된 토큰을'GetTokenFor' 메쏘드가 반환하는 새 토큰으로 대체해야합니다. – thehennyy

답변

1

당신은 이런 식으로 작업을 수행 할 수 있습니다

byte[] code = body.GetILAsByteArray(); 
ILReader reader = new ILReader(method); 
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code); 
reader.Accept(visitor); 
ilInfo.SetCode(code, body.MaxStackSize); 

ILReader 당신을 위해 열심히 일을하는 클래스이다. here에서 복사 할 수 있습니다.

예 : 당신의 방법은 (일반 및 예외가 처리하지 않음)하는 간단한 방법입니다

MethodInfo method = ... 
DynamicMethod dm = new DynamicMethod(
    method.Name, 
    method.ReturnType, 
    method.GetParameters.Select(pi => pi.ParameterType).ToArray(), 
    method.DeclaringType, 
    skipVisibility: true\fasle - depends of your need); 

DynamicILInfo ilInfo = dm.GetDynamicILInfo(); 
var body = method.GetMethodBody(); 
SignatureHelper sig = SignatureHelper.GetLocalVarSigHelper(); 
foreach(LocalVariableInfo lvi in body.LocalVariables) 
{ 
    sig.AddArgument(lvi.LocalType, lvi.IsPinned); 
} 
ilInfo.SetLocalSignature(sig.GetSignature()); 
byte[] code = body.GetILAsByteArray(); 
ILReader reader = new ILReader(method); 
ILInfoGetTokenVisitor visitor = new ILInfoGetTokenVisitor(ilInfo, code); 
reader.Accept(visitor); 
ilInfo.SetCode(code, body.MaxStackSize); 

경우, thid 작동합니다. 당신의 방법은 일반적인 하나 인 경우

, 당신은 DynamicMethod 생성자에 소유자 유형을 전달하는이 작업을 수행 할 필요가 : 자사가 여전히 작동하고 있지 않은 경우

var owner = method.DeclaringType.MakeGenericType(
      method.DeclaringType.GetGenericArguments()); 

한 가지 더, 당신의 방법은 인스턴스입니다 메서드에서 DynamicMethod 생성자의 paramters 배열의 첫 번째 셀에 메서드의 instacne 형식을 전달합니다.

정적 방법은 아닙니다 dm.Invoke(**null**, new object[] { "World" });myAction 때문에 여기 null를 전달할 수 없습니다 업데이트

.

myAction (Action<string>)은 실제로이 메소드를 보유하는 새로운 생성 클래스의 메소드입니다.

그러나 확인한 결과 myAction.Target 또는 해당 유형의 새 인스턴스를 전달하더라도 예외가 throw됩니다. 예외 (CLR 잘못된 프로그램을 dedes) IL은 정확하지 않다는 것을 알려줍니다.나는 당신에게 문제가 무엇인지 정확히 말할 수는 없지만, 당신에게 중요하다면, 나는 다시 일할 때 다음 주에 그것을 확인할 수 있습니다.

어쨌든 당신은 그냥 당신이 당신의 코드를 사용할 수 있습니다 행동에 DynamicIlInfo.SetCode를 볼 수 있지만 그냥이의 방법의 정보를 변경하려면이에

class Program 
{   
    static void Main(string[] args) 
    { 
     Action<string> myAction = s => 
     { 
      Console.WriteLine("Hello " + s); 
     }; 
     MethodInfo method = myAction.GetMethodInfo(); 

     //Rest of your code 
    } 
} 

:

class Program 
{ 
    static void M(string s) 
    { 
     Console.WriteLine("Hello " + s); 
    } 

    static void Main(string[] args) 
    { 
     MethodInfo method = typeof (Program).GetMethod("M", BindingFlags.Static | BindingFlags.NonPublic); 

     //Rest of your code 
    } 
} 

업데이트 2 :

분명히 나는 ​​어제 매우 피곤했습니다. 나는 당신의 실수를 깨닫지 못했습니다. 그 여전히 작동하지 않는, 그리고 메소드가 인스턴스 메소드의 경우

내 원래의 대답에 쓴 것처럼,

한가지 더,의 paramters의 첫 번째 셀에 방법의 instacne 유형을 통과 DynamicMethod 생성자 배열입니다.

그래서 당신은이 작업을 수행해야합니다

DynamicMethod dm = new DynamicMethod(
    method.Name, 
    method.ReturnType, 
    new[] {method.DeclaringType}. 
     Concat(method.GetParameters(). 
     Select(pi => pi.ParameterType)).ToArray(), 
    method.DeclaringType, 
    skipVisibility: true); 

그리고 동적 방법을 다음과 같이 호출 :

dm.Invoke(myAction.Target, new object[] { myAction.Target, "World" }); 

것은 지금은 작업을 완벽.

+0

'ILInfoGetTokenVisitor'가 선언 된 곳은 어디입니까? – Sjoerd222888

+0

@ Sjoerd222888 [여기.] (https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/01/02/35/ 08/DynamicMethodHelper.cs) 그리고 바로 [여기]이 (https://blogs.msdn.microsoft.com/haibo_luo/2006/11/07/turn-methodinfo-to-dynamicmethod/) –

+0

I에 대한 블로그 게시물이 있습니다 'System.Reflection.TargetInvocationException'메시지와 함께 "{"Bad binary signature. (HRESULT 예외 : 0x80131192) "}"동적 메서드를 호출하려고 할 때. 나는 근본적인 무언가를 놓친다 고 생각한다. – Sjoerd222888