Generowanie źródła dla wywołań platformy

Platforma .NET 7 wprowadza generator źródła dla P/Invoke, który rozpoznaje LibraryImportAttribute kod w języku C#.

Jeśli nie używa ona generowania źródła, wbudowany system międzyoperacyjności w środowisku uruchomieniowym platformy .NET generuje wycinkę IL — strumień instrukcji IL, który jest JIT-ed — w czasie wykonywania, aby ułatwić przejście z zarządzanego do niezarządzanego. Poniższy kod pokazuje definiowanie, a następnie wywoływanie wywołania P/Invoke, które korzysta z tego mechanizmu:

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

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

Łącznik IL obsługuje marshalling parametrów i wartości zwracanych oraz wywołuje niezarządzany kod przy jednoczesnym poszanowaniu ustawień dotyczących DllImportAttribute tego wpływu na sposób wywoływania niezarządzanego kodu (na przykład SetLastError). Ponieważ ten wycink il jest generowany w czasie wykonywania, nie jest dostępny dla kompilatora AOT (head-of-time) kompilatora ani scenariuszy przycinania IL. Generowanie IL stanowi ważny koszt, który należy wziąć pod uwagę w przypadku marshallingu. Ten koszt można zmierzyć pod względem wydajności aplikacji i obsługi potencjalnych platform docelowych, które mogą nie zezwalać na dynamiczne generowanie kodu. Natywny model aplikacji AOT rozwiązuje problemy z generowaniem kodu dynamicznego przez wstępne skompilowanie całego kodu przed upływem czasu bezpośrednio do kodu natywnego. Użycie DllImport nie jest opcją dla platform, które wymagają pełnych natywnych scenariuszy AOT i dlatego użycie innych metod (na przykład generowania źródła) jest bardziej odpowiednie. Debugowanie logiki marshalling w DllImport scenariuszach jest również ćwiczeniem nietrygalnym.

Generator źródła P/Invoke dołączony do zestawu SDK platformy .NET 7 i domyślnie włączony szuka LibraryImportAttributestatic metody i partial w celu wyzwolenia generowania źródła czasu kompilacji kodu marshallingu, usuwając potrzebę generowania wycinków IL w czasie wykonywania i zezwalając na podsunięcie P/Invoke. Analizatory i poprawki kodu są również uwzględniane w celu ułatwienia migracji z wbudowanego systemu do generatora źródłowego i ogólnego użycia.

Podstawowy sposób użycia

Element LibraryImportAttribute jest zaprojektowany tak, aby był podobny do DllImportAttribute użycia. Możemy przekonwertować poprzedni przykład, aby użyć generowania źródła P/Invoke, używając LibraryImportAttribute metody i oznaczając metodę jako partial zamiast extern:

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

Podczas kompilacji generator źródła będzie wyzwalany w celu wygenerowania implementacji ToLower metody obsługującej marshalling parametru string i zwracanej wartości jako UTF-16. Ponieważ marshalling jest teraz generowany kod źródłowy, możesz faktycznie przyjrzeć się logice i przejść przez ten proces w debugerze.

MarshalAs

Generator źródła uwzględnia również element MarshalAsAttribute. Powyższy kod może być również napisany jako:

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

Niektóre ustawienia nie MarshalAsAttribute są obsługiwane. Generator źródła wygeneruje błąd, jeśli spróbujesz użyć nieobsługiwanych ustawień. Aby uzyskać więcej informacji, zobacz Różnice z dllImport.

Konwencja wywoływania

Aby określić konwencję wywoływania, użyj polecenia UnmanagedCallConvAttribute, na przykład:

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

Różnice w porównaniu z DllImport

LibraryImportAttribute jest przeznaczona do prostej konwersji z DllImportAttribute w większości przypadków, ale istnieją pewne zamierzone zmiany:

  • CallingConvention nie ma odpowiednika w pliku LibraryImportAttribute. UnmanagedCallConvAttribute Zamiast tego należy użyć polecenia .
  • CharSet (dla CharSet) element został zastąpiony elementem StringMarshalling (dla elementu StringMarshalling). Usługa ANSI została usunięta, a utF-8 jest teraz dostępna jako opcja pierwszej klasy.
  • BestFitMapping i ThrowOnUnmappableChar nie mają odpowiednika. Te pola były istotne tylko podczas wybierania ciągu ANSI w systemie Windows. Wygenerowany kod do marshalingu ciągu ANSI będzie miał równoważne zachowanie i BestFitMapping=falseThrowOnUnmappableChar=false.
  • ExactSpelling nie ma odpowiednika. To pole było ustawieniem skoncentrowanym na systemie Windows i nie miało wpływu na systemy operacyjne innych niż Windows. Nazwa metody lub EntryPoint powinna być dokładną pisownią nazwy punktu wejścia. To pole zawiera historyczne zastosowania związane z A sufiksami i W używanymi w programowaniu Win32.
  • PreserveSig nie ma odpowiednika. To pole było ustawieniem skoncentrowanym na systemie Windows. Wygenerowany kod zawsze bezpośrednio tłumaczy podpis.
  • Projekt musi być oznaczony jako niebezpieczny przy użyciu polecenia AllowUnsafeBlocks.

Istnieją również różnice w obsłudze niektórych ustawień w systemie MarshalAsAttribute, domyślne marshalling niektórych typów i innych atrybutów związanych z międzyoperacyjności. Aby uzyskać więcej informacji, zobacz naszą dokumentację dotyczącą różnic w zgodności.

Zobacz też