2017-09-05 16 views
0

Mono.Cecil의 경우 MethodDefinitionBody을 의 Body으로 설정하면 매우 간단 해 보입니다. 간단한 방법의 경우에는 정상적으로 작동합니다. 그러나 사용자 정의 유형 (예 : 새 객체 초기화)이 사용되는 반면 일부 메소드의 경우 작동하지 않습니다 (어셈블리를 다시 쓰는 동안 예외가 발생 함).Mono.Cecil을 사용하여 메서드의 본문을 다른 메서드의 본문으로 바꿉니 까?

이 코드는 위의 어느 곳에서 참조되지 않은
//in current app 
public class Form1 { 
    public string Test(){ 
    return "Modified Test"; 
    } 
} 
//in another assembly 
public class Target { 
    public string Test(){ 
    return "Test"; 
    } 
} 

//the copying code, this works for the above pair of methods 
//the context here is of course in the current app 
var targetAsm = AssemblyDefinition.ReadAssembly("target_path"); 
var mr1 = targetAsm.MainModule.Import(typeof(Form1).GetMethod("Test")); 
var targetType = targetAsm.MainModule.Types.FirstOrDefault(e => e.Name == "Target"); 
var m2 = targetType.Methods.FirstOrDefault(e => e.Name == "Test"); 
var m1 = mr1.Resolve(); 
var m1IL = m1.Body.GetILProcessor(); 

foreach(var i in m1.Body.Instructions.ToList()){ 
    var ci = i; 
    if(i.Operand is MethodReference){ 
     var mref = i.Operand as MethodReference; 
     ci = m1IL.Create(i.OpCode, targetType.Module.Import(mref)); 
    } 
    else if(i.Operand is TypeReference){ 
     var tref = i.Operand as TypeReference; 
     ci = m1IL.Create(i.OpCode, targetType.Module.Import(tref)); 
    } 
    if(ci != i){ 
     m1IL.Replace(i, ci); 
    } 
} 
//here the source Body should have its Instructions set imported fine 
//so we just need to set its Body to the target's Body 
m2.Body = m1.Body; 
//finally write to another output assembly 
targetAsm.Write("modified_target_path"); 

, 난 그냥 그것을 자신을 시도하고 (예 : 제가 위에서 게시 된이 방법 Test 만) 간단한 경우 작동 발견 : 여기

내 코드입니다 . (현재 응용 프로그램에 정의) 소스 메서드는이 같은 몇 가지 유형 참조 (예 : 일부 생성자 초기화를 ...), 포함한다면 :
public class Form1 { 
    public string Test(){ 
    var u = new Uri("SomeUri"); 
    return u.AbsolutePath; 
    } 
} 

는 그런 다음 어셈블리를 다시 작성시 실패합니다. 던져진 예외는 다음과 같은 메시지 ArgumentException입니다 :

"다른 모듈에 선언 된 회원 '선택 System.Uri'수입해야"사실

내가 비슷한 메시지 전에가 발생했습니다 하지만 (string.Concat)와 같은 메소드 호출 용입니다. 그래서 내가 MethodReference을 가져 오려고 시도한 것입니다 (게시 한 코드의 foreach 루프 내에서 if을 볼 수 있습니다). 그리고 그 사실은이 경우에 효과가있었습니다. 하지만이 경우는 다릅니다. 사용 된/참조 된 형식 (이 경우에는 System.Uri)을 올바르게 가져 오는 방법을 알지 못합니다. Import의 결과를 사용해야한다는 것을 알고 있으므로 MethodReference의 경우 각 Instruction에 대해 Operand을 바꾸는 데 결과가 사용됨을 알 수 있습니다. 하지만이 경우 유형 참조에 대해서는 전혀 모릅니다.

여기이 경험이있는 사람이 Mono.Cecil으로이 문제를 해결할 수 있기를 바랍니다. 나는 그것이 단순해야한다고 생각하지만 모노를 이해하지 못할 수도 있습니다. 잘 지켜라. 도와 줘서 고마워.

+0

이 새로운 방법에 대한 호출로 몸을 대체하기 위해 훨씬 더 간단하지 않을까요 : 여기 는 VariableDefinition를 가져올 단지 유사한 코드? –

+0

@JeroenMostert 소스'Test' 메소드는 단지 간단한 코드 일뿐입니다. 실제로 복잡한 코드 (수십 줄이 포함 된 ...)가 될 수 있습니다. 따라서 매번 이러한 코드를 매번 'Instructions'로 변환하면 어렵고 전혀 흥미롭지 않습니다. 기존의 방법을 사용하여 다른 어셈블리에 정의 된 다른 코드를 대체하고 싶습니다.나는 이것이 모노와 함께 할 수 있다고 생각한다. 세실. – Hopeless

+1

아니요, 요점은 - 원하는 메서드 본문이 이미 올바르게 컴파일 된 것입니다 (형식 및 어셈블리 참조와 전체 후프 포함). 새로운 몸체로 이식하려고하는 대신,'Source.Test' 메서드 본문을'Target.Test'에 대한 호출로 대체하지 않을까요? (별도 어셈블리의 존재가 문제가 있다면 먼저 ILMerge하십시오.) 이것은 소스 또는 타겟이 얼마나 복잡한 지 상관없이 작동합니다. –

답변

0

내 모든 코드는 내 질문에 게시되지만 괜찮지는 않습니다. 실제로 예외 메시지 :

'회원'선택 System.Uri '다른 모듈에 선언 수입 할 필요가있다 "

대해 불평 VariableDefinitionVariableType. 방금 지시문을 가져 오지만 변수 (정확히 소스 코드 MethodBody에서 참조)는 가져 오지 않습니다. 그래서 해결책은 같은 방법으로 변수를 가져와야합니다. ExceptionHandlerCatchType을 가져와야하므로 ExceptionHandlers도 가져올 수 있습니다.

var vars = m1.Body.Variables.ToList(); 
m1.Body.Variables.Clear(); 
foreach(var v in vars){ 
    var nv = new VariableDefinition(v.Name, targetType.Module.Import(v.VariableType)); 
    m1.Body.Variables.Add(nv); 
}