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 LibraryImportAttribute static
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=false
ThrowOnUnmappableChar=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 iW
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.