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

C# 呼叫 Windows API 完整入門:從 MessageBox 到結構體封送

     編輯:關於C語言

前言

C# 開發者遲早會面臨呼叫 Windows 原生 API (P/Invoke) 的需求。本文由淺入深,先示範最簡單的 MessageBox 呼叫,再進階講解結構體 (struct) 封送與 GetSystemInfo/GlobalMemoryStatus 實戰,幫助你掌握核心概念:DllImport、StructLayout、MarshalAs、ref/out 參數傳遞。

基礎篇:呼叫簡單 API - MessageBox

步驟 1:引入命名空間

using System.Runtime.InteropServices;

步驟 2:宣告外部方法

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
  • DllImport:指定 DLL 名稱,CharSet.Auto 自動依專案 Unicode/ANSI 切換字串封送。
  • static extern:標記為外部靜態方法,無方法主體。
  • 參數型別對應:HWND→IntPtr、LPCTSTR→string、UINT→uint。

步驟 3:呼叫

private void btnCallApi_Click(object sender, EventArgs e)
{
    MessageBox(IntPtr.Zero, "來自 User32.dll 的問候", "API Demo", 0);
}

進階篇:結構體封送 - GetSystemInfo / GlobalMemoryStatus

定義結構體對應

[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_INFO
{
    public uint dwOemId;
    public uint dwPageSize;
    public uint lpMinimumApplicationAddress;
    public uint lpMaximumApplicationAddress;
    public uint dwActiveProcessorMask;
    public uint dwNumberOfProcessors;
    public uint dwProcessorType;
    public uint dwAllocationGranularity;
    public uint dwProcessorLevel;
    public uint dwProcessorRevision;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct MEMORYSTATUS
{
    public uint dwLength;
    public uint dwMemoryLoad;
    public uint dwTotalPhys;
    public uint dwAvailPhys;
    public uint dwTotalPageFile;
    public uint dwAvailPageFile;
    public uint dwTotalVirtual;
    public uint dwAvailVirtual;
}
  • LayoutKind.Sequential:依欄位順序記憶體配置,符合 C 語言結構體預設佈局。
  • CharSet.Auto:確保字串欄位正確封送(此範例無字串,但屬好習慣)。
  • 所有欄位使用 uint 對應 DWORD/ULONG,避免正負號誤解。

宣告 API

[DllImport("kernel32.dll")]
static extern void GetSystemInfo(ref SYSTEM_INFO lpSystemInfo);

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern void GlobalMemoryStatus(ref MEMORYSTATUS lpBuffer);
  • ref 關鍵字:傳遞結構體參考,API 填寫資料後呼叫端可取得結果。
  • kernel32.dll 為核心系統 DLL,常駐記憶體,載入開銷極低。

完整呼叫範例

private void btnGetInfo_Click(object sender, EventArgs e)
{
    try
    {
        SYSTEM_INFO sysInfo = new SYSTEM_INFO();
        GetSystemInfo(ref sysInfo);

        MEMORYSTATUS memStatus = new MEMORYSTATUS { dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUS)) };
        GlobalMemoryStatus(ref memStatus);

        // 顯示處理器類型
        string cpuType = sysInfo.dwProcessorType switch
        {
            386 => "Intel 386",
            486 => "Intel 486",
            586 => "Intel Pentium",
            4000 => "MIPS R4000",
            21064 => "DEC Alpha 21064",
            _ => "(unknown)"
        };

        listBox.Items.Add($"CPU 類型: {cpuType}");
        listBox.Items.Add($"處理器數量: {sysInfo.dwNumberOfProcessors}");
        listBox.Items.Add($"頁面大小: {sysInfo.dwPageSize} bytes");
        listBox.Items.Add($"記憶體使用率: {memStatus.dwMemoryLoad}%");
        listBox.Items.Add($"可用實體記憶體: {memStatus.dwAvailPhys / 1024 / 1024} MB");
        listBox.Items.Add($"總實體記憶體: {memStatus.dwTotalPhys / 1024 / 1024} MB");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"API 呼叫失敗: {ex.Message}");
    }
}

關鍵知識點總結

概念說明
DllImport指定 DLL 路徑、進入點、呼叫慣例、字元集等
StructLayout控制結構體記憶體佈局,Sequential 最常用
MarshalAs單一欄位自訂封送,如 UnmanagedType.LPStr 強制 ANSI 字串
ref/out對應 C/C++ 指標參數,ref 需初始化,out 僅輸出
IntPtr平台無關指標/控制代碼,32/64 位元自動適配
SetLastError=true搭配 Marshal.GetLastWin32Error() 取得錯誤碼

常見陷阱與最佳實務

  • 結構體大小不符:MEMORYSTATUS.dwLength 必須先設定為 Marshal.SizeOf 結果,否則 API 可能拒絕填寫。
  • 字元集不匹配:專案為 Unicode 時,預設封送為 LPWSTR;若 DLL 僅提供 ANSI 版本,需加 CharSet=CharSet.Ansi 或欄位加 [MarshalAs(UnmanagedType.LPStr)]。
  • 64 位元相容:所有指標型參數務必用 IntPtr,切勿用 int/long。
  • 記憶體洩漏:API 配置記憶體需呼叫對應釋放函式 (如 LocalFree、CoTaskMemFree),C# 端可用 Marshal.FreeHGlobal。

延伸閱讀

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