プラットフォーム呼び出し用のソース生成
.NET 7 では、C# コードの LibraryImportAttribute を認識する P/Invokes のソース ジェネレーターが導入されています。
ソース生成を使用しない場合、.NET ランタイムの組み込み相互運用システムは、実行時に IL スタブ (JIT 化された IL 命令のストリーム) を生成し、マネージドからアンマネージドへの移行を容易にします。 次のコードは、このメカニズムを使用する P/Invoke の定義と呼び出しを示しています。
[DllImport(
"nativelib",
EntryPoint = "to_lower",
CharSet = CharSet.Unicode)]
internal static extern string ToLower(string str);
// string lower = ToLower("StringToConvert");
IL スタブは、アンマネージド コードの呼び出し方法に影響を与える DllImportAttribute での設定 (SetLastError など) を考慮して、パラメーターと戻り値のマーシャリングとアンマネージ コードの呼び出しを処理します。 この IL スタブは実行時に生成されるため、Ahead-Of-Time (AOT) コンパイラまたは IL トリミングのシナリオでは使用できません。 IL の生成は、マーシャリングに関して考慮すべき重要なコストです。 このコストは、アプリケーションのパフォーマンスと、動的コード生成を許可しない可能性のある潜在的なターゲット プラットフォームのサポートの観点から測定できます。 ネイティブ AOT アプリケーション モデルは、すべてのコードを事前に直接ネイティブ コードにプリコンパイルすることで、動的コード生成の問題に対処します。 完全なネイティブ AOT シナリオを必要とするプラットフォームでは、DllImport
の使用はオプションではないため、他のアプローチ (ソース生成など) を使用する方が適切です。 DllImport
シナリオでのマーシャリング ロジックのデバッグも簡単な作業ではありません。
.NET 7 SDK に含まれ、既定で有効になる P/Invoke ソース ジェネレーターにより、static
で partial
のメソッドにおいて LibraryImportAttribute が検索されて、マーシャリング コードのコンパイル時ソース生成がトリガーされるので、実行時に IL スタブを生成する必要がなくなり、P/Invoke をインライン化できるようになります。 また、組み込みシステムからソース ジェネレーターへの移行や、一般的な使用に役立つよう、アナライザーとコード修正ツールも含まれています。
基本的な使用
LibraryImportAttribute は、DllImportAttribute と同じように使用できるように設計されています。 LibraryImportAttribute を使用し、メソッドを extern
ではなく partial
としてマークすることで、P/Invoke ソース生成を使用するように前の例を変換できます。
[LibraryImport(
"nativelib",
EntryPoint = "to_lower",
StringMarshalling = StringMarshalling.Utf16)]
internal static partial string ToLower(string str);
コンパイル中にソース ジェネレーターによって、string
パラメーターと戻り値の UTF-16 としてのマーシャリングを処理する ToLower
メソッドの実装の生成がトリガーされます。 マーシャリングのソース コードが生成されたので、デバッガーでそのロジックを実際に確認してステップ実行できます。
MarshalAs
ソース ジェネレーターでは、MarshalAsAttribute も尊重されます。 前記のコードは、次のように記述することもできます。
[LibraryImport(
"nativelib",
EntryPoint = "to_lower")]
[return: MarshalAs(UnmanagedType.LPWStr)]
internal static partial string ToLower(
[MarshalAs(UnmanagedType.LPWStr)] string str);
MarshalAsAttribute の一部の設定はサポートされていません。 サポートされていない設定を使おうとすると、ソース ジェネレーターでエラーが発生します。 詳しくは、「DllImport との違い」をご覧ください。
呼び出し規約
呼び出し規則を指定するには、UnmanagedCallConvAttribute を使います。次はその例です。
[LibraryImport(
"nativelib",
EntryPoint = "to_lower",
StringMarshalling = StringMarshalling.Utf16)]
[UnmanagedCallConv(
CallConvs = new[] { typeof(CallConvStdcall) })]
internal static partial string ToLower(string str);
DllImport
との違い
LibraryImportAttribute の目的は、ほとんどの場合に DllImportAttribute をいっそう簡単にすることですが、意図的な変更がいくつかあります。
- CallingConvention には、LibraryImportAttribute に相当するものはありません。 代わりに UnmanagedCallConvAttribute を使用します。
- CharSet (CharSet の場合) は、StringMarshalling (StringMarshalling の場合) に置き換えられています。 ANSI は削除され、UTF-8 が優れたオプションとして使用できるようになっています。
- BestFitMapping と ThrowOnUnmappableChar に相当するものはありません。 これらのフィールドは、Windows で ANSI 文字列をマーシャリングする場合にのみ関連していました。 ANSI 文字列のマーシャリング用に生成されるコードは、
BestFitMapping=false
およびThrowOnUnmappableChar=false
と同じように動作します。 - ExactSpelling に相当するものはありません。 これは Windows 中心の設定であり、Windows オペレーティング システム以外では効果はありませんでした。 メソッド名または EntryPoint では、エントリ ポイント名の正確なスペルを指定する必要があります。 このフィールドは、Win32 のプログラミングで使用される
A
サフィックスにW
関連して過去に使用されてきました。 - PreserveSig に相当するものはありません。 このフィールドは Windows 中心の設定でした。 生成されるコードでは、シグネチャは常に直接変換されます。
- AllowUnsafeBlocks を使って、プロジェクトをアンセーフとマークする必要があります。
また、MarshalAsAttribute での一部の設定、特定の型の既定のマーシャリング、その他の相互運用関連の属性についても、サポートに違いがあります。 詳しくは、互換性の違いに関するドキュメントをご覧ください。
関連項目
.NET