zhouqijie

动态平台调用

如果要调用的非托管函数没有位于所有可供搜索的路径下,那么就无法对非托管函数进行平台调用。这时可以使用动态平台调用。



通过手动加载非托管DLL实现动态平台调用

在CLR开始搜索DLL并加载之前,先手动用Win32APILoadLibary来加载DLL。这样CLR就可以搜索到该DLL并进行平台调用了。

[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
public static extern IntPtr LoadLibrary(string lpLibFileName);
//...
IntPtr dllAddr = LoadLibrary(dllPath);
//...



利用反射实现动态平台调用

  1. 使用DefineDynamicAssembly创建一个动态程序集对象。
  2. 使用DefineDynamicModule为该程序集定义一个动态模块。
  3. 使用DefinePInvokeMethod为该模块定义一个用于访问非托管函数的P/Invoke方法。
  4. 调用CreateGlobalFunctions函数完成动态模块的全局函数定义和全局数据定义。
  5. 调用GetMethod方法获得方法并进行调用。



使用GetDelegateForFunctionPointer实现动态平台调用

前面两种方法是.NET Framework 1.0和1.1下进行动态平台调用的主要方法。从.NET Framework2.0开始引入了一个能够进行动态平台调用的新技术,即Marshal.GetDelegateForFunctionPointer

主要步骤:

  1. 为非托管函数定义一个委托。
  2. 使用Win32APILoadLibrary加载非托管DLL。
  3. 使用Win32APIGetProcAddress获取非托管函数地址。
  4. 使用Marshal.GetDelegateForFunctionsPointer将上述函数地址封送到第一步定义的委托。
  5. 使用委托调用函数。
  6. 使用Win32APIFreeLibrary释放DLL。
//1
[UnmanagedFunctionPointer(CallingConvertion.StdCall)]
delegate int MultiplyDelegate(int x, int y);

public static void Test()
{
    string entryPoint = "_Multiply@8";
    string currentDir = (Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
    string dllPath = Path.Combine(currentDir, @"nativelibfordynamicpinvoke\NativeLibForDynamicPInvoke.dll");

    //2
    IntPtr dllAddr = Win32API.LoadLibrary(dllPath);
    //3
    IntPtr procAddr = Win32API.GetProcAddress(dllAddr, entryPoint);
    //4
    MultiplyDelegate multiplyDel = (MultiplyDelegate)Marshal.GetDelegateForFunctionPointer(procAddr, typeof(MultiplyDelegate));
    //5
    int result = multiplyDel(233, 233);
    //6
    bool isFree = Win32API.FreeLibrary(dllAddr);
}
//已忽略错误和异常处理  

(END)