天天看点

.NET简谈互操作(五:基础知识之Dynamic平台调用)

平台调用过程原理

文字使用始终没有图片的表达性强,我们还是来看图吧;

图1:

.NET简谈互操作(五:基础知识之Dynamic平台调用)

这幅图画的不是很全,但是大概能表达意思了;

当我们第一次调用非托管DLL文件的时候(穿插一下,这里就牵扯到为什么有些东西必须由操作系统来处理,为什么要有内核,就是用来处理一些我们平时不能随便动的东西,就拿LoadLibrary方法来讲,可能它就是进入了内核然后设置相关参数,帮我们保存了非托管DLL在内存的代理存根,当我们下次又进入到内核的时候,系统去检查一下,发现有过一次调用了,所以下次就去读取存根中的地址进行调用),系统会去加载非托管DLL文件到内存并设置相关数据,以便后期使用;动态调用的原理就是我们把这部分的工作自己手动来做,比如第一次调用非托管DLL肯定是要慢于后面调用的;所以在一些必要的场合下,我们真的有必要进行动态P/Invoke;

动态平台调用示例1

在托管的.NET中我们可以通过使用Win32API中的LoadLibrary方法来手动加载非托管DLL到内存来;

 [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]

 public static extern IntPtr LoadLibrary(string iplibfilenmae);

这样的操作就好比我们图1中的第一次调用过程要执行的操作;

 [DllImport("Win32DLL.dll", EntryPoint = "add", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

 private static extern int add(int x, int y);

同样我们还是申明非托管代码的定义,我们来看全部代码;

namespace CSharp.Interop

{

    /// <summary>

    /// 动态平台调用,手动加载非托管DLL文件

    /// </summary>

    public static class DynamicPinvoke

    {

        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]

        public static extern IntPtr LoadLibrary(string iplibfilenmae);

        [DllImport("Win32DLL.dll", EntryPoint = "add", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

        private static extern int add(int x, int y);

        public static void Test()

        {

            string currentdirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            string dllpath = Path.Combine(currentdirectory, "Win32DLL.dll");

            IntPtr dlladdr = LoadLibrary(dllpath);

            if (dlladdr == IntPtr.Zero)

                throw new DllNotFoundException(string.Format("在{0}未能找到相关DLL文件", dllpath));

            int addnumber = add(10, 20);

            Console.Write(addnumber);

        }

    }

}

动态平台调用示例2

第一个示例我们是省略了系统调用过程,我们手动调用LoadLibrary来加载;可能没啥大的变化,示例2是通过非托管函数委托来进行动态调用的;

都知道托管委托就好比非托管的函数指针,幸好微软为我们提供了委托来调用非托管方法,适合真的很强大;请看代码;

delegate int add(int x, int y);

系统特性能改变代码的编译行为,所以我们有理由相信我们的add委托已经变成了非托管代码的引用;

    [UnmanagedFunctionPointer(CallingConvention.StdCall)]

    delegate int add(int x, int y);

    public static class DelegateInvoke

            IntPtr dlladdr = Interop.DynamicPinvoke.LoadLibrary(dllpath);

            IntPtr procadd = Win32Api.GetProcAddress(dlladdr, "_add@8");

            if (procadd == IntPtr.Zero)

                throw new DllNotFoundException(string.Format("未能在内存中找到{0}入口点", Marshal.PtrToStringUni(dlladdr)));

            add adddelegate = (add)Marshal.GetDelegateForFunctionPointer(procadd, typeof(add));

            int result = adddelegate(10, 20);

            bool isfree = Win32Api.FreeLibrary(dlladdr);

Marshal是一个很强大的P/Invoke的类,可以将它看成是平台调用的体现吧,Marshal.GetDelegateForFunctionPointer方法是通过非托管内存指针获取UnmanagedFunctionPointer类型的委托;

总结:其实动态调用就是让我们竟可能的多去接触底层知识,一切都是可以理解的,只是功夫没到家;