首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 呼叫 Win32 API 完整指南:P/Invoke 實戰技巧

     編輯:關於C#

為何需要 P/Invoke?

.NET Framework 雖提供大量托管類別庫,但 Win32 API 龐大且涵蓋底層系統功能,框架團隊無法為所有 API 提供托管包裝。當需要使用未封裝的 Windows 原生功能(如特定裝置控制、系統級設定)時,平台呼叫 P/Invoke 是最常用的橋接方案。

核心步驟與屬性

  1. 定義 extern 方法原型:使用 static extern 宣告,參數型別需對應 Win32 型別(如 DWORD → int/uint,BOOL → bool)。
  2. 標註 DllImport 屬性:指定包含目標函式的 DLL 檔名(如 kernel32.dll、user32.dll)。
  3. 處理型別對應:結構、字串、回呼函式需特殊封送處理。

實戰範例

1. 簡單函式:Beep

[DllImport("kernel32.dll")]
public static extern bool Beep(int frequency, int duration);

// 呼叫
Beep(1000, 500);

2. 列舉與常數:MessageBeep

將 Win32 常數定義為 enum,提升可讀性與型別安全:

public enum BeepType : uint
{
    SimpleBeep = 0xFFFFFFFF,
    IconAsterisk = 0x00000040,
    IconExclamation = 0x00000030,
    IconHand = 0x00000010,
    IconQuestion = 0x00000020,
    Ok = 0x00000000,
}

[DllImport("user32.dll")]
public static extern bool MessageBeep(BeepType beepType);

3. 結構封送:GetSystemPowerStatus

對應 SYSTEM_POWER_STATUS 結構,使用 ref 傳遞指標,並將位元欄位定義為 enum:

[StructLayout(LayoutKind.Sequential)]
struct SystemPowerStatus
{
    public ACLineStatus ACLineStatus;
    public BatteryFlag BatteryFlag;
    public byte BatteryLifePercent;
    public byte Reserved1;
    public int BatteryLifeTime;
    public int BatteryFullLifeTime;
}

enum ACLineStatus : byte { Offline = 0, Online = 1, Unknown = 255 }
enum BatteryFlag : byte { High = 1, Low = 2, Critical = 4, Charging = 8, NoSystemBattery = 128, Unknown = 255 }

[DllImport("kernel32.dll")]
public static extern bool GetSystemPowerStatus(ref SystemPowerStatus status);

4. 字串處理:CharSet 與 StringBuilder

  • CharSet.Auto:自動依平台選擇 ANSI (A) 或 Unicode (W) 版本 API。
  • StringBuilder:用於接收 API 填充的字串緩衝區,避免 .NET 字串不可變導致的記憶體損壞。
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetShortPathName(
    [MarshalAs(UnmanagedType.LPTStr)] string path,
    [MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath,
    int shortPathLength);

5. 內嵌字元陣列結構:GetTimeZoneInformation

使用 StructLayout 指定 CharSet.Unicode,並以 MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32) 對應固定長度字元陣列。

6. 回呼函式:EnumDesktops

以 delegate 替代函式指標,並注意委派生命週期:若非托管碼長期持有指標,必須在托管端保持委派參考防止 GC 回收。

進階屬性與疑難排解

屬性用途
CallingConvention指定呼叫慣例(預設 StdCall,C 函式需設 Cdecl)
ExactSpelling設 true 關閉 A/W 自動查找
SetLastError啟用 GetLastError 取得錯誤碼
StructLayout.Pack控制結構體對齊封裝大小

常見錯誤:long 大小不一(C++ 4 bytes vs C# 8 bytes)、字串封送型別錯誤、結構體大小不符。建議使用 Marshal.SizeOf() 驗證結構大小。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved