平台叫用的來源產生
.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 中,並依預設啟用) 會在 static
和 partial
方法上尋找 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 轉換,但有一些刻意的變更:
- 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 上的某些設定、特定類型的預設封送處理和其他 Interop 相關屬性的支援也有些差異。 如需詳細資訊,請參閱我們的相容性差異文件。