Generování zdrojového kódu pro vyvolání platformy

.NET 7 zavádí generátor zdrojového kódu pro P/Invoke volání, která rozpozná LibraryImportAttribute v C# kódu.

Pokud nepoužívá generování zdrojového kódu, integrovaný systém interoperability v modulu runtime .NET generuje zástupný kód IL – tok instrukcí IL, který je za běhu just-in-time kompilován – pro usnadnění přechodu ze spravovaného na nespravované prostředí. Následující kód ukazuje, jak definovat a následně zavolat P/Invoke, který používá tento mechanismus:

[DllImport(
    "nativelib",
    EntryPoint = "to_lower",
    CharSet = CharSet.Unicode)]
internal static extern string ToLower(string str);

// string lower = ToLower("StringToConvert");

Zástupný kód IL zpracovává zařazování parametrů a návratové hodnoty a volá nespravovaný kód při zachování nastavení DllImportAttribute , která ovlivňují způsob vyvolání nespravovaného kódu (například SetLastError). Vzhledem k tomu, že se tento zástupný kód IL generuje za běhu, není k dispozici pro scénáře ahead-of-time (AOT) kompilátoru nebo ořezávání IL. Generování IL představuje významné náklady, které je třeba zvážit při shromažďování. Tyto náklady je možné měřit z hlediska výkonu aplikace a podpory potenciálních cílových platforem, které neumožňují dynamické generování kódu. Nativní aplikační model AOT řeší problémy s dynamickým generováním kódu předkompilováním veškerého kódu předem přímo do nativního kódu. Použití DllImport není možnost pro platformy, které vyžadují úplné nativní scénáře AOT, a proto je vhodnější použít jiné přístupy (například generování zdroje). Ladění logiky zařazování ve DllImport scénářích je také netriviální cvičení.

Generátor kódu P/Invoke, který je součástí sady .NET 7 SDK a je ve výchozím nastavení povolen, hledá LibraryImportAttribute na static a partial metodu pro aktivaci generování kódu pro marshalingu během kompilace, čímž se eliminuje potřeba generování zástupných procedur IL za běhu a umožňuje volání P/Invoke inline. Analyzátory a opravy kódu jsou také zahrnuty, aby pomohly s migrací z integrovaného systému do generátoru zdrojů a obecně s využitím.

Základní použití

LibraryImportAttribute je navržený tak, aby jeho použití bylo podobné jako u DllImportAttribute. Předchozí příklad můžeme převést na použití generování zdroje P/Invoke pomocí LibraryImportAttribute a označením metody jako partial namísto extern.

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

Během kompilace se spustí generátor zdroje, který vygeneruje implementaci ToLower metody, která zpracovává zařazování parametru string a návratovou hodnotu jako UTF-16. Vzhledem k tomu, že se teď vygeneruje zdrojový kód, můžete se na logiku v ladicím programu podívat a projít si ji.

MarshalAs

Generátor zdroje také respektuje MarshalAsAttribute. Předchozí kód lze také napsat takto:

[LibraryImport(
    "nativelib",
    EntryPoint = "to_lower")]
[return: MarshalAs(UnmanagedType.LPWStr)]
internal static partial string ToLower(
    [MarshalAs(UnmanagedType.LPWStr)] string str);

Některá nastavení MarshalAsAttribute nejsou podporovaná. Pokud se pokusíte použít nepodporovaná nastavení, generátor zdroje vygeneruje chybu. Další informace naleznete v tématu Rozdíly oproti DllImport.

Volací konvence

Pokud chcete zadat konvenci volání, použijte UnmanagedCallConvAttributenapříklad:

[LibraryImport(
    "nativelib",
    EntryPoint = "to_lower",
    StringMarshalling = StringMarshalling.Utf16)]
[UnmanagedCallConv(
    CallConvs = new[] { typeof(CallConvStdcall) })]
internal static partial string ToLower(string str);

Rozdíly od DllImport

LibraryImportAttribute má být ve většině případů jednoduchým převodem DllImportAttribute , ale existují některé záměrné změny:

  • CallingConvention nemá žádný ekvivalent na LibraryImportAttribute. UnmanagedCallConvAttribute by měl být použit místo toho.
  • CharSet (pro CharSet) byla nahrazena písmenem StringMarshalling (pro StringMarshalling). ANSI byl odebrán a UTF-8 je nyní k dispozici jako prvotřídní možnost.
  • BestFitMapping a ThrowOnUnmappableChar nemají žádný ekvivalent. Tato pole byla relevantní pouze při zařazování řetězce ANSI ve Windows. Vygenerovaný kód pro převod řetězce ANSI bude mít ekvivalentní vlastnosti jako BestFitMapping=false a ThrowOnUnmappableChar=false.
  • ExactSpelling nemá žádný ekvivalent. Toto pole bylo nastavení zaměřené na Windows a nemělo žádný vliv na operační systémy jiné než Windows. Název metody nebo EntryPoint by měl být přesným pravopisem názvu vstupního bodu. Toto pole má historické použití spojené s příponami A a W, které se používají v programování Win32.
  • PreserveSig nemá žádný ekvivalent. Toto pole bylo nastavení zaměřené na Windows. Vygenerovaný kód vždy přímo přeloží podpis.
  • Projekt musí být označený jako nebezpečný pomocí AllowUnsafeBlocks.

Existují také rozdíly v podpoře některých nastavení MarshalAsAttribute, výchozí zařazování určitých typů a dalších atributů souvisejících s interoperabilitou. Další informace najdete v naší dokumentaci k rozdílům v kompatibilitě.

Viz také