AllowPartiallyTrustedCallers 특성이 적용된 GAC 어셈블리에서 DynamicMethod의 calli 명령어 사용
calli 명령어 사용법에 대해 지난 글에서 대략 설명드렸는데요.
calli IL 호출이 DllImport 호출보다 빠를까요?
; https://www.sysnet.pe.kr/2/0/10808
OpenCover 코드 커버리지 도구의 동작방식을 통해 살펴보는 Calli IL 코드 사용법
; https://www.sysnet.pe.kr/2/0/2882
calli IL 코드를 사용하는 메서드를 만들기 위해 직접 DynamicMethod를 사용했습니다.
// x86 기준 calli IL 코드를 사용하는 동적 메서드 생성
long result = 0;
if (IntPtr.Size == 4)
{
result = GetThisThreadId32();
}
var type = typeof(Class1);
DynamicMethod dynamicMethod = new DynamicMethod("", typeof(int), Type.EmptyTypes, type, true);
var iLGenerator = dynamicMethod.GetILGenerator();
if (IntPtr.Size == 4)
{
iLGenerator.Emit(OpCodes.Ldc_I4, (int)result);
}
iLGenerator.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, typeof(int), Type.EmptyTypes);
iLGenerator.Emit(OpCodes.Ret);
GetThisThreadIdDelegate tempDelegate = dynamicMethod.CreateDelegate(typeof(GetThisThreadIdDelegate)) as GetThisThreadIdDelegate;
_GetThisThreadIdMethod = tempDelegate;
재미있는 것은 이 코드를 .NET 4.0 보안의 APTCA가 적용된 어셈블리 내에 두면,
.NET CLR4 보안 모델 - 3. CLR4 보안 모델에서의 APTCA 역할
; https://www.sysnet.pe.kr/2/0/1682
dynamicMethod.CreateDelegate 단계까지 정상적으로 실행은 되지만 그렇게 해서 생성한 _GetThisThreadIdMethod 메서드를 호출하면 다음과 같은 예외가 발생합니다.
System.Security.VerificationException was caught
_HResult=-2146233075
_message=Operation could destabilize the runtime.
HResult=-2146233075
IsTransient=false
Message=Operation could destabilize the runtime.
Source=ClassLibrary1
StackTrace:
at ()
at ClassLibrary1.Class1.GetThisThreadId()
InnerException:
원인은 DynamicMethod 메서드의 추가를 APTCA가 적용된 어셈블리 내에 있는 타입을 기준으로 했기 때문입니다.
var type = typeof(Class1);
DynamicMethod dynamicMethod = new DynamicMethod("", typeof(int), Type.EmptyTypes, type, true);
따라서, 해당 타입 대신 APTCA가 적용되지 않은 어셈블리에 정의된 타입을 대신 넣어주면 됩니다. 가령, 이런 식이겠지요.
// GAC 어셈블리의 GetThisThreadId를 호출할 때 별도의 타입을 지정
class Program
{
static void Main(string[] args)
{
ClassLibrary1.Class1.GetThisThreadId(typeof(Program));
}
}
// ClassLibrary1.Class1.GetThisThreadId에서는 외부의 타입을 기반으로 동적 메서드 추가
using System;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
[System.Security.SecuritySafeCritical]
public class Class1
{
// ...[생략]...
public static int GetThisThreadId(Type type)
{
long result = 0;
// ...[생략]...
DynamicMethod dynamicMethod = new DynamicMethod("", typeof(int), Type.EmptyTypes, type, true);
// ...[생략]...
GetThisThreadIdDelegate tempDelegate = dynamicMethod.CreateDelegate(typeof(GetThisThreadIdDelegate)) as GetThisThreadIdDelegate;
_GetThisThreadIdMethod = tempDelegate;
return _GetThisThreadIdMethod();
}
}
}
근데... 이런 식은 좀 번거로우니 의존성을 제거하기 위해 아예 어셈블리 및 타입도 동적으로 생성해 주는 방법이 있습니다.
AssemblyName myAssemblyName = new AssemblyName();
myAssemblyName.Name = "EmittedAssembly_" + Guid.NewGuid().ToString();
AssemblyBuilder myAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(myAssemblyName,
AssemblyBuilderAccess.Run);
ModuleBuilder myModule = myAssembly.DefineDynamicModule("EmittedModule");
TypeBuilder helperClass = myModule.DefineType("Helper", TypeAttributes.Public);
Type type = helperClass.CreateType();
DynamicMethod dynamicMethod = new DynamicMethod("", typeof(int), Type.EmptyTypes, type, true);
어떤 것을 사용하든... 취향에 맞게! ^^
(
첨부한 파일은 위의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]