平台叫用的來源產生

.NET 7 引進 P/Invokes 的來源產生器,可辨識 C# 程式碼中的 LibraryImportAttribute

當它未使用來源產生時,執行階段中的 .NET 內建 Interop 系統會產生 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 虛設常式會處理參數和傳回值封送處理,並呼叫 Unmanaged 程式碼,同時遵守會影響 Unmanaged 程式碼叫用方式的 DllImportAttribute 相關設定 (例如,SetLastError)。 由於此 IL 虛設常式是在執行階段產生,因此不適用於預先 (AOT) 編譯器或 IL 修剪案例。 IL 的產生代表要考慮封送處理的重要成本。 根據應用程式效能以及支援可能不允許動態程式碼產生的潛在目標平台,可以測量此成本。 原生 AOT 應用程式模型會預先將所有程式碼預先編譯成機器碼,來解決動態程式碼產生的問題。 使用 DllImport 並非需要完整原生 AOT 案例的平台選項,因此使用其他方法 (例如,來源產生器) 較為適當。 在 DllImport 案例中對封送處理邏輯進行偵錯也是非一般的練習。

P/Invoke 來源產生器 (隨附於 .NET 7 SDK 中,並依預設啟用) 會在 staticpartial 方法上尋找 LibraryImportAttribute 來觸發封送處理程式碼的編譯時間來源產生,而不需要在執行階段產生 IL 虛設常式,並且可以內嵌 P/Invoke。 另包含分析器和程式碼修正程式,以協助從內建系統移轉至來源產生器,以及一般使用方式。

基本使用方式

LibraryImportAttribute 設計為要在用法上類似於 DllImportAttribute。 我們可以使用 LibraryImportAttribute,將上述範例轉換成使用 P/Invoke 來源產生,並將方法標示為 partial 而不是 extern

[LibraryImport(
    "nativelib",
    EntryPoint = "to_lower",
    StringMarshalling = StringMarshalling.Utf16)]
internal static partial string ToLower(string str);

在編譯期間,來源產生器會觸發以產生 ToLower 方法的實作,以處理 string 參數封送處理,並將值傳回為 UTF-16。 由於封送處理目前已產生原始程式碼,因此您可以在偵錯工具中實際查看並逐步執行邏輯。

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 轉換,但有一些刻意的變更:

對於 MarshalAsAttribute 上的某些設定、特定類型的預設封送處理和其他 Interop 相關屬性的支援也有些差異。 如需詳細資訊,請參閱我們的相容性差異文件

另請參閱