推薦從 C# 呼叫 Win32 API 的方法是 CsWin32,這是一個源碼產生器,能在編譯時產生型別安全的 P/Invoke 包裝器。 CsWin32 可支援任何 C# 專案類型——WinUI 3、WPF、WinForms、主控台或類別函式庫——並免除手寫DllImport或LibraryImport宣告的需求。
你在文字檔中列出你需要的 Win32 函式名稱,CsWin32 會自動從 Windows SDK 的元資料產生正確的簽名、結構式、常數和 COM 介面。
選擇互操作性方法
| Approach | 何時使用 | 優點 | Cons |
|---|---|---|---|
| CSWin32 (推薦) | 從 C# 呼叫任何 Win32/原生 API | 型別安全,由官方 Windows SDK 中繼資料產生,可處理編組與結構體,對 AOT 友善,並支援設定 | 需要 NuGet 套件;生成的程式碼預設不會被看見 |
| LibraryImport (.NET 7+) | 一次性通話,你知道確切的簽名 | 原始碼生成、AOT 相容、無執行時編組 | 你要手動撰寫並維護每個簽名 |
| DllImport (舊版) | 現有程式碼,或 .NET Framework 專案 | 作品遍布各地,社群範例豐富 | 執行時分組、易出錯簽名 |
| C#/WinRT | Windows 執行階段 API(Windows.*命名空間) |
投影的 .NET 類型,自然的 C# 體驗 | 僅適用於 WinRT API,不適用於原生 Win32 |
Note
CsWin32 的預設輸出使用 .NET 執行階段封送處理器,且 不會自動與 AOT 相容。 關於 NativeAOT 或修剪,請啟用 CsWin32RunAsBuildTask 和 DisableRuntimeMarshalling—— 請參見 CsWin32 AOT 指引。
Tip
如果你需要的 API 位於 Windows.* 命名空間中(例如 Windows.Storage 或 Windows.Media),那麼它就是 Windows 執行階段 API。 使用 WinRT 投影代替 P/Invoke。 請參考從 .NET 應用程式呼叫互操作 API。
先決條件
- Visual Studio 2022(版本 17.4 或更新)或 .NET 8+ SDK
- 一個現有的 C# 專案(WinUI 3、WPF、WinForms 或控制台)
Note
目標是 .NET 框架還是 .NET 標準? 在你的專案檔案中設定 <LangVersion>9</LangVersion> (或之後),並加入 System.Memory 和 System.Runtime.CompilerServices.Unsafe NuGet 套件。
步驟 1:安裝 CsWin32 NuGet 套件
在你的專案目錄中,執行:
dotnet add package Microsoft.Windows.CsWin32
CsWin32 產生的程式碼使用指標和不安全的上下文。 NuGet 套件會自動啟用 AllowUnsafeBlocks 。 如果你的專案明確設定了 <AllowUnsafeBlocks>false</AllowUnsafeBlocks>,請移除那行或改成 true,否則產生的程式碼無法編譯。
步驟 2:請求你需要的 API
在你的專案根目錄(檔案旁邊)建立一個名為 .csproj 的檔案。 每行新增一個 API 名稱。 在這個攻略中,請從一個簡單的函數開始:
GetTickCount
儲存檔案。 CsWin32 在編譯時讀取並產生匹配的 P/Invoke 包裝器。
步驟 3:呼叫產生的 API
產生的程式碼位於 Windows.Win32 命名空間中,並置於名為 PInvoke 的靜態類別底下。 像呼叫任何其他靜態方法一樣呼叫它:
using Windows.Win32;
// Get the number of milliseconds since the system started.
uint uptime = PInvoke.GetTickCount();
Console.WriteLine($"System uptime: {uptime} ms");
建立您的專案。 如果 NativeMethods.txt 中的函式名稱有效,呼叫即可編譯並執行,無需額外工作。
常見的陷阱
「我看不到生成的程式碼」
CsWin32 是一個原始碼產生器——它的輸出預設不會以檔案形式出現在你的專案中。 要檢查產生的程式碼:
- 在 Visual Studio 的方案總管中,展開 相依性 > 分析器 > Microsoft.Windows.CsWin32 > Microsoft.Windows.CsWin32.SourceGenerator。
- 或者,在專案檔案中設定
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>將產生的原始碼寫入資料夾。obj/
AnyCPU 平台目標
CsWin32 產生的程式碼可與 AnyCPU 相容。 大多數 Win32 通話不需要更改平台目標。
在 WinUI 3 中取得 HWND
許多 Win32 API 需要視窗句柄。 在 WinUI 3 應用程式中,從你的 Window 實例取得 HWND:
using WinRT.Interop;
var hWnd = WindowNative.GetWindowHandle(this);
接著將 hWnd(作為 HWND 或 nint)傳遞給 Win32 函式。 如需詳細資訊,請參閱 取得視窗控制代碼 (HWND)。
自訂 CsWin32 的行為
在文字檔旁建立一個 NativeMethods.json 檔案,以控制生成選項,例如寬弦與窄字串編組或友善過載:
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"emitSingleFile": false,
"public": true
}
請參閱 CsWin32 設定參考資料 以獲得所有選項。
下一步
- 選擇你的互通方法——所有 Windows 互通技術的決策指南
- 攻略:WinUI 3 應用程式與 Win32 互通 ——一個更深入的範例,使用 CsWin32 自訂標題列
- GitHub 上的 CsWin32 — 原始碼、範例與問題追蹤器
- Platform Invoke (P/Invoke) — 關於 P/Invoke 基礎的 .NET 文件
- 從 .NET 應用程式呼叫互操作 API — 用於基於 WinRT COM 的互通情境(HWND 傳遞、選擇器等)