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

C# 使用 Win32 類別庫入門(一):P/Invoke 基礎、Beep/MessageBeep、結構封送、列舉與常數

     編輯:關於C語言

前言

.NET Framework 未包裝所有 Win32 API。平台調用 讓 C# 直接呼叫非受控 DLL 函數。本文為入門系列,涵蓋:P/Invoke 基礎流程、Beep/MessageBeep 簡單範例、列舉與常數對應、結構封送(GetSystemPowerStatus)。

一、P/Invoke 核心兩步驟

  1. 定義簽章:用 static extern 宣告方法,型別對應 Win32 → C#
  2. 標註 DLL:[DllImport("dll名稱")] 告知執行期函數位置

二、型別對應速查表

Win32C#備註
DWORD, ULONG, UINTuint / intint 為 CLS 相容,常用
BOOLbool
HANDLE, HWND, HINSTANCEIntPtr平台相關指標大小
LPCTSTR, LPTSTRstring / StringBuilder配合 CharSet、MarshalAs
BYTEbyte
LONGint
WORDushort

三、範例 1:Beep — 最簡單的 P/Invoke

Win32 原型

BOOL Beep(DWORD dwFreq, DWORD dwDuration); // kernel32.dll

C# 定義與呼叫

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 檔名(不含路徑),系統搜尋順序:應用目錄 → 系統目錄 → PATH
  • P/Invoke 需完全信任權限(桌面應用預設滿足)

四、範例 2:MessageBeep — 列舉與常數對應

Win32 原型

BOOL MessageBeep(UINT uType); // user32.dll
// uType 預定義常數:MB_OK=0x0000, MB_ICONHAND=0x10, MB_ICONQUESTION=0x20, MB_ICONEXCLAMATION=0x30, MB_ICONASTERISK=0x40, -1=簡單嗶聲

C# 列舉定義

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);

查找常數值技巧

  1. 安裝 Windows SDK / Visual Studio C++ 工作負載
  2. 標頭檔位置:C:\Program Files (x86)\Windows Kits\10\Include\\um\winuser.h
  3. 搜尋:findstr "MB_ICONHAND" *.h

五、範例 3:GetSystemPowerStatus — 結構封送

Win32 結構

typedef 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;

C# 結構定義

[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
}

P/Invoke 定義

[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用 intC# long=8 bytes,Win32 LONG=4 bytes
字串參數不加 MarshalAs加 [MarshalAs(UnmanagedType.LPTStr)]確保正確編碼與緩衝區語意
結構用 class用 struct + StructLayoutclass 為參考型別,記憶體配置不同
指標參數用 int用 IntPtr64 位元相容性
忘記 SetLastError = true加上並用 Marshal.GetLastWin32Error()取得 Win32 錯誤碼

七、總結

P/Invoke 入門三步曲:

  1. 查閱 MSDN 確認 DLL、函數原型、常數值
  2. 建立 C# 簽章:型別對應、DllImport、StructLayout
  3. 測試驗證,善用 Marshal.GetLastWin32Error() 除錯

掌握 Beep(簡單函數)、MessageBeep(列舉常數)、GetSystemPowerStatus(結構封送)三大基礎模式,即可應對 80% 以上 P/Invoke 場景。

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