Flier's Sky

天空,蓝色的天空,眼睛看不到的东西,眼睛看得到的东西

导航

    Junfeng Zhang在其 BLog 的文章,What is new in .Net framework 2.0 (7) --- Marshal.GetDelegateForFunctionPointer,中介绍了 .NET 2.0 中如何使用 Marshal.GetDelegateForFunctionPointer 方法,直接从导入的函数指针生成 Delegate。这一功能可以大大减少使用者的薄记工作,让 interop 的操作更加自然。其文中给出的示例代码如下:

using System.Runtime.InteropServices;
using System;

public class TestClass
{
    public static void Main(String[] args)
    {
        IntPtr user32 = LoadLibrary("user32.dll");
        IntPtr procaddr = GetProcAddress(user32, "MessageBoxW");

        Console.In.ReadLine();

        MyMessageBox mbx = (MyMessageBox)Marshal.GetDelegateForFunctionPointer(procaddr, typeof(MyMessageBox));
        mbx(IntPtr.Zero, "Hello, World", "A Test Run", 0);
    }

    internal delegate int MyMessageBox(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)]String text, [MarshalAs(UnmanagedType.LPWStr)]String Caption, int type);

    [DllImport("kernel32.dll")]
    internal static extern IntPtr LoadLibrary(String dllname);

    [DllImport("kernel32.dll")]
    internal static extern IntPtr GetProcAddress(IntPtr hModule, String procname);
}

    这一功能虽然强大,但实现代码非常简洁,通过 ILDASM 我们可以看到如下实现:

.method public hidebysig static void  Main(string[] args) cil managed
{
  IL_0000:  ldstr      "user32.dll"
  IL_0005:  call       native int TestClass::LoadLibrary(string)
  IL_000a:  stloc.0

  IL_000b:  ldloc.0
  IL_000c:  ldstr      "MessageBoxW"
  IL_0011:  call       native int TestClass::GetProcAddress(native int,
                                                            string)
  IL_0016:  stloc.1

  IL_0022:  ldloc.1
  IL_0023:  ldtoken    TestClass/MyMessageBox
  IL_0028:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_002d:  call       class [mscorlib]System.Delegate [mscorlib]System.Runtime.InteropServices.Marshal::GetDelegateForFunctionPointer(native int,
                                                                                                                                       class [mscorlib]System.Type)
  IL_0032:  castclass  TestClass/MyMessageBox
  IL_0037:  stloc.2

  IL_0038:  ldloc.2
  IL_0039:  ldsfld     native int [mscorlib]System.IntPtr::Zero
  IL_003e:  ldstr      "Hello, World"
  IL_0043:  ldstr      "A Test Run"
  IL_0048:  ldc.i4.0
  IL_0049:  callvirt   instance int32 TestClass/MyMessageBox::Invoke(native int,
                                                                     string,
                                                                     string,
                                                                     int32)
  IL_004e:  pop
  IL_004f:  ret
} // end of method TestClass::Main

    而在 mscorlib.dll 中,这个 Marshal::GetDelegateForFunctionPointer 函数实际上是通过一个 native 函数具体完成的:

[SuppressUnmanagedCodeSecurity]
public abstract sealed class Marshal
{
  [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
  public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, Type t)
  {
    if (ptr == IntPtr.Zero) throw new ArgumentNullException("ptr");

    if (t == null) throw new ArgumentNullException("t");

    if (!(t is RuntimeType)) throw new ArgumentException(Environment.GetResourceString("Argument_MustBeRuntimeType"), "t");

    Type type1 = t.BaseType;

    if ((type1 == null) || ((type1 != typeof(Delegate)) && (type1 != typeof(MulticastDelegate))))
      throw new ArgumentException(Environment.GetResourceString("Arg_MustBeDelegate"), "t");

    return Marshal.GetDelegateForFunctionPointerInternal(ptr, t);
  }

  [MethodImpl(MethodImplOptions.InternalCall)]
  internal static Delegate GetDelegateForFunctionPointerInternal(IntPtr ptr, Type t);

}

    而 Marshal.GetDelegateForFunctionPointer 函数的具体实现,是在 mscorwks.dll 中的MarshalNative::GetDelegateForFunctionPointerInternal 完成的,伪代码如下:

