关于C#调用非托管动态库方式的性能疑问

  最近的项目中,因为一些原因,需要C#调用非托管(这里为C++)的动态库。网上喜闻乐见的方式是采用静态(DllImport)方式进行调用。偶然在园子里看到可以用动态(LoadLibrary,GetProcAddress,FreeLibrary)方式调用非托管动态库,于是就想着比较一下静态和动态方式的性能(主要想用运行时间来体现)。

  以下为源码:

  1.主程序源码:

 1 using System;
 2 using System.Diagnostics;
 3 using System.Text;
 4 using System.Threading;
 5 
 6 namespace DllImportDemo
 7 {
 8     internal class Program
 9     {
10       
11         private static void Main()
12         {
13 
14             const int callCount = 100000;
15             Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
16             Thread.CurrentThread.Priority = ThreadPriority.Highest;
17             var watch = new Stopwatch();
18             watch.Start();
19             while (watch.ElapsedMilliseconds < 2000)
20             {
21                 
22             }
23             watch.Stop();
24             watch.Restart();
25             Console.WriteLine("DllImportStatic will be called.");
26             for (var i = 0; i < callCount; i++)
27             {
28                 DllImportStatic();
29             }
30             watch.Stop();
31             Console.WriteLine("It costs {0}ms when DllImportStatic is called {1} times.",
32                 watch.ElapsedMilliseconds, callCount);
33 
34             watch.Restart();
35             Console.WriteLine("DllImportDynamic will be called.");
36             for (var i = 0; i < callCount; i++)
37             {
38                 DllImportDynamic();
39             }
40             watch.Stop();
41             Console.WriteLine("It costs {0}ms when DllImportDynamic is called {1} times.",
42                 watch.ElapsedMilliseconds, callCount);
43             Console.Read();
44         }
45 
46         private static void DllImportStatic()
47         {
48             IniHelper.ReadIniData("info", "v.productname", string.Empty, "./extends.properties");
49         }
50 
51         private static void DllImportDynamic()
52         {
53             ReadIniData("info", "v.productname", string.Empty, "./extends.properties");
54         }
55 
56         private delegate long GetPrivateProfileString(string section, string key,
57             string def, StringBuilder retVal, int size, string filePath);
58 
59         private static string ReadIniData(string section, string key, string noText, string iniFilePath)
60         {
61             var invoker = new DllInvoker("kernel32.dll");
62 
63             var profileString = (GetPrivateProfileString)invoker.Invoke("GetPrivateProfileStringA",
64                 typeof(GetPrivateProfileString));
65 
66             var valueItem = new StringBuilder(1024);
67 
68             profileString(section, key, noText, valueItem, 1024, iniFilePath);
69 
70             return valueItem.ToString();
71         }
72     }
73 
74 
75 }

  2.动态调用托管动态库源码(部分代码参考网络)

 1 using System;
 2 using System.Runtime.InteropServices;
 3 
 4 namespace DllImportDemo
 5 {
 6     class DllInvoker
 7     {
 8 
 9         [DllImport("kernel32.dll")]
10         private extern static IntPtr LoadLibrary(String path);
11 
12         [DllImport("kernel32.dll")]
13         private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
14 
15         [DllImport("kernel32.dll")]
16         private extern static bool FreeLibrary(IntPtr lib);
17 
18         private readonly IntPtr _hLib;    
19  
20         public DllInvoker(string dllPath)
21         {
22             _hLib = LoadLibrary(dllPath);   
23         }
24 
25         ~DllInvoker()      
26         {         
27             FreeLibrary(_hLib);   
28         }         
29          
30         /// <summary>
31         /// 将要执行的函数转换为委托
32         /// </summary>
33         /// <param name="apiName"></param>
34         /// <param name="type">委托类型</param>
35         /// <returns></returns>
36         public Delegate Invoke(string apiName, Type type)
37         {
38             var api = GetProcAddress(_hLib, apiName);
39             return Marshal.GetDelegateForFunctionPointer(api, type);
40         }  
41     }
42 }
View Code

  3.静态调用动态库源码(部分代码参考网络)

 1 using System;
 2 using System.IO;
 3 using System.Runtime.InteropServices;
 4 using System.Text;
 5 
 6 namespace DllImportDemo
 7 {
 8     public class IniHelper
 9     {
10         #region API函数声明
11 
12         /// <summary>
13         /// ini文件读取函数
14         /// </summary>
15         /// <param name="section">ini文件节点名称</param>
16         /// <param name="key">节点下面项的key值</param>
17         /// <param name="def">项key对应值的默认值,如果通过项key查找到结果,返回该结果;
18         /// 如果未找到,返回指定的默认值</param>
19         /// <param name="retVal">字符串缓冲区,即返回结果的存储区</param>
20         /// <param name="size">字符串缓冲区初始大小</param>
21         /// <param name="filePath">ini文件路径</param>
22         /// <returns>返回取得字符串缓冲区的长度</returns>
23         [DllImport("kernel32")]
24         private static extern long GetPrivateProfileString(string section, string key,
25             string def, StringBuilder retVal, int size, string filePath);
26 
27         #endregion
28 
29         #region 读Ini文件
30 
31         public static string ReadIniData(string section, string key, string noText, string iniFilePath)
32         {
33             if (File.Exists(iniFilePath))
34             {
35                 var temp = new StringBuilder(1024);
36 
37                 GetPrivateProfileString(section, key, noText, temp, 1024, iniFilePath);
38                
39                 return temp.ToString();
40             }
41             return String.Empty;
42         }
43 
44         #endregion
45     }
46 }
View Code

 

  4.运行结果:

 

 

  疑问:

  从表面来看,读取ini文件的静态方式,只采用了一次[DllImport],动态调用时采用了两次[DllImport](FreeLibrary在析构函数中调用),按道理静态方式执行速度会快一些,但结果却相反,不知道是理解的问题还是其它什么原因?

[DllImport("kernel32")]
private static extern long GetPrivateProfileString(string section, string key,string def, StringBuilder retVal, int size, string filePath);

 

 

 

 

  

  

posted @ 2016-02-02 11:43  LightSmaile  阅读(516)  评论(0编辑  收藏  举报