C#中DllImport用法匯總

來源:互聯網
上載者:User

標籤:style   blog   http   color   io   os   使用   ar   for   

(轉)

最近使用DllImport,從網上google後發現,大部分內容都是相同,又從MSDN中搜集下,現將內容匯總,與大家分享。

 

大家在實際工作學習C#的時候,可能會問:為什麼我們要為一些已經存在的功能(比如Windows中的一些功能,C++中已經編寫好的一些方法)要重新編寫代碼,C#有沒有方法可以直接都用這些原本已經存在的功能呢?答案是肯定的,大家可以通過C#中的DllImport直接調用這些功能。
DllImport是System.Runtime.InteropServices命名空間下的一個屬性類,因此ASP.NET中要使用DllImport的,必須在先“using System.Runtime.InteropServices;”。其功能是提供從非託管DLL匯出的函數進行調用所必需的資訊。DllImport屬性應用於方法,要求最少要提供包含進入點的dll的名稱。

DllImport 屬性定義
如下:
namespace System.Runtime.InteropServices
   {
    [AttributeUsage(AttributeTargets.Method)]
    public class DllImportAttribute: System.Attribute
    {

 

 

public DllImportAttribute(string dllName){...}   //定位參數為dllName
public CallingConvention CallingConvention;      //進入點呼叫慣例
public CharSet CharSet;                          //進入點採用的字元接
public string EntryPoint;                        //進入點名稱
public bool ExactSpelling;                       //是否必須與指示的進入點拼字完全一致,預設false
public bool PreserveSig;                         //方法的簽名是被保留還是被轉換
public bool SetLastError;                        //FindLastError方法的傳回值儲存在這裡
public string Value {get {...}}                            

 

 

    } 
}
說明:
1、DllImport只能放置在方法聲明上。
2、DllImport具有單個定位參數:指定包含被匯入方法的 dll 名稱的 dllName 參數。
3、DllImport具有五個具名引數:
   a、CallingConvention 參數指示進入點的呼叫慣例。如果未指定CallingConvention,則使用預設值CallingConvention.Winapi。
   b、CharSet參數指定用在進入點的字元集。如果未指定CharSet,則使用預設值CharSet.Auto。
   c、EntryPoint參數給出dll中進入點的名稱。如果未指定EntryPoint,則使用方法本身的名稱。
   d、ExactSpelling參數指示EntryPoint是否必須與指示的進入點的拼字完全符合。如果未指定ExactSpelling,則使用預設值false。
   e、PreserveSig參數指示方法的簽名被保留還是被轉換。當簽名被轉換時,它被轉換為一個具有HRESULT傳回值和該傳回值的一個名為retval的附加輸出參數的簽名。如果未指定PreserveSig,則使用預設值true。
   f、SetLastError參數指示方法是否保留Win32“上一錯誤”。如果未指定SetLastError,則使用預設值false。
4、它是一次性屬性類。
5、用DllImport屬性修飾的方法必須具有extern修飾符。
DllImport的用法樣本(是用來寫入ini檔案的一個win32api):
 DllImport("kernel32")
 private static extern long WritePrivateProfileString(string section,string key,string val,string filePath);
用此方法調用WinAPI的資料類型對應:DWORD=int或uint,BOOL=bool,預定義常量=enum,結構=struct。

 

DllImport路徑問題:

 

DllImport會按照順序自動去尋找的地方: 
1、exe所在目錄 
2、System32目錄 
3、環境變數目錄

所以只需要你把引用的DLL 拷貝到這三個目錄下 就可以不用寫路徑了。

 

 

web中的,同時也是應用程式中的
後來發現用[DllImport(@"C:\OJ\Bin\Judge.dll")]這樣指定DLL的絕對路徑就可以正常裝載。
這個問題最常出現在使用第三方非託管DLL組件的時候,我的也同樣是這時出的問題,Asp.Net Team的官方解決方案如下:
首先需要確認你引用了哪些組件,那些是託管的,哪些是非託管的.託管的很好辦,直接被使用的需要引用,間接使用的需要拷貝到bin目錄下.非託管的處理會比較麻煩。實際上,你拷貝到bin沒有任何協助,因為CLR會把檔案拷貝到一個臨時目錄下,然後在那運行web,而CLR只會拷貝託管檔案,這就是為什麼我們明明把非託管的dll放在了bin下卻依然提示不能載入模組了。
具體做法如下:
首先我們在伺服器上隨便找個地方建立一個目錄,假如為C:\DLL;
然後,在環境變數中,給Path變數添加這個目錄;
最後,把所有的非託管檔案都拷貝到C:\DLL中,或者更乾脆的把DLL放到system32目錄。
對於可以自己部署的應用程式,這樣未償不是一個解決辦法,然而,如果我們用的是虛擬空間,我們是沒辦法把註冊PATH變數或者把我們自己的DLL拷到system32目錄的。同時我們也不一定知道我們的Dll的實體路徑。
DllImport裡面只能用字串常量,而不能夠用Server.MapPath(@"~/Bin/Judge.dll")來確定實體路徑。

