.NET Framework SDK 關於平台呼叫的文件相對分散,且多以 VB.NET 為主。本文整理 C# 呼叫 Windows API 的關鍵知識,並提供實用範例。安裝 Visual Studio 後,可在 C:\Program Files\Microsoft Visual Studio .Net\FrameworkSDK\Samples\Technologies\Interop\PlatformInvoke\WinAPIs\CS 找到大量範例。
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
public static extern 回傳型別 函數名稱(參數型別 參數名, ...);
System.Runtime.InteropServices 命名空間。| 屬性 | 說明 |
|---|---|
CallingConvention |
呼叫慣例。StdCall(預設,被呼叫者清理堆疊)適用大多數 Win32 API;Cdecl 用於可變參數函式(如 printf)。 |
CharSet |
控制字串封送與函數名稱修飾。Ansi(預設)對應「A」尾綴;Unicode 對應「W」;Auto 依平台決定(NT 為 Unicode,9x 為 Ansi)。 |
EntryPoint |
指定 DLL 實際匯出的函數名稱或序數。若 C# 方法名不同於 API 名,必須設定此屬性。 |
ExactSpelling |
true 時禁止依 CharSet 自動加尾綴;false(預設)允許自動修飾。 |
SetLastError |
true 時呼叫端會呼叫 SetLastError,C# 可透過 Marshal.GetLastWin32Error() 取得錯誤碼。 |
| 非受控型別 | C# 型別 | 備註 |
|---|---|---|
| 數值型別 (int, long, DWORD 等) | 對應的 int, long, uint 等 | 直接對應 |
| 字串指標 (LPSTR/LPWSTR) | string 或 StringBuilder | 輸出緩衝區必須用 StringBuilder |
| 控制代碼 (HANDLE, HWND) | IntPtr | 平台相關大小 |
| 結構 | struct 或 class + [StructLayout(LayoutKind.Sequential)] | 欄位順序必須一致 |
[StructLayout(LayoutKind.Sequential)]
public class OSVersionInfo
{
public int OSVersionInfoSize;
public int majorVersion;
public int minorVersion;
public int buildNumber;
public int platformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string versionString;
}
[DllImport("kernel32", EntryPoint = "GetVersionEx")]
public static extern bool GetVersionEx(ref OSVersionInfo osvi);
注意:結構作為參數時,通常需加 ref,否則會出現「物件參考未設定為物件實例」錯誤。
若受控物件在平台呼叫完成前被 GC 回收,代碼會失效。使用 HandleRef 包裝可延長存活期:
[DllImport("Kernel32.dll")]
public static extern bool ReadFile(
HandleRef hndRef,
StringBuilder buffer,
int numberOfBytesToRead,
out int numberOfBytesRead,
ref Overlapped flag
);
// 使用方式
FileStream fs = new FileStream("HandleRef.txt", FileMode.Open);
HandleRef hr = new HandleRef(fs, fs.Handle);
StringBuilder buffer = new StringBuilder(5);
int read = 0;
ReadFile(hr, buffer, 5, out read, 0); // 平台呼叫期間 hr 會保持參考
IntPtr 處理指標與控制代碼。StringBuilder 並預留足夠容量。[StructLayout(LayoutKind.Sequential)],必要時用 [MarshalAs] 精確控制欄位封送。IDisposable 並搭配 SafeHandle 衍生類別管理生命週期。