摘要:[C#] 調用 Win32 API DLL
一般來說我們是透過 Platform Invoke (P/Invoke) 呼叫標準 Win32 API ,呼叫外部 Win32 api ,有三個重要步驟:
一、加入 System.Runtime.InteropServices 命名空間。
二、宣告對應外部 Win32 API 的方法。一定要使用 static 與 extern 修飾子,告訴編譯器該方法與實體物件無關且並非存在
於內部,同時必須將原函式參數轉換成對應於 C# 當中的型態。
三、於宣告的對應方法加上 [Dlllmport("目標函式所屬的 DLL 動態連結函式庫")] 的屬性,以下面範例來說就是 kernel32.dll。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Win32 API Beep 函式原型宣告:
BOOL Beep( DWORD dwFreq, DWORD dwDuration ); |
Win32 API Beep 函式回傳值說明:
若函式執行正常,則回傳非 0 值,若執行期發生錯誤,則回傳 0 ,可透過呼叫 GetLastError 取得進一步的錯誤資訊。
Win32 API Beep 所屬的 DLL 動態連結函式庫:
using System; using System.Threading; using System.Runtime.InteropServices; namespace ExampleSolution {
class Example221 { static void Main(string[] args) { for (int i = 1000; i < 1300; i += 10 ) {
// Console.Beep( i, 100); Beep( i, 300 ); Thread.Sleep(100); } }
[DllImport("kernel32.dll")] public static extern bool Beep(int frequency, int duration); } } |
執行結果:
程式執行後便會透過 PC 喇叭播放單音,並且逐漸上高音頻。
上面範例程式碼中 [DllImport("kernel32.dll")] 屬性的 kernel32.dll 修改成任意檔名,此時程式編譯不會出現錯誤,但是再執行
期會發生 CLR 找不到 DLL 的例外。因此可知,CLR 是在程式執行期才動態連結該 Win32 API 。
表: P/Invoke 資料型態轉換對應表
Wtypes.h 中的 Unmanaged 型別 | Unmanaged C 語言型別 | Managed 類別名稱 | 說明 |
HANALE | void* | System.IntPtr | 32 位元 Windows 作業系統上為 32 位元。 64 位元 Windows 作業系統上為 64位元。 |
BYTE | unsigned char | System.Byte | 8 位元。 |
SHORT | short | System.Int16 | 16 位元。 |
WORD | unsigned short | System.UInt16 | 16 位元。 |
INT | int | System.Int32 | 32 位元。 |
UINT | unsigned int | System.UInt32 | 32 位元。 |
LONG | long | System.Int32 | 32 位元。 |
BOOL | long | System.Int32 | 32 位元。 |
DWORD | unsigned long | System.UInt32 | 32 位元。 |
ULONG | unsigned long | System.UInt32 | 32 位元。 |
CHAR | char | System.Char | 以 ANSI 修飾。 |
LPSTR | char* | System.String 或 System.Text.StringBuilder | 以 ANSI 修飾。 |
LPCSTR | Const char* | System.String 或 System.Text.StringBuilder | 以 ANSI 修飾。 |
LPWSTR | wchar_t* | System.String 或 System.Text.StringBuilder | 以 ANSI 修飾。 |
LPCWSTR | Const wchar_t* | System.String 或 System.Text.StringBuilder | 以 ANSI 修飾。 |
FLOAT | Float | System.Single | 32 位元。 |
DOUBLE | Double | System.Double | 64 位元。 |
資源:http://www.chenjiliang.com/Article/View.aspx?ArticleID=5209&TypeID=34
DllImport 屬性格式
DllImport( DLL 名稱 [, BestFitMapping] [,CallingConvention ] [, CarSet] [, EntryPoint] [,ExactSpelling] [, PreserveSig] [, SetLastError] [, ThrowOnUnmappableChar])
DllImport 屬性提供呼叫 Unmanaged DLL 中匯出函式所需的資訊。就最低需求而言,必須包含進入點的 DLL名稱。
以下列出說明。
表: DllImport 屬性欄位說明
DLL 名稱 | 說明 |
BestFitMapping | 將 Unicode 字元轉換成 ANSI 字元時,啟用或停用自動對應行為。如果為 true ,則啟用自動對應行為;否則停用自動對應。依預設 BestFitMapping 欄位為 true。 |
CallingConvention | CallingConvention 列舉成員,此欄位的預設值為 WinAPI,也是 StdCall 慣例的預設值。 |
CharSet | 使用這個欄位配合 CharSet 列舉的成員已指定字串參數的封送處理行為,並指定要叫用的進入點名稱(Win32 API 當中有許多函式具有單字元集和寬字元集兩個版本,透過此欄位的設定,可以明確控制 P/invoke 轉呼叫的目標函式為其中何者)。C# 的預設列舉成員為 CharSet.Ansi。 |
EntryPoint | 指示要呼叫的 DLL 進入點(Entry Point) 的名稱或序數。 序數會使用 # 符號當做前置字元,例如 #1 。如果省略這個欄位,CLR 會直接使用 DllImport 屬性標記對象的 C# 方法名稱。 |
ExactSpelling | 控制 DllImport 屬性的 CharSet 欄位是否會導致 CLR 搜尋 Unmanaged DLL 以取得不是指定名稱的進入點名稱。如果是 false ,DllImport 屬性的 CharSet 欄位設定為 CharSet.Ansi 時會叫用附加字母 A 的進入點名稱,DllImport 屬性的 CharSet 欄位設定為 CharSet.Unicode 時會叫用附加字母 W 的進入點名稱,通常 Managed 編譯器會設定這個欄位。 |
PreserveSig | 指定簽章是否為 Unmanaged 程式碼進入點的直接轉譯。預設此欄位為 true。 |
SetLasrError | 指示自屬性方法傳回之前,被呼叫端是否呼叫 SetLastError Win32 API 函式。表示會呼叫 SetLastError 時,為 true ,否則為 false,預設為 false。 |
ThrowOnUnmappableChar | CLR 會將傳遞給 Unmanaged 方法 (在 Win98 或 Win Me 上執行) 的所有 Managed Unicode 字元,轉換成 ANSI 字元。在無法對應的 Unicode 字元轉換為 ANSI "?" 字元時,啟用或停用例外狀況的擲回。此欄位預設為 false。 |
以下程式碼與之前的比較做了一些更動,使用了 EntryPoint 這個 DllImport 屬性欄位,如此便可以在 Managed 程式碼中自訂 P/Invoke 方法名稱,而無須受限於該外部函式的原始名稱。
using System; using System.Threading; using System.Runtime.InteropServices; namespace ExampleSolution {
class Example221 { static void Main(string[] args) { for (int i = 1000; i < 1300; i += 10 ) {
// Console.Beep( i, 100); Beep( i, 300 ); Thread.Sleep(100); } } [DllImport("kernel32.dll"),EntryPoint="Beep"]//進入點 Beep 。 public static extern bool Singing(int frequency, int duration);//因為設定了進入點,所以可以自訂函式名稱。 } } |
API查詢:
1.Windows API Reference for C#, VB.NET and VB6
http://www.webtropy.com/articles/Win32-API-DllImport-art9.asp
2.PInvoke.NET
3.Microsoft Win32 to Microsoft .NET Framework API Map
http://msdn.microsoft.com/en-us/library/aa302340.aspx
補充
http://msdn.microsoft.com/en-us/library/dd469351%28VS.85%29.aspx
(PInvoke(1))(C# Windows API)
原文出處:張宇超研究室
三小俠 小弟獻醜,歡迎指教