DllImport載入速度慢的問題:
不過,我發現,調用這種"非託管Dll”相當的慢,可能是因為我的方法需要遠程驗證吧,但是實在是太慢了。經過一翻研究,終於想到了一個完美的解決辦法。
首先我們用
[DllImport("kernel32.dll")]
private extern static IntPtr LoadLibrary(String path);
[DllImport("kernel32.dll")]
private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
[DllImport("kernel32.dll")]
private extern static bool FreeLibrary(IntPtr lib);
分別取得了LoadLibrary和GetProcAddress函數的地址,再通過這兩個函數來取得我們的DLL裡面的函數。
我們可以先用Server.MapPath(@"~/Bin/Judge.dll")來取得我們的DLL的實體路徑,然後再用LoadLibrary進行載入,最後用GetProcAddress取得要用的函數地址。
以下自訂類的程式碼完成LoadLibrary的裝載和函數調用:
public class DllInvoke
    {

        [DllImport("kernel32.dll")]
        private extern static IntPtr LoadLibrary(String path);
        [DllImport("kernel32.dll")]
        private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
        [DllImport("kernel32.dll")]
        private extern static bool FreeLibrary(IntPtr lib);
        private IntPtr hLib;
        public DllInvoke(String DLLPath)
        {
            hLib = LoadLibrary(DLLPath);
        }
        ~DllInvoke()
        {
            FreeLibrary(hLib);          
        }
        //將要執行的函數轉換為委託
        public Delegate Invoke(String APIName,Type t)
        {
            IntPtr api = GetProcAddress(hLib, APIName);
            return (Delegate)Marshal.GetDelegateForFunctionPointer(api,t);
        }
}
下面代碼進行調用
public delegate int Compile(String command, StringBuilder inf);//編譯
DllInvoke dll = new DllInvoke(Server.MapPath(@"~/Bin/Judge.dll"));
Compile compile = (Compile)dll.Invoke("Compile", typeof(Compile));
StringBuilder inf;
compile(@“gcc a.c -o a.exe“,inf); //這裡就是調用我的DLL裡定義的Compile函數

