.NET 7 引進 P/Invokes 的來源產生器,可辨識 C# 程式碼中的 LibraryImportAttribute。
當不使用原始碼產生時,.NET 執行時內建的互操作系統會在執行時產生 IL stub——一串經過 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 預設啟用,會檢查 LibraryImportAttribute 來觸發 static 和 partial 方法的編譯時原始碼產生,以生成編組程式碼,從而省去在執行時產生 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 相關屬性的支援也有些差異。 如需詳細資訊,請參閱我們的相容性差異文件。