0Pricing
C# Academy · Lektion

LibraryImport und quellcodegeneriertes P/Invoke

Verwenden Sie [LibraryImport] (C# 11+) für AOT-kompatibles, quellcodegeneriertes Marshaling, das schneller ist als DllImport.

LibraryImport und quellcodegeneriertes P/Invoke ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C# Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Warum LibraryImport

[LibraryImport] wurde in .NET 7 (C# 11) als quellgenerierter, AOT-kompatibler Ersatz für [DllImport] eingeführt. Das klassische DllImport verwendet zur Laufzeit ein auf Reflection basierendes Marshalling – problematisch für Native AOT. LibraryImport erzeugt den gesamten Marshalling-Code zur Kompilierzeit.

Eine LibraryImport-Methode deklarieren

Markieren Sie eine static partial-Methode mit [LibraryImport]. Der Source Generator ergänzt die Implementierung. Außerdem müssen Sie die enthaltende Klasse mit partial markieren.

using System.Runtime.InteropServices;

internal static partial class NativeMethods
{
    // Source generator creates the P/Invoke body at compile time
    [LibraryImport("mylib", EntryPoint = "add_integers")]
    internal static partial int AddIntegers(int a, int b);

    // String marshaling must be explicit in LibraryImport
    [LibraryImport("mylib", StringMarshalling = StringMarshalling.Utf8)]
    internal static partial int ProcessString(string text);
}

String-Marshalling in LibraryImport

Im Gegensatz zu DllImport müssen Sie bei LibraryImport das String-Marshalling explizit angeben. Sie können das StringMarshalling-Enum oder MarshalAs-Attribute verwenden, sodass die Kosten des Marshallings sichtbar und kontrollierbar werden.

// Option 1: StringMarshalling enum (all strings in method)
[LibraryImport("kernel32", StringMarshalling = StringMarshalling.Utf16)]
static partial bool CreateDirectory(string lpPathName, nint lpSecurityAttributes);

// Option 2: Per-parameter MarshalAs
[LibraryImport("libc")]
static partial int Open(
    [MarshalAs(UnmanagedType.LPUTF8Str)] string path,
    int flags);

// Option 3: MarshalUsing for custom marshalers
[LibraryImport("mylib")]
static partial void Process(
    [MarshalUsing(typeof(Utf8StringMarshaller))] string name);

Struct-Marshalling

Fügen Sie bei Strukturen [NativeMarshalling] hinzu, um zu definieren, wie der verwaltete Typ seiner nativen Darstellung zugeordnet wird. Der Source Generator verwendet den Marshaller-Typ, um sicheren Code mit möglichst wenigen Allokationen zu erzeugen.

[NativeMarshalling(typeof(PointMarshaller))]
public struct Point
{
    public int X;
    public int Y;
}

[CustomMarshaller(typeof(Point), MarshalMode.Default, typeof(PointMarshaller))]
public static class PointMarshaller
{
    public static Point ConvertToManaged(NativePoint native)
        => new Point { X = native.X, Y = native.Y };
    public static NativePoint ConvertToUnmanaged(Point managed)
        => new NativePoint { X = managed.X, Y = managed.Y };

    [StructLayout(LayoutKind.Sequential)]
    public struct NativePoint { public int X, Y; }
}

DllImport und LibraryImport im Vergleich

DllImport wird zur Laufzeit interpretiert – langsamer Start, Reflection-basierte Verarbeitung und nicht mit AOT-Trimming kompatibel. LibraryImport generiert zur Buildzeit optimierten C#-Code: keine Reflection zur Laufzeit, trim-sicher und in Benchmarks messbar schneller.

// Old — DllImport (still works, but avoid for new AOT code)
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool MoveFile(string src, string dst);

// New — LibraryImport (AOT-safe, source-generated)
[LibraryImport("kernel32", StringMarshalling = StringMarshalling.Utf16,
                SetLastError = true)]
static partial bool MoveFile(string src, string dst);
// The compiler generates the actual P/Invoke wrapper body

SetLastError und Fehlerbehandlung

Setzen Sie SetLastError = true in [LibraryImport], um den Fehlercode des Betriebssystems zu erfassen. Verwenden Sie Marshal.GetLastPInvokeError() (bevorzugt) oder Marshal.GetLastWin32Error(), um ihn nach dem Aufruf abzurufen.

[LibraryImport("kernel32", StringMarshalling = StringMarshalling.Utf16,
                SetLastError = true)]
static partial bool CreateDirectory(string path, nint secAttr);

bool ok = CreateDirectory(@"C:\Temp\NewDir", 0);
if (!ok)
{
    int err = Marshal.GetLastPInvokeError();
    // err is ERROR_ALREADY_EXISTS (183) if folder exists
    throw new Win32Exception(err);
}

Span<T> und Memory-Marshalling

Einer der Vorteile von quellgeneriertem P/Invoke ist die erstklassige Unterstützung von Span<T>. Die Übergabe eines ReadOnlySpan<byte> vermeidet das Pinning und die Allokation, die DllImport mit Arrays erfordern würde.

[LibraryImport("mylib")]
static partial int ProcessBuffer(
    ReadOnlySpan<byte> data,
    int length);

// Usage — no fixed or GCHandle needed
byte[] buffer = Encoding.UTF8.GetBytes("hello");
int result = ProcessBuffer(buffer, buffer.Length);

// For output buffers use Span<byte>
[LibraryImport("mylib")]
static partial int FillBuffer(Span<byte> output, int maxLen);

Quellcodegenerierung aktivieren

Die Quellcodegenerierung wird automatisch aktiviert, wenn Sie den Namespace System.Runtime.InteropServices in einem Projekt referenzieren, das auf .NET 7 oder höher abzielt. Für ältere Zielplattformen benötigen Sie das Analyzer-Paket Microsoft.Interop.SourceGeneration.

<!-- In your .csproj — no extra package needed on .NET 7+ -->
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <!-- Enable Roslyn analyzers and source generators -->
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <!-- AllowUnsafeBlocks may be needed for some marshalers -->
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
</Project>

Generierten Code untersuchen

Fügen Sie <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> zu Ihrer csproj-Datei hinzu, um generierte Dateien in obj/ zu schreiben. So können Sie genau untersuchen, was der Source Generator erzeugt – eine hervorragende Übung zum Lernen.

// The generator emits something like this for a LibraryImport method:
// (simplified view of generated stub)

internal static partial int AddIntegers(int a, int b)
{
    // Direct call — no reflection, no boxing, fully inlineable
    return __PInvoke(a, b);

    [System.Runtime.InteropServices.DllImportAttribute(
        "mylib",
        EntryPoint = "add_integers",
        ExactSpelling = true)]
    static extern int __PInvoke(int a, int b);
}

Praxisbeispiel: Eine C-Bibliothek kapseln

Ein gängiges Muster besteht darin, eine statische Wrapperklasse mit allen LibraryImport-Deklarationen zu definieren und darüber eine sichere API auf höherer Ebene bereitzustellen. Halten Sie die partial-Deklarationen internal/private und veröffentlichen Sie nur die sicheren Wrapper.

internal static partial class LibSodiumNative
{
    private const string LibName = "libsodium";

    [LibraryImport(LibName, EntryPoint = "crypto_secretbox_keybytes")]
    internal static partial nuint KeyBytes();

    [LibraryImport(LibName, EntryPoint = "crypto_secretbox_easy")]
    internal static partial int SecretBoxEasy(
        Span<byte> ciphertext,
        ReadOnlySpan<byte> message,
        ulong mlen,
        ReadOnlySpan<byte> nonce,
        ReadOnlySpan<byte> key);
}

// Public safe wrapper hides the native signature
public static byte[] Encrypt(byte[] message, byte[] key, byte[] nonce)
{
    var cipher = new byte[message.Length + 16];
    LibSodiumNative.SecretBoxEasy(cipher, message, (ulong)message.Length, nonce, key);
    return cipher;
}

Schnelltest

Was ist der wichtigste Vorteil von [LibraryImport] gegenüber [DllImport]?

Zusammenfassung: LibraryImport und quellgeneriertes P/Invoke

Wichtigste Erkenntnisse:

  • [LibraryImport] ersetzt [DllImport] für AOT-sicheres P/Invoke
  • Das Marshalling wird zur Kompilierzeit generiert – keine Reflection zur Laufzeit
  • Erfordert eine static partial-Methode und eine partial-Klasse
  • String-Marshalling muss über StringMarshalling oder MarshalAs explizit angegeben werden
  • Erstklassige Unterstützung für Span<T> ohne Pinning-Overhead
  • Generierten Code mit EmitCompilerGeneratedFiles untersuchen

Häufig gestellte Fragen

Ist die Lektion „LibraryImport und quellcodegeneriertes P/Invoke“ kostenlos?

Ja — der vollständige Text von „LibraryImport und quellcodegeneriertes P/Invoke“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C# Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „LibraryImport und quellcodegeneriertes P/Invoke“?

Verwenden Sie [LibraryImport] (C# 11+) für AOT-kompatibles, quellcodegeneriertes Marshaling, das schneller ist als DllImport. Du übst C# Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um C# Academy zu starten?

Keine Vorkenntnisse erforderlich. C# Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.

Wie lange dauert die Lektion „LibraryImport und quellcodegeneriertes P/Invoke“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser C# Academy-Lektion Code schreiben und ausführen?

Ja. Jede C# Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Grundlagen von P/Invoke
  2. LibraryImport und quellcodegeneriertes P/Invoke
  3. Unsicherer Code, Zeiger und feste Puffer
  4. COM-Interop und Runtime Callable Wrappers
← Zurück zu C# Academy