DllImport用法樣本:
一 在C#程式設計中使用Win32類庫 常用對應類型:
1、DWORD 是 4 位元組的整數,因此我們可以使用 int 或 uint 作為 C# 對應類型。
2、bool 類型與 BOOL 對應。
樣本一:調用 Beep() API 來發出聲音
Beep() 是在 kernel32.lib 中定義的,在MSDN 中的定義,Beep具有以下原型:
BOOL Beep(DWORD dwFreq, // 聲音頻率
          DWORD dwDuration // 聲音期間); 
用 C# 編寫以下原型:
[DllImport("kernel32.dll")] 
public static extern bool Beep(int frequency, int duration);
樣本二:枚舉類型和常量
MessageBeep() 是在 user32.lib 中定義的,在MSDN 中的定義,MessageBeep具有以下原型:
BOOL MessageBeep(UINT uType // 聲音類型
                  ); 
用C#編寫一下原型:
public enum BeepType
{
   SimpleBeep = -1,
   IconAsterisk = 0x00000040,
   IconExclamation = 0x00000030,
   IconHand = 0x00000010,
   IconQuestion = 0x00000020,
   Ok = 0x00000000,
}
uType 參數實際上接受一組預先定義的常量,對於 uType 參數,使用 enum 類型是合乎情理的。
[DllImport("user32.dll")]
public static extern boolMessageBeep(BeepType beepType);
樣本三:處理結構
有時我需要確定我筆記本的電池狀況。Win32 為此提供了電源管理函數,搜尋 MSDN 可以找到GetSystemPowerStatus() 函數。
BOOL GetSystemPowerStatus(LPSYSTEM_POWER_STATUS lpSystemPowerStatus);
此函數包含指向某個結構的指標,我們尚未對此進行過處理。要處理結構,我們需要用 C# 定義結構。我們從非託管的定義開始:
typedef struct _SYSTEM_POWER_STATUS {
   BYTE  ACLineStatus;
   BYTE  BatteryFlag;
   BYTE  BatteryLifePercent;
   BYTE  Reserved1;
   DWORD BatteryLifeTime;
   DWORD BatteryFullLifeTime;
} SYSTEM_POWER_STATUS, *LPSYSTEM_POWER_STATUS;
然後,通過用 C# 類型代替 C 類型來得到 C# 版本。
struct SystemPowerStatus
{
  byte ACLineStatus;
  byte batteryFlag;
  byte batteryLifePercent;
  byte reserved1;
  int batteryLifeTime;
  int batteryFullLifeTime;
}
    這樣,就可以方便地編寫出 C# 原型:
    [DllImport("kernel32.dll")]
    public static extern bool GetSystemPowerStatus(ref SystemPowerStatus systemPowerStatus);
在此原型中,我們用“ref”指明將傳遞結構指標而不是結構值。這是處理通過指標傳遞的結構的一般方法。
此函數運行良好,但是最好將 ACLineStatus 和 batteryFlag 欄位定義為 enum:
enum ACLineStatus: byte
{
   Offline = 0,
   Online = 1,
   Unknown = 255,
}
enum BatteryFlag: byte
{
   High = 1,
   Low = 2,
   Critical = 4,
   Charging = 8,
   NoSystemBattery = 128,
   Unknown = 255,
}
請注意,由於結構的欄位是一些位元組,因此我們使用 byte 作為該 enum 的基本類型。
二 C# 中調用C++代碼
int 類型
[DllImport(“MyDLL.dll")] 
public static extern int mySum (int a1,int b1);    //返回個int 類型
extern “C” __declspec(dllexport)  int WINAPI mySum(int a2,int b2)    //DLL中申明
{ 
    //a2 b2不能改變a1 b1
    //a2=..
    //b2=...
 return a+b;
}
//參數傳遞int類型 public static extern int mySum (ref int a1,ref int b1); //DLL中申明 extern “C” __declspec(dllexport) int WINAPI mySum(int *a2,int *b2) { //可以改變 a1, b1 *a2=... *b2=... return a+b; }

DLL 需傳入char *類型 [DllImport(“MyDLL.dll")] //傳入值 public static extern int mySum (string astr1,string bstr1); //DLL中申明 extern “C” __declspec(dllexport) int WINAPI mySum(char * astr2,char *bstr2) { //改變astr2bstr 2 ,astr1 bstr1不會被改變 return a+b; }

DLL 需傳出char *類型 [DllImport(“MyDLL.dll")] // 傳出值 public static extern int mySum (StringBuilder abuf, StringBuilder bbuf ); //DLL中申明 extern “C” __declspec(dllexport) int WINAPI mySum(char * astr,char *bstr) { //傳出char * 改變astr bstr -->abuf, bbuf可以被改變 return a+b; }  DLL 回呼函數 BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)

usingSystem; using System.Runtime.InteropServices; public delegate bool CallBack(int hwnd, int lParam); //定義委託函數類型 public class EnumReportApp {    [DllImport("user32")]    public static extern int EnumWindows(CallBack x, int y);    public static void Main() 
   {      CallBack myCallBack = new CallBack(EnumReportApp.Report);
     EnumWindows(myCallBack, 0);    }    public static bool Report(int hwnd, int lParam)    {       Console.Write("Window handle is ");       Console.WriteLine(hwnd); return true;    } }

DLL 傳遞結構 BOOL PtInRect(const RECT *lprc, POINT pt); using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] public struct Point

{ public int x; public int y; } [StructLayout(LayoutKind.Explicit)] public struct Rect { [FieldOffset(0)] public int left; [FieldOffset(4)] public int top; [FieldOffset(8)] public int right; [FieldOffset(12)] public int bottom; } Class XXXX

{ [DllImport("User32.dll")] public static extern bool PtInRect(ref Rect r, Point p); }

關於DllImportAttribute下四個欄位的詳細用法見:C#DllImportAttribute用法匯總

C#中DllImport用法匯總

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.