Współdziałanie kodu natywnego z natywną funkcją AOT

Współdziałanie kodu natywnego to technologia, która umożliwia korzystanie z bibliotek niezarządzanych przez kod zarządzany lub udostępnianie bibliotek zarządzanych kodowi niezarządzanemu (w przeciwnym kierunku).

Mimo że współdziałanie kodu natywnego działa podobnie we wdrożeniach natywnej usługi AOT i innych niż AOT, istnieją pewne specyfiki, które różnią się podczas publikowania jako natywna funkcja AOT.

Bezpośrednie wywołania P/Invoke

Wywołania P/Invoke w plikach binarnych skompilowanych AOT są domyślnie wiązane bezczynnie podczas wykonywania, aby zapewnić lepszą zgodność. Kompilator AOT można skonfigurować tak, aby generował bezpośrednie wywołania dla wybranych metod P/Invoke powiązanych podczas uruchamiania przez dynamiczny moduł ładujący dostarczany z systemem operacyjnym. Niezarządzane biblioteki i punkty wejścia, do których odwołuje się bezpośrednie wywołania, muszą być zawsze dostępne w czasie wykonywania. W przeciwnym razie nie można uruchomić natywnego pliku binarnego.

Zalety bezpośrednich wywołań P/Invoke to:

  • Mają lepszą wydajność stanu stałego.
  • Umożliwiają one statyczne łączenie niezarządzanych zależności.

Konfigurację generowania P/Invoke bezpośrednio można ustawić przy użyciu elementów <DirectPInvoke> w pliku projektu. Nazwa elementu może być <nazwą modułu>, która umożliwia bezpośrednie wywołania dla wszystkich punktów wejścia w module lub <modulename!entrypointname>, co umożliwia bezpośrednie wywołanie tylko określonego modułu i punktu wejścia.

Aby określić listę punktów wejścia w pliku zewnętrznym, użyj <DirectPInvokeList> elementów w pliku projektu. Lista jest przydatna, gdy liczba bezpośrednich wywołań P/Invoke jest duża i niepraktyczne jest ich określanie za pomocą poszczególnych elementów <DirectPInvoke>. Plik może zawierać puste wiersze i komentarze rozpoczynające się od #.

Przykłady:

<ItemGroup>
  <!-- Generate direct PInvoke calls for everything in __Internal -->
  <!-- This option replicates Mono AOT behavior that generates direct PInvoke calls for __Internal -->
  <DirectPInvoke Include="__Internal" />
  <!-- Generate direct PInvoke calls for everything in libc (also matches libc.so on Linux or libc.dylib on macOS) -->
  <DirectPInvoke Include="libc" />
  <!-- Generate direct PInvoke calls for Sleep in kernel32 (also matches kernel32.dll on Windows) -->
  <DirectPInvoke Include="kernel32!Sleep" />
  <!-- Generate direct PInvoke for all APIs listed in DirectXAPIs.txt -->
  <DirectPInvokeList Include="DirectXAPIs.txt" />
</ItemGroup>

W systemie Windows natywna funkcja AOT używa wstępnie wypełnianej listy metod bezpośredniego P/Invoke, które są dostępne we wszystkich obsługiwanych wersjach systemu Windows.

Ostrzeżenie

Ponieważ metody bezpośrednie P/Invoke są rozpoznawane przez dynamiczny moduł ładujący systemu operacyjnego, a nie przez natywną bibliotekę środowiska uruchomieniowego AOT, metody bezpośrednie P/Invoke nie będą uwzględniać DefaultDllImportSearchPathsAttribute. Kolejność wyszukiwania biblioteki będzie przestrzegać reguł dynamicznego ładowania zdefiniowanych przez system operacyjny. Niektóre systemy operacyjne i moduły ładującego oferują sposoby kontrolowania dynamicznego ładowania za pośrednictwem flag konsolidatora (takich jak /DEPENDENTLOADFLAG w systemie Windows lub -rpath w systemie Linux). Aby uzyskać więcej informacji na temat określania flag linkera, zobacz sekcję Linkowanie.

Łączenie

Aby statycznie połączyć się z co najmniej jedną niezarządzaną biblioteką, należy określić <NativeLibrary Include="filename" /> wskazujące na plik .lib w systemie Windows i plik .a w systemach przypominających system Unix dla każdej biblioteki.

Przykłady:

<ItemGroup>
  <!-- Generate direct PInvokes for Dependency -->
  <DirectPInvoke Include="Dependency" />
  <!-- Specify library to link against -->
  <NativeLibrary Include="Dependency.lib" Condition="$(RuntimeIdentifier.StartsWith('win'))" />
  <NativeLibrary Include="Dependency.a" Condition="!$(RuntimeIdentifier.StartsWith('win'))" />
</ItemGroup>

Jeśli określona biblioteka natywna ma zależność od innych bibliotek statycznych, należy dodać elementy NativeLibrary dla każdej zależności przechodniej, w przeciwnym razie linker natywny zakończy się niepowodzeniem z powodu brakujących symboli.

Konwencje platformy są przestrzegane, jeśli chodzi o format NativeLibrary wpisów. Bazowy natywny linker może przeszukiwać standardowe ścieżki biblioteki lub zezwalać na ścieżki bezwzględne.

Aby określić dodatkowe flagi dla konsolidatora natywnego, użyj <LinkerArg> elementu .

Przykłady:

<ItemGroup>
  <!-- link.exe is used as the linker on Windows -->
  <LinkerArg Include="/DEPENDENTLOADFLAG:0x800" Condition="$(RuntimeIdentifier.StartsWith('win'))" />

  <!-- Native AOT invokes clang/gcc as the linker, so arguments need to be prefixed with "-Wl," -->
  <LinkerArg Include="-Wl,-rpath,'/bin/'" Condition="$(RuntimeIdentifier.StartsWith('linux'))" />
</ItemGroup>

Eksporty natywne

Natywny kompilator AOT eksportuje metody oznaczone adnotacjami z niepustą właściwością UnmanagedCallersOnlyAttribute jako publiczne punkty wejścia w języku C. Dzięki temu można dynamicznie lub statycznie połączyć skompilowane moduły AOT z programami zewnętrznymi. Rozważane są tylko metody oznaczone UnmanagedCallersOnly w opublikowanym zestawie. Metody w odwołaniach do projektu lub pakietach NuGet nie zostaną wyeksportowane. Aby uzyskać więcej informacji, zobacz Przykład NativeLibrary.

Metody z adnotacjami UnmanagedCallersOnly nie wykonują marshalingu i wymagają parametru oraz typu zwracanego będących blittable. Począwszy od .NET 11, gdy marshalling środowiska uruchomieniowego jest włączony, funkcja Native AOT zgłasza InvalidProgramException, jeśli niezarządzany kod wywołujący wywołuje taką metodę z podpisem nieblittable, takim jak typ zwracany bool lub parametr char. Wyjątek powoduje zakończenie procesu (szybkie niepowodzenie) i nie można go przechwycić, aby naprawić błąd nieprawidłowego podpisu. Użyj odpowiedników blittable lub, w przypadku obsługiwanych typów niezarządzanych, wyłącz marshalling środowiska uruchomieniowego w zestawie, który deklaruje metodę. Aby uzyskać szczegółowe informacje, zobacz Native AOT odrzuca nieblittowalne sygnatury UnmanagedCallersOnly.