람다 식을 IL 바이트 스트림으로 보조 AppDomain에 전달한 다음 DynamicMethod를 사용하여 다시 조립할 수 있습니까? 불리다? 나는이 그래서 여기에, 처음에 갈 수있는 올바른 방법이다 나는이 질문을 물어 (상세) 이유도 확실하지 않다보조 AppDomain에 람다를 IL의 스트림으로 전달하고 DynamicMethod를 사용하여 다시 어셈블
... 내 응용 프로그램에서
, 많은 경우를가 리플렉션을 위해 두 개의 어셈블리를로드해야 할 때 다음에 수행 할 작업을 결정할 수 있습니다. 문제의 일부는 어셈블리를 언로드 한 후에 어셈블리를 언로드 할 수 있어야한다는 것입니다. 즉, 다른 AppDomain
을 사용하여로드해야합니다.
지금 내 사례의 대부분은 유사하지 않지만 아주 비슷합니다. 예를 들어, 때때로 어셈블리에서 리소스 스트림을 serialize해야하는 다른 시간에 간단한 확인을 반환해야하며, 콜백 또는 두 번해야 할 경우도 있습니다.
그래서 반쯤 복잡한 임시 코드 AppDomain
작성 코드를 반복 작성하고 사용자 정의 MarshalByRefObject
프록시를 구현하여 새 도메인과 원래 도메인간에 통신합니다. 실행 나를
using (var reflector = new AssemblyReflector(@"C:\MyAssembly.dll"))
{
bool isMyAssembly = reflector.Execute(assembly =>
{
return assembly.GetType("MyAssembly.MyType") != null;
});
}
AssemblyReflector
이 IDisposable
의 미덕으로 AppDomain
하역을 automize 것, 허용이 더 이상 정말 허용되지 않습니다으로
, 나는 나에게이 방법을 사용할 수있는 AssemblyReflector
클래스를 코드로 결정 반사 코드를 다른 AppDomain
에 투명하게 유지하는 Func<Assembly,object>
유형 람다.
문제는 람다를 다른 도메인으로 간단하게 전달할 수 없다는 것입니다. 그래서 주위를 검색 한 후, 나는 그것을 수행하는 방법처럼 보이는 것을 발견했습니다 : 람다를 IL 스트림으로 새로운 AppDomain
에 전달하십시오 - 그러면 원래의 질문으로 연결됩니다. 여기
BadImageFormatException
발생되고 있었다) 시도했지만 작동하지 않았다 무엇 :
public delegate object AssemblyReflectorDelegate(Assembly reflectedAssembly);
public class AssemblyReflector : IDisposable
{
private AppDomain _domain;
private string _assemblyFile;
public AssemblyReflector(string fileName) { ... }
public void Dispose() { ... }
public object Execute(AssemblyReflectorDelegate reflector)
{
var body = reflector.Method.GetMethodBody();
_domain.SetData("IL", body.GetILAsByteArray());
_domain.SetData("MaxStackSize", body.MaxStackSize);
_domain.SetData("FileName", _assemblyFile);
_domain.DoCallBack(() =>
{
var il = (byte[])AppDomain.CurrentDomain.GetData("IL");
var stack = (int)AppDomain.CurrentDomain.GetData("MaxStackSize");
var fileName = (string)AppDomain.CurrentDomain.GetData("FileName");
var args = Assembly.ReflectionOnlyLoadFrom(fileName);
var pars = new Type[] { typeof(Assembly) };
var dm = new DynamicMethod("", typeof(object), pars,
typeof(string).Module);
dm.GetDynamicILInfo().SetCode(il, stack);
var clone = (AssemblyReflectorDelegate)dm.CreateDelegate(
typeof(AssemblyReflectorDelegate));
var result = clone(args); // <-- BadImageFormatException thrown.
AppDomain.CurrentDomain.SetData("Result", result);
});
// Result obviously needs to be serializable for this to work.
return _domain.GetData("Result");
}
}
가 있습니까 I (무엇을 놓치고?), 또는이 경우에도 가까운 무의미한 운동은 모두?
참고 :이 방법이 효과가 있었다면 참고 문헌과 관련하여 람다에 넣은 것에 대해 계속주의를 기울여야한다는 것을 알고 있습니다. 그래도 문제가되지 않습니다.
업데이트 : 나는 그럭저럭 조금 더 얻을 수 있었다. 단순히 SetCode(...)
을 호출하는 것만으로는 그 방법을 재구성하기에는 충분하지 않은 것 같습니다. 필요한 것은 다음과 같습니다.
// Build a method signature. Since we know which delegate this is, this simply
// means adding its argument types together.
var builder = SignatureHelper.GetLocalVarSigHelper();
builder.AddArgument(typeof(Assembly), false);
var signature = builder.GetSignature();
// This is the tricky part... See explanation below.
di.SetCode(ILTokenResolver.Resolve(il, di, module), stack);
dm.InitLocals = initLocals; // Value gotten from original method's MethodInfo.
di.SetLocalSignature(signature);
트릭은 다음과 같습니다. 원본 IL에는 원본 메타 데이터의 문맥에서만 유효한 특정 메타 데이터 토큰이 포함됩니다. IL을 구문 분석하고 새로운 토큰에서 유효한 토큰으로 대체해야했습니다. 특수 클래스 인 ILTokenResolver
을 사용하여이 작업을 수행했습니다.이 소스는 Drew Wilson 및 Haibo Luo입니다.
새로운 IL은 아직 유효하지 않습니다.람다의 정확한 내용에 따라 런타임시 InvalidProgramException을 던지거나 던지지 않을 수도 있습니다.
간단한 예를 들어,이 작품 :
reflector.Execute(a => { return 5; });
를이하지 않는 동안 :
reflector.Execute(a => { int a = 5; return a; });
중 일부 미처 작업 여부에 따라 있습니다 또한 더 복잡한 사례가있다 결정할 차이. 작지만 중요한 세부 사항을 놓친 것일 수 있습니다. 그러나 필자는 ildasm 결과물을보다 자세히 비교 한 후에 그것을 발견 할 것이라고 확신한다. 내가 할 때 여기에 내 결과를 게시 할 것입니다.
편집 : 오, 이런. 나는이 질문이 아직도 열려 있다는 것을 완전히 잊었다. 그러나 아마 그 자체로 명백 해졌을 때, 나는 이것을 해결하는 것을 포기했다. 나는 그것에 대해 행복하지 않다. 그것은 확실하다. 정말 부끄러운 일이지만, 다시 시도하기 전에 프레임 워크 및/또는 CLR에서 더 나은 지원을 기다릴 것입니다. 이 작업을하기 위해해야하는 많은 해킹이 있습니다. 심지어 신뢰할 수 없습니다. 관심있는 모든 사람에게 사과.
나는 그것을 알고있었습니다. 하지만 람다에서 외부 변수를 사용하지 않으면 문제가되지 않을 것이라고 생각했습니다. 람다 대신 일반 오래된 대리인을 사용하는 경우는 어떻습니까? – aoven
요점은 람다가 표현식의 코드가 아니라는 점입니다. 또한 컴파일 타임에 앱의 다른 코드를 변환합니다. 컴파일 타임 변경을 이미 컴파일 된 다른 어셈블리로 전송할 수 있습니다. –