C# から Win32 API を呼び出す場合に推奨される方法は、コンパイル時にタイプ セーフな P/Invoke ラッパーを生成するソース ジェネレーター である CsWin32 です。 CsWin32 は、任意の C# プロジェクトの種類 (WinUI 3、WPF、WinForms、コンソール、またはクラス ライブラリ) で動作し、DllImportまたはLibraryImport宣言を手動で記述する必要がなくなります。
必要な Win32 関数名をテキスト ファイルに一覧表示すると、CsWin32 は SDK メタデータから正しいシグネチャ、構造体、定数、および COM インターフェイスWindows自動的に生成します。
相互運用アプローチを選択する
| Approach | いつ使用するか | 利点 | デメリット |
|---|---|---|---|
| CsWin32 (推奨) | C# からの任意の Win32/ネイティブ API 呼び出し | 型安全で、公式の Windows SDK メタデータから生成され、マーシャリングや構造体を処理し、設定によりAOT に対応 | NuGet パッケージが必要です。生成されたコードは既定では表示されません |
| LibraryImport (.NET 7 以降) | 正確なシグネチャがわかっている一度限りの呼び出し | ソース生成、AOT 互換、ランタイム マーシャリングなし | すべての署名を手動で記述して維持する |
| DllImport (レガシ) | 既存のコードまたは .NET Framework プロジェクト | どこでも動作し、コミュニティによる豊富な作例 | ランタイム マーシャリング、エラーを招きやすいシグネチャ |
| C#/WinRT | Windows ランタイム API (Windows.*名前空間) |
射影された .NET 型、自然な C# の使用感 | 未加工の Win32 ではなく、WinRT API の場合のみ |
Note
CsWin32 の既定の出力では、.NET ランタイム マーシャラーが使用され、自動的に AOT 互換ではありません。 NativeAOT またはトリミングについては、 CsWin32RunAsBuildTask と DisableRuntimeMarshallingを有効にします。 CsWin32 AOT ガイダンスを参照してください。
Tip
必要な API がWindows.*名前空間 (Windows.StorageやWindows.Mediaなど) にある場合は、Windows ランタイム API です。 P/Invoke の代わりに WinRT プロジェクションを使用します。
「.NET アプリからの相互運用 API の呼び出し」を参照してください。
前提条件
- Visual Studio 2022 (バージョン 17.4 以降) または .NET 8 以降の SDK
- 既存の C# プロジェクト (WinUI 3、WPF、WinForms、またはコンソール)
Note
.NET Framework または .NET Standard をターゲットにしますか? プロジェクト ファイル <LangVersion>9</LangVersion> (またはそれ以降) を設定し、 System.Memory と nuGet パッケージ System.Runtime.CompilerServices.Unsafe 追加します。
手順 1: CsWin32 NuGet パッケージをインストールする
プロジェクト ディレクトリで、次のコマンドを実行します。
dotnet add package Microsoft.Windows.CsWin32
CsWin32 は、ポインターと安全でないコンテキストを使用するコードを生成します。 NuGet パッケージを使用すると、 AllowUnsafeBlocks が自動的に有効になります。 プロジェクトで <AllowUnsafeBlocks>false</AllowUnsafeBlocks>を明示的に設定している場合は、その行を削除するか、 trueに変更します。そうしないと、生成されたコードはコンパイルされません。
手順 2: 必要な API を要求する
プロジェクト ルート ( ファイルの横) に .csproj という名前のファイルを作成します。 1 行に 1 つの 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 で、ソリューション エクスプローラーの Dependencies > Analyzers > 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相互運用手法の意思決定ガイド
- チュートリアル: Win32 相互運用機能を備えた WinUI 3 アプリ - CsWin32 を使用してタイトル バーをカスタマイズするより深い例
- GitHub上の CsWin32 - ソース、サンプル、問題トラッカー
- プラットフォーム呼び出し (P/Invoke) — P/Invoke の基礎に関する.NETドキュメント
- .NET アプリから相互運用 API を呼び出す — WinRT COM ベースの相互運用シナリオ (HWND パッシング、ピッカーなど)
Windows developer