Källgenerering för plattform anropar

.NET 7 introducerar en källgenerator för P/Invokes som identifierar LibraryImportAttribute I C#-koden.

När det inte använder källgenerering genererar det inbyggda interopsystemet i .NET-körningen en IL-stub – en ström av IL-instruktioner som är JIT-ed – vid körning för att underlätta övergången från hanterad till ohanterad. Följande kod visar hur du definierar och sedan anropar en P/Invoke som använder den här mekanismen:

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

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

IL-stuben hanterar rangering av parametrar och returvärden och anropar den ohanterade koden samtidigt som inställningarna för DllImportAttribute detta påverkar hur den ohanterade koden ska anropas (till exempel SetLastError). Eftersom den här IL-stuben genereras vid körning är den inte tillgänglig för AOT-kompilator eller IL-trimningsscenarier i förväg. Generation av IL representerar en viktig kostnad att tänka på för marshalling. Den här kostnaden kan mätas i termer av programprestanda och stöd för potentiella målplattformar som kanske inte tillåter dynamisk kodgenerering. Den interna AOT-programmodellen åtgärdar problem med dynamisk kodgenerering genom att förkompilera all kod i förväg direkt i den interna koden. Att använda DllImport är inte ett alternativ för plattformar som kräver fullständiga interna AOT-scenarier och därför är det lämpligare att använda andra metoder (till exempel källgenerering). Felsökning av marshallinglogik i DllImport scenarier är också en icke-trivial övning.

P/Invoke-källgeneratorn, som ingår i .NET 7 SDK och aktiveras som standard, söker efter en staticpartial och-metod för LibraryImportAttribute att utlösa kompileringstidskällans generering av kod, vilket tar bort behovet av att generera en IL-stub vid körningstid och tillåter att P/Invoke infogas. Analysverktyg och kodkorrigeringar ingår också för att hjälpa till med migrering från det inbyggda systemet till källgeneratorn och med användning i allmänhet.

Grundläggande användning

LibraryImportAttribute Är utformad för att likna DllImportAttribute i användning. Vi kan konvertera föregående exempel till att använda P/Invoke-källgenerering med hjälp LibraryImportAttribute av och markera metoden som partial i stället för extern:

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

Under kompileringen utlöses källgeneratorn för att generera en implementering av ToLower metoden som hanterar marshalling av parametern string och returnerar värdet som UTF-16. Eftersom marshallingen nu genereras källkod kan du faktiskt titta på och gå igenom logiken i ett felsökningsprogram.

MarshalAs

Källgeneratorn respekterar MarshalAsAttributeockså . Föregående kod kan också skrivas som:

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

Vissa inställningar för MarshalAsAttribute stöds inte. Källgeneratorn genererar ett fel om du försöker använda inställningar som inte stöds. Mer information finns i Skillnader från DllImport.

Samtalskonvention

Om du vill ange anropskonventionen använder du UnmanagedCallConvAttribute, till exempel:

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

Skillnader från DllImport

LibraryImportAttribute är avsedd att vara en enkel konvertering från DllImportAttribute i de flesta fall, men det finns några avsiktliga ändringar:

  • CallingConvention har ingen motsvarighet på LibraryImportAttribute. UnmanagedCallConvAttribute ska användas i stället.
  • CharSet (för CharSet) har ersatts med StringMarshalling (för StringMarshalling). ANSI har tagits bort och UTF-8 är nu tillgängligt som ett förstklassigt alternativ.
  • BestFitMapping och ThrowOnUnmappableChar har ingen motsvarighet. De här fälten var bara relevanta när du samlade en ANSI-sträng i Windows. Den genererade koden för att ordna en ANSI-sträng har motsvarande beteende BestFitMapping=false för och ThrowOnUnmappableChar=false.
  • ExactSpelling har ingen motsvarighet. Det här fältet var en Windows-centrerad inställning och hade ingen effekt på andra operativsystem än Windows. Metodnamnet eller EntryPoint bör vara den exakta stavningen av startpunktens namn. Det här fältet har historiska användningsområden relaterade till suffixenA och W som används i Win32-programmering.
  • PreserveSig har ingen motsvarighet. Det här fältet var en Windows-centrerad inställning. Den genererade koden översätter alltid signaturen direkt.
  • Projektet måste markeras som osäkert med AllowUnsafeBlocks.

Det finns också skillnader i stöd för vissa inställningar för MarshalAsAttribute, standard marshalling av vissa typer och andra interop-relaterade attribut. Mer information finns i vår dokumentation om kompatibilitetsskillnader.

Se även