OBJECTREF MarshalNative::GetDelegateForFunctionPointerInternal(LPVOID pCallback, TypeHandle *type)
{
  HELPER_METHOD_FRAME_BEGIN_2(type, NULL);

  if(GetThread()->m_State & TS_AbortRequested == TS_AbortRequested)
    Thread::HandleThreadAbort();

  OBJECTREF obj = COMDelegate::ConvertToDelegate(pCallback, type->GetMethodTable());

  if(GetThread()->m_State & TS_AbortInitiated == TS_AbortInitiated)
    Thread::HandleThreadAbort();

  return obj;
}

    除了进行堆栈保护,以及在开始和结束时处理线程中断状态以外,MarshalNative::GetDelegateForFunctionPointerInternal 实际上把大部分工作转交给 COMDelegate::ConvertToDelegate 完成。.NET Framework 2.0 中的 COMDelegate::ConvertToDelegate 函数与 Rotor 中的实现相比起来要复杂许多。Rotor 中此函数只需要处理已经被 Thunk 处理过的回调指针,对非法指针直接抛出参数错误异常。

// 检查 ptr 指向函数入口是否是已经被 Thunk 处理过的
// 如果处理过,则返回被处理后的实际入口地址,否则返回 NULL
//
// vm\i386\cgenx86.cpp:4642
//
UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
{
    if (*((BYTE*)pCallback) != 0xb8 ||
        ( ((size_t)pCallback) & 3) != 2) {
        return NULL;
    }
    return *(UMEntryThunk**)( 1 + (BYTE*)pCallback );
}

// Marshals an unmanaged callback to Delegate
//
// vm\comdelegate.cpp:500
//
OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback)
{
    THROWSCOMPLUSEXCEPTION();

    if (!pCallback) {
        return NULL;
    }

    UMEntryThunk *pUMEntryThunk = UMEntryThunk::Decode(pCallback);
    if (!pUMEntryThunk) {
        COMPlusThrow(kArgumentException, IDS_EE_NOTADELEGATE);
    }
    return ObjectFromHandle(pUMEntryThunk->GetObjectHandle());
}

    .NET 2.0 中的 COMDelegate::ConvertToDelegate 函数则相对来说较为复杂,还需要处理未 Thunk 处理的 Unmanaged 函数指针的情况,大致流程的伪代码如下:

OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable *mtType)
{
  if(ptr == NULL) return NULL;

  if(g_pMdaStaticHeap.m_pMda == NULL)
  {
    // 进行 ManagedDebuggingAssistants 相关的初始化和处理工作
  }

  // 检查 ptr 指向函数入口是否是已经被 Thunk 处理过的
  // 如果处理过,则返回被处理后的实际入口地址,否则返回 NULL
  UMEntryThunk *thunk = UMEntryThunk::Decode(pCallback);

  if(thunk)
  {
    // 在全局唯一的函数指针到 Delegate 缓存 HashMap 中查找是否已经处理过此指针
    Delegate *d = s_pDelegateToFPtrHash.LookupValue(thunk, NULL);

    if(d != -1) return d;
  }

  // 查找此 Delegate 是否已经由 JIT 生成方法体
  MethodDesc *md = FindDelegateInvokeMethod(mtType->m_pEEClass);

  if(md == NULL)
  {
    // 使用 StubLinkerCPU 类通过 ComputeDllImportStub 函数构造方法体
  }

  // 对于泛型类,则使用 CreateGenericDllImportStubForDelegate 函数进行处理

  // 是否启用全局安全设置
  if (Security::s_dwGlobalSettings & CORSETTING_SECURITY_OFF == 0)
  {
    Module *mod = mtType->getModule();

    PEFile *file = mod->GetPEFile();

    IMDInternalImport *imp = file->GetMDImport();

    if(关心文件输入节的安全性)
    {
      DeclActionInfo *act = Security::DetectDeclActions(md, DECLSEC_UNMNGD_ACCESS_DEMAND);

      if(act)
      {
        // 使用 StubLinkerCPU::EmitSecurityInterceptorStub 等方法添加安全检测代码
      }
    }
  }

  _UNCHECKED_OBJECTREF oref;

  SetObjectReferenceUnchecked(&oref, mtType->Allocate());

  return UNCHECKED_OBJECTREF_TO_OBJECTREF(oret);
}