.NET Framework 未包裝所有 Win32 API。平台調用 讓 C# 直接呼叫非受控 DLL 函數。本文為入門系列,涵蓋:P/Invoke 基礎流程、Beep/MessageBeep 簡單範例、列舉與常數對應、結構封送(GetSystemPowerStatus)。
static extern 宣告方法,型別對應 Win32 → C#[DllImport("dll名稱")] 告知執行期函數位置| Win32 | C# | 備註 |
|---|---|---|
DWORD, ULONG, UINT | uint / int | int 為 CLS 相容,常用 |
BOOL | bool | |
HANDLE, HWND, HINSTANCE | IntPtr | 平台相關指標大小 |
LPCTSTR, LPTSTR | string / StringBuilder | 配合 CharSet、MarshalAs |
BYTE | byte | |
LONG | int | |
WORD | ushort |
BOOL Beep(DWORD dwFreq, DWORD dwDuration); // kernel32.dll
using System;
using System.Runtime.InteropServices;
class BeepDemo
{
[DllImport("kernel32.dll")]
public static extern bool Beep(int frequency, int duration);
static void Main()
{
Random r = new Random();
for (int i = 0; i < 10; i++)
Beep(r.Next(500, 2000), 200); // 隨機頻率 500-2000Hz,200ms
}
}
DllImport 參數為 DLL 檔名(不含路徑),系統搜尋順序:應用目錄 → 系統目錄 → PATHBOOL MessageBeep(UINT uType); // user32.dll
// uType 預定義常數:MB_OK=0x0000, MB_ICONHAND=0x10, MB_ICONQUESTION=0x20, MB_ICONEXCLAMATION=0x30, MB_ICONASTERISK=0x40, -1=簡單嗶聲
public enum BeepType : uint
{
SimpleBeep = 0xFFFFFFFF, // -1 (uint.MaxValue)
Ok = 0x00000000,
IconHand = 0x00000010,
IconQuestion = 0x00000020,
IconExclamation = 0x00000030,
IconAsterisk = 0x00000040,
}
[DllImport("user32.dll")]
public static extern bool MessageBeep(BeepType beepType);
C:\Program Files (x86)\Windows Kits\10\Include\\um\winuser.h findstr "MB_ICONHAND" *.htypedef struct _SYSTEM_POWER_STATUS {
BYTE ACLineStatus; // 0=離線, 1=線上, 255=未知
BYTE BatteryFlag; // 1=高, 2=低, 4=危險, 8=充電中, 128=無電池, 255=未知
BYTE BatteryLifePercent; // 0-100, 255=未知
BYTE Reserved1;
DWORD BatteryLifeTime; // 剩餘秒數, -1=未知
DWORD BatteryFullLifeTime;// 滿電秒數, -1=未知
} SYSTEM_POWER_STATUS;
[StructLayout(LayoutKind.Sequential)]
struct SystemPowerStatus
{
public byte ACLineStatus;
public byte BatteryFlag;
public byte BatteryLifePercent;
public byte Reserved1;
public int BatteryLifeTime; // DWORD → int
public int BatteryFullLifeTime; // DWORD → int
}
[DllImport("kernel32.dll")]
public static extern bool GetSystemPowerStatus(ref SystemPowerStatus status);
var status = new SystemPowerStatus();
if (GetSystemPowerStatus(ref status))
{
Console.WriteLine($"AC: {status.ACLineStatus}, Battery%: {status.BatteryLifePercent}");
}
// 建議:將 BYTE 欄位對應為列舉
enum ACLineStatus : byte { Offline = 0, Online = 1, Unknown = 255 }
enum BatteryFlag : byte { High = 1, Low = 2, Critical = 4, Charging = 8, NoSystemBattery = 128, Unknown = 255 }
// 使用
Console.WriteLine($"AC: {(ACLineStatus)status.ACLineStatus}");
[StructLayout(LayoutKind.Sequential)]:依宣告順序配置記憶體,符合 C 結構預設ref 關鍵字:傳遞結構指標而非值(避免複製大型結構)Marshal.SizeOf<T>() 驗證大小| 錯誤 | 正確 | 原因 |
|---|---|---|
C# long 對應 Win32 LONG | 用 int | C# long=8 bytes,Win32 LONG=4 bytes |
字串參數不加 MarshalAs | 加 [MarshalAs(UnmanagedType.LPTStr)] | 確保正確編碼與緩衝區語意 |
結構用 class | 用 struct + StructLayout | class 為參考型別,記憶體配置不同 |
指標參數用 int | 用 IntPtr | 64 位元相容性 |
忘記 SetLastError = true | 加上並用 Marshal.GetLastWin32Error() | 取得 Win32 錯誤碼 |
P/Invoke 入門三步曲:
DllImport、StructLayoutMarshal.GetLastWin32Error() 除錯掌握 Beep(簡單函數)、MessageBeep(列舉常數)、GetSystemPowerStatus(結構封送)三大基礎模式,即可應對 80% 以上 P/Invoke 場景。