Grundlagen von P/Invoke
Deklarieren und rufen Sie native Funktionen mit DllImport auf und verstehen Sie das Marshaling von primitiven Typen, Zeichenfolgen und Structs.
Grundlagen von P/Invoke ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 1 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.
Was ist P/Invoke?
Platform Invocation Services (P/Invoke) ermöglicht es C#, Funktionen aus nativen gemeinsam genutzten Bibliotheken aufzurufen (.dll unter Windows, .so unter Linux, .dylib unter macOS). Dies ist der Standardmechanismus, um Win32-APIs oder beliebige Bibliotheken mit C-ABI aus .NET aufzurufen.
Ihr erster P/Invoke-Aufruf
Deklarieren Sie die native Funktion mit [DllImport] und geben Sie den Bibliotheksnamen an. Die CLR übernimmt das Auffinden und Laden der Bibliothek sowie das Marshalling der Argumente.
using System.Runtime.InteropServices;
// Calling MessageBoxW from user32.dll (Windows)
internal static partial class NativeMethods
{
[DllImport("user32.dll",
EntryPoint = "MessageBoxW",
CharSet = CharSet.Unicode,
SetLastError = true)]
internal static extern int MessageBox(
IntPtr hwnd,
string text,
string caption,
uint type);
}
// Call it:
NativeMethods.MessageBox(IntPtr.Zero, "Hello!", "P/Invoke", 0);
// Windows-only — wrap in RuntimeInformation.IsOSPlatform check
// for cross-platform codeBenutzerdefinierte native Bibliotheken aufrufen
P/Invoke funktioniert mit jeder von C exportierten Funktion, nicht nur mit Betriebssystem-APIs. Erstellen Sie eine native Bibliothek, exportieren Sie Funktionen mit C-Verknüpfung und rufen Sie sie aus C# auf.
// mymath.h / mymath.c:
// extern "C" double AddNumbers(double a, double b) { return a + b; }
// Compile: gcc -shared -o libmymath.so mymath.c
// C# declaration:
[DllImport("mymath", // libmymath.so / mymath.dll
EntryPoint = "AddNumbers",
CallingConvention = CallingConvention.Cdecl)]
private static extern double AddNumbers(double a, double b);
// Call:
double result = AddNumbers(3.14, 2.72); // 5.86
// .NET resolves the library via:
// 1. Absolute path if provided
// 2. App directory
// 3. OS library path (PATH / LD_LIBRARY_PATH / DYLD_LIBRARY_PATH)Grundlegende Typen marshallen
Die CLR marshalt die meisten primitiven Typen automatisch zwischen verwalteten und nativen Darstellungen. Wenn Sie die Zuordnungen kennen, vermeiden Sie schwer erkennbare Fehler.
// C Type → C# Type
// int (32-bit) → int or System.Int32
// long (64-bit) → long or System.Int64
// float → float
// double → double
// bool → [MarshalAs(UnmanagedType.Bool)] bool
// char* (ANSI) → string (CharSet.Ansi)
// wchar_t* → string (CharSet.Unicode)
// void* → IntPtr
// size_t → UIntPtr
// Example with explicit marshalling:
[DllImport("libc", EntryPoint = "strlen", CharSet = CharSet.Ansi)]
private static extern UIntPtr StrLen(
[MarshalAs(UnmanagedType.LPStr)] string s);
int len = (int)StrLen("hello"); // 5Strukturen an nativen Code übergeben
Verwenden Sie [StructLayout(LayoutKind.Sequential)], um sicherzustellen, dass die Struktur im Speicher genau so angeordnet ist, wie es der native Code erwartet.
// C struct:
// struct Point { int x; int y; };
// void DrawPoint(struct Point p);
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int X;
public int Y;
}
[DllImport("graphics", EntryPoint = "DrawPoint",
CallingConvention = CallingConvention.Cdecl)]
private static extern void DrawPoint(Point p);
// Pass by value:
DrawPoint(new Point { X = 10, Y = 20 });
// Pass by pointer (ref or out):
[DllImport("graphics", EntryPoint = "GetCenter",
CallingConvention = CallingConvention.Cdecl)]
private static extern void GetCenter(out Point center);
GetCenter(out var pt);Fehlerbehandlung: GetLastWin32Error
Windows-API-Funktionen signalisieren Fehler über GetLastError(). Setzen Sie SetLastError = true in [DllImport] und rufen Sie nach dem Aufruf Marshal.GetLastWin32Error() auf.
[DllImport("kernel32.dll",
EntryPoint = "CreateFileW",
CharSet = CharSet.Unicode,
SetLastError = true)]
private static extern IntPtr CreateFile(
string fileName,
uint desiredAccess,
uint shareMode,
IntPtr securityAttributes,
uint creationDisposition,
uint flagsAndAttributes,
IntPtr templateFile);
const uint GENERIC_READ = 0x80000000;
const uint OPEN_EXISTING = 3;
var handle = CreateFile("test.txt", GENERIC_READ, 0,
IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (handle == (IntPtr)(-1))
{
int error = Marshal.GetLastWin32Error();
throw new System.ComponentModel.Win32Exception(error);
}Strings und Puffer marshallen
Beim String-Marshalling müssen Sie sorgfältig auf die Kodierung und den Besitz achten. Verwenden Sie StringBuilder für Ausgabepuffer und MarshalAs-Attribute, um die Kodierung anzugeben.
// Read into a buffer:
[DllImport("kernel32.dll",
EntryPoint = "GetComputerNameW",
CharSet = CharSet.Unicode,
SetLastError = true)]
private static extern bool GetComputerName(
System.Text.StringBuilder lpBuffer,
ref uint nSize);
uint size = 256;
var buffer = new System.Text.StringBuilder((int)size);
if (GetComputerName(buffer, ref size))
Console.WriteLine(buffer.ToString());
// Return a string owned by native code (don't free it):
[DllImport("mylib", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string GetVersion();Funktionszeiger und Callbacks
Übergeben Sie verwaltete Delegates als C-Funktionszeiger. Die CLR erstellt einen Thunk, aber Sie müssen den Delegate am Leben erhalten (eine Referenz darauf halten), andernfalls wird er von der GC eingesammelt und der native Code stürzt ab.
// C callback signature: typedef int (*Comparer)(const void*, const void*);
// void qsort(void* base, size_t nitems, size_t size, Comparer compar);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int CompareCallback(IntPtr a, IntPtr b);
[DllImport("libc", CallingConvention = CallingConvention.Cdecl)]
private static extern void QSort(
int[] data, UIntPtr count, UIntPtr size, CompareCallback compare);
// Keep the delegate ALIVE for the duration of the call:
private static readonly CompareCallback _compare =
(a, b) => Marshal.ReadInt32(a).CompareTo(Marshal.ReadInt32(b));
var data = new[] { 5, 2, 8, 1, 3 };
QSort(data, (UIntPtr)data.Length, (UIntPtr)sizeof(int), _compare);NativeLibrary-API
Die Klasse NativeLibrary ermöglicht das explizite Laden von Bibliotheken, die Auflösung von Funktionszeigern und die plattformübergreifende Anpassung von Pfaden – sie ist die moderne Alternative zur magischen Auflösung von Bibliotheksnamen.
using System.Runtime.InteropServices;
// Explicit load:
var handle = NativeLibrary.Load("/usr/lib/libssl.so.3");
// Resolve a function pointer:
var addPtr = NativeLibrary.GetExport(handle, "AddNumbers");
var addFn = Marshal.GetDelegateForFunctionPointer<Func<double,double,double>>(addPtr);
double result = addFn(1.0, 2.0);
// Unload:
NativeLibrary.Free(handle);
// Custom resolver (called when DllImport can't find a library):
NativeLibrary.SetDllImportResolver(typeof(MyNative).Assembly,
(libName, assembly, searchPath) =>
{
if (libName == "mymath")
return NativeLibrary.Load("/opt/mymath/libmymath.so");
return IntPtr.Zero;
});Praxisbeispiel: OpenSSL aufrufen
Ein Praxisbeispiel: Aufrufen der SHA-256-Digest-Funktion von OpenSSL aus C# mit P/Invoke.
internal static class OpenSslInterop
{
[DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr EVP_MD_CTX_new();
[DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
private static extern void EVP_MD_CTX_free(IntPtr ctx);
[DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr EVP_sha256();
[DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
private static extern int EVP_DigestInit_ex(IntPtr ctx, IntPtr type, IntPtr engine);
// In practice, use System.Security.Cryptography.SHA256 instead:
// var hash = SHA256.HashData(data);
// P/Invoke to OpenSSL is only needed when you require
// non-managed cryptographic operations or specific OpenSSL features
}Kurzer Check
Warum müssen Sie einen Delegate am Leben erhalten, wenn Sie ihn als nativen Callback übergeben?
Zusammenfassung: P/Invoke-Grundlagen
Wichtigste Erkenntnisse:
- P/Invoke: Aufrufen nativer C-ABI-Funktionen über
[DllImport]-Deklarationen - Die CLR führt für primitive Typen automatisch das Marshalling durch; für Zeichenfolgen und benutzerdefinierte Typen verwenden Sie
[MarshalAs] [StructLayout(LayoutKind.Sequential)]: Sicherstellen, dass das Speicherlayout der Struktur den Erwartungen des nativen Codes entspricht- Setzen Sie
SetLastError = trueund rufen SieMarshal.GetLastWin32Error()für die Win32-Fehlerbehandlung auf - Halten Sie Delegate-Verweise am Leben, wenn Sie sie als native Callbacks übergeben
NativeLibrary: Explizites Laden und Auflösen sowie ein benutzerdefinierter Resolver zur plattformübergreifenden Steuerung von Pfaden
Häufig gestellte Fragen
Ist die Lektion „Grundlagen von P/Invoke“ kostenlos?
Ja — der vollständige Text von „Grundlagen von 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 „Grundlagen von P/Invoke“?
Deklarieren und rufen Sie native Funktionen mit DllImport auf und verstehen Sie das Marshaling von primitiven Typen, Zeichenfolgen und Structs. 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 1 von 4.
Wie lange dauert die Lektion „Grundlagen von 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
- Grundlagen von P/Invoke
- LibraryImport und quellcodegeneriertes P/Invoke
- Unsicherer Code, Zeiger und feste Puffer
- COM-Interop und Runtime Callable Wrappers