[API] 調用 Win32 API DLL (一)

  • 33359
  • 0
  • 2010-01-05

摘要:[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 類別名稱說明
 HANALEvoid*System.IntPtr

32 位元 Windows 作業系統上為 32 位元。

64 位元 Windows 作業系統上為 64位元。

BYTEunsigned charSystem.Byte8 位元。
SHORTshortSystem.Int1616 位元。
WORDunsigned shortSystem.UInt1616 位元。
INTintSystem.Int3232 位元。
UINTunsigned intSystem.UInt3232 位元。
LONGlongSystem.Int3232 位元。
BOOLlongSystem.Int3232 位元。
DWORDunsigned longSystem.UInt3232 位元。
ULONGunsigned longSystem.UInt3232 位元。
 CHARcharSystem.Char以 ANSI 修飾。
 LPSTRchar*

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。
ThrowOnUnmappableCharCLR 會將傳遞給 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

http://pinvoke.net/index.aspx

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)

原文出處:張宇超研究室

三小俠  小弟獻醜,歡迎指教