Fondamentaux de P/Invoke
Déclarez et appelez des fonctions natives avec DllImport, puis comprenez le marshaling des types primitifs, des chaînes et des structures.
Fondamentaux de P/Invoke est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 4 leçons au total.
Qu'est-ce que P/Invoke ?
Les services d'appel de plateforme (P/Invoke) permettent à C# d'appeler des fonctions dans des bibliothèques partagées natives (.dll sous Windows, .so sous Linux et .dylib sous macOS). Il s'agit du mécanisme standard permettant d'appeler les interfaces Win32 ou toute bibliothèque dotée d'une ABI C depuis .NET.
Votre premier appel P/Invoke
Déclarez la fonction native avec [DllImport] et indiquez le nom de la bibliothèque. Le CLR se charge de trouver et de charger la bibliothèque, ainsi que de convertir les arguments.
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 codeAppeler des bibliothèques natives personnalisées
P/Invoke fonctionne avec toute fonction exportée par C, et pas seulement avec les interfaces du système d'exploitation. Générez une bibliothèque native, exportez ses fonctions avec une liaison C, puis appelez-les depuis C#.
// 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)Convertir les types de base
Le CLR convertit automatiquement la plupart des types primitifs entre leurs représentations managée et native. Connaître ces correspondances permet d'éviter des bogues difficiles à détecter.
// 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"); // 5Transmettre des structures au code natif
Utilisez [StructLayout(LayoutKind.Sequential)] pour garantir que la structure est organisée en mémoire exactement comme le code natif l'attend.
// 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);Gestion des erreurs : GetLastWin32Error
Les fonctions de l'interface Windows signalent les erreurs via GetLastError(). Définissez SetLastError = true dans [DllImport], puis appelez Marshal.GetLastWin32Error() après l'appel.
[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);
}Convertir les chaînes et les tampons
La conversion des chaînes exige une attention particulière à l'encodage et à la gestion de la propriété des données. Utilisez StringBuilder pour les tampons de sortie et les attributs MarshalAs pour spécifier l'encodage.
// 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();Pointeurs de fonction et fonctions de rappel
Transmettez des délégués managés comme des pointeurs de fonctions C. Le CLR crée un trampoline, mais vous devez maintenir le délégué en vie (conserver une référence), sinon le GC le récupérera et le code natif plantera.
// 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);API NativeLibrary
La classe NativeLibrary fournit le chargement explicite des bibliothèques, la résolution des pointeurs de fonctions et la personnalisation des chemins multiplateformes — une alternative moderne à la résolution implicite des noms de bibliothèques.
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;
});Dans la pratique : appeler OpenSSL
Un exemple concret : appeler depuis C# la fonction de hachage SHA-256 d'OpenSSL à l'aide de 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
}Vérification rapide
Pourquoi devez-vous maintenir un délégué en vie lorsque vous le transmettez comme fonction de rappel native ?
Récapitulatif : fondamentaux de P/Invoke
Points essentiels :
- P/Invoke : appeler des fonctions C natives via des déclarations
[DllImport] - Le CLR convertit automatiquement les types primitifs ; annotez les chaînes et les types personnalisés avec
[MarshalAs] [StructLayout(LayoutKind.Sequential)]: garantir que la disposition mémoire de la structure correspond aux attentes du code natif- Définissez
SetLastError = trueet appelezMarshal.GetLastWin32Error()pour gérer les erreurs Win32 - Conservez les références aux délégués lorsque vous les transmettez comme rappels natifs
NativeLibrary: chargement explicite, résolution et résolveur personnalisé pour contrôler les chemins multiplateformes
Questions Fréquemment Posées
La leçon « Fondamentaux de P/Invoke » est-elle gratuite ?
Oui — le texte complet de « Fondamentaux de P/Invoke » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Fondamentaux de P/Invoke » ?
Déclarez et appelez des fonctions natives avec DllImport, puis comprenez le marshaling des types primitifs, des chaînes et des structures. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C# Academy ?
Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « Fondamentaux de P/Invoke » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?
Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Fondamentaux de P/Invoke
- LibraryImport et P/Invoke généré à partir du code source
- Code non sécurisé, pointeurs et tampons de taille fixe
- Interopérabilité COM et wrappers appelables depuis l’environnement d’exécution