1. 程式人生 > >如何使用C#呼叫非託管DLL函式

如何使用C#呼叫非託管DLL函式

如何使用C#呼叫非託管DLL函式


由於工作需要,學習了GDI+程式設計的一些知識。其中看到了一個比較好的Demo,深入的瞭解後,卻發現自己對如何用C#呼叫非託管DLL函式也有了更好的理解,於是整理了一下,跟大家一起分享。


引用: 用C#來捕獲螢幕的源程式程式碼(Capture.cs)

C#捕獲當前螢幕的例子

上面的例子中,應用C#呼叫非託管DLL的函式如下:

在上面這段程式碼中,我將以分別介紹 DllImportAttribute屬性、extern關鍵字 、IntPtr型別 這三個方面,向大家介紹如何應用C#呼叫非託管DLL函式。


C# 如何使用 DllImport Attribute(屬性) 標識 DLL 和函式
 

// 宣告一個API函式 
 [ System.Runtime.InteropServices.DllImportAttribute (  " gdi32.dll "  ) ] 
 
private   static   extern   bool  BitBlt ( 
  IntPtr hdcDest , 
//  目標 DC的控制代碼 
   int  nXDest , 
  
int  nYDest , 
  
int  nWidth , 
  
int  nHeight , 
  IntPtr hdcSrc , 
//  源DC的控制代碼 
   int  nXSrc , 
  
int  nYSrc , 
  System.Int32 dwRop 
//  光柵的處理數值 
  ) ;

 

System.Runtime.InteropServices.DllImportAttribute

從託管程式碼中訪問非託管 DLL 函式之前,需要知道該函式的名稱以及該 DLL 的名稱,然後為 DLL 的非託管函式 編寫 託管定義。

它將用到 static 和 extern 修飾符,此型別的公共靜態成員對於多執行緒操作是安全的。

DllImport 屬性提供非託管 DLL 函式的呼叫資訊。


示例1  簡單DllImportAttribute 應用

using  System.Runtime.InteropServices;
[DllImport(
" user32.dll " )]     
public   static   extern   int  MessageBox( int  hWnd, String text, String caption,  uint  type);

示例2  如何將 DllImportAttribute 應用於方法。

using  System.Runtime.InteropServices;
[DllImport(  
" KERNEL32.DLL "
             EntryPoint
= " MoveFileW "
             SetLastError
= true
             CharSet
= CharSet.Unicode, 
             ExactSpelling
= true
             CallingConvention
= CallingConvention.StdCall
          )
]
public   static   extern   bool  MoveFile(String src, String dst);

引數說明:

EntryPoint         指定要呼叫的 DLL 入口點。

SetLastError       判斷在執行該方法時是否出錯(使用 Marshal.GetLastWin32Error API 函式來確定)。
                   C#中預設值為 false。

CharSet            控制名稱及函式中字串引數的編碼方式。預設值為 CharSet.Ansi。

ExactSpelling      是否修改入口點以對應不同的字元編碼方式。

CallingConvention  指定用於傳遞方法引數的呼叫約定。預設值為 WinAPI。
                   該值對應於基於32位Intel平臺的 __stdcall。

BestFitMapping     是否啟用最佳對映功能,預設為 true。
                   最佳對映功能提供在沒有匹配項時,自動提供匹配的字元。
                   無法對映的字元通常轉換為預設的“?”。

PreserveSig        託管方法簽名是否轉換成返回 HRESULT,預設值為 true(不應轉換籤名)。
                   並且返回值有一個附加的 [out, retval] 引數的非託管簽名。 
                    
ThrowOnUnmappableChar     控制對轉換為 ANSI '?' 字元的不可對映的 Unicode 字元引發異常。


C# 關鍵字 extern 的使用

public static extern int MyMethod(int x);

外部修飾符 extern 用於指示外部實現方法,常與 DllImport 屬性一起使用(DllImport 屬性提供非託管 DLL 函式的呼叫資訊)。

若將 abstract 和 extern 修飾符一起使用來修改同一成員是錯誤的。extern 將方法在 C# 程式碼的外部實現,而 abstract 意味著在此類中未提供此方法的實現。

因為外部方法宣告不提供具體實現,所以沒有方法體;
此方法宣告只是以一個分號結束,並且在簽名後沒有大括號{ }。

示例3  接收使用者輸入的字串並顯示在訊息框中

程式從 User32.dll 庫匯入的 MessageBox 方法。

using  System;
using  System.Runtime.InteropServices;
class  MyClass 
{
   [DllImport(
" User32.dll " )]
   
public   static   extern   int  MessageBox( int  h,  string  m,  string  c,  int  type);

   
public   static   int  Main() 
   {
      
string  myString; 
      Console.Write(
" Enter your message:  " );
      myString 
=  Console.ReadLine();
      
return  MessageBox( 0 , myString,  " My Message Box " 0 );
   }
}

執行結果: 輸入"Hello"文字後,螢幕上將彈出一個包含該文字的訊息框。
Enter your message: Hello

示例4  呼叫DLL進行計算 

該示例使用兩個檔案 CM.cs 和 Cmdll.c 來說明 extern。
C 檔案是從 C# 程式中呼叫的外部 DLL。

使用 Visual C++ 命令列將 Cmdll.c 編譯為 DLL:
cl /LD /MD Cmdll.c

檔案:Cmdll.c

//  cmdll.c
//  compile with: /LD /MD
int  __declspec(dllexport) MyMethod( int  i)
{
   
return  i * 10 ;
}


使用命令列編譯 CM.cs:
csc CM.cs
這將建立可執行檔案 CM.exe。


檔案:CM.cs

//  cm.cs
using  System;
using  System.Runtime.InteropServices;
public   class  MyClass 
{
   [DllImport(
" Cmdll.dll " )]
   
public   static   extern   int  MyMethod( int  x);

   
public   static   void  Main() 
   {
      Console.WriteLine(
" MyMethod() returns {0}. " , MyMethod( 5 ));
   }
}

執行此程式,MyMethod 將值 5 傳遞到 DLL 檔案,該檔案將此值乘以 10 返回。
執行結果:MyMethod() returns 50.


IntPtr 型別的說明

對於平臺呼叫,應讓引數為 IntPtr 型別,而不是 String 型別。使用 System.Runtime.InteropServices.Marshal 類所提供的方法,可將型別手動轉換為字串並手動將其釋放。

IntPtr 型別被設計成整數,其大小適用於特定平臺。
                  在 32 位硬體和作業系統中將是 32 位;
                  在 64 位硬體和作業系統中將是 64 位。

IntPtr 型別由支援指標的語言使用,並作為在支援與不支援指標的語言間引用資料的一種通用方式。它也可用於保持控制代碼。例如,IntPtr 的例項廣泛地用在 System.IO.FileStream 類中來保持檔案控制代碼。

IntPtr 型別符合 CLS,而 UIntPtr 型別卻不符合。只有 IntPtr 型別可用在公共語言執行庫中。此型別實現 ISerializable 介面。