• 关于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);

      

      

  • 相关阅读:
    winform程序,备份数据库+并压缩+并删除以前的备份
    冒泡排序
    存储过程和SQL语句比较
    简单的firebird插入速度测试
    [收藏转]由于CredSSP加密Oracle修正 导致远程桌面报错处理
    由Handle转换为控件
    字符串格式化
    查询mssql的死锁语句
    读取和修改app.config文件
    json序列化与反序列化
  • 原文地址:https://www.cnblogs.com/LightSmile/p/5177131.html
Copyright © 2020-2023  润新知