Fundamentos de P/Invoke
Declare e chame funções nativas usando DllImport e entenda o marshaling de tipos primitivos, strings e structs.
Fundamentos de P/Invoke é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.
O que é P/Invoke
Os Serviços de Invocação de Plataforma (P/Invoke) permitem que o C# chame funções em bibliotecas compartilhadas nativas (.dll no Windows, .so no Linux e .dylib no macOS). Esse é o mecanismo padrão para chamar APIs Win32 ou qualquer biblioteca com ABI de C a partir do .NET.
Sua primeira chamada P/Invoke
Declare a função nativa com [DllImport] e informe o nome da biblioteca. O CLR localiza e carrega a biblioteca e converte os argumentos automaticamente.
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 codeChamando bibliotecas nativas personalizadas
O P/Invoke funciona com qualquer função exportada por C, não apenas com APIs do sistema operacional. Compile uma biblioteca nativa, exporte funções com ligação C e chame-as a partir do 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)Conversão de tipos básicos
O CLR converte automaticamente a maioria dos tipos primitivos entre representações gerenciadas e nativas. Conhecer os mapeamentos evita erros sutis.
// 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"); // 5Passando estruturas para código nativo
Use [StructLayout(LayoutKind.Sequential)] para garantir que a estrutura seja organizada na memória exatamente como o código nativo espera.
// 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);Tratamento de erros: GetLastWin32Error
As funções da API do Windows sinalizam erros por meio de GetLastError(). Defina SetLastError = true em [DllImport] e chame Marshal.GetLastWin32Error() depois da chamada.
[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);
}Conversão de cadeias de caracteres e áreas de armazenamento
A conversão de cadeias de caracteres exige atenção cuidadosa à codificação e à posse da memória. Use StringBuilder para áreas de armazenamento de saída e atributos MarshalAs para especificar a codificação.
// 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();Ponteiros de função e callbacks
Passe objetos delegate gerenciados como ponteiros de função C. O CLR cria uma ponte intermediária, mas você deve manter o delegate ativo (guardando uma referência), caso contrário, o GC o coletará e o código nativo falhará.
// 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
A classe NativeLibrary fornece carregamento explícito de bibliotecas, resolução de ponteiros de função e personalização de caminhos entre plataformas — a alternativa moderna à resolução automática de nomes de bibliotecas.
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;
});Caso real: chamando o OpenSSL
Um exemplo real: chamar a função de resumo SHA-256 do OpenSSL a partir do C# usando 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
}Verificação rápida
Por que você deve manter um delegate ativo ao passá-lo como callback nativo?
Recapitulação: fundamentos de P/Invoke
Principais conclusões:
- P/Invoke: chamar funções nativas da C-ABI por meio de declarações
[DllImport] - O CLR converte tipos primitivos automaticamente; use anotações
[MarshalAs]para strings e tipos personalizados [StructLayout(LayoutKind.Sequential)]: garante que o layout de memória da estrutura corresponda às expectativas nativas- Defina
SetLastError = truee chameMarshal.GetLastWin32Error()para tratar erros do Win32 - Mantenha as referências a delegados vivas ao passá-las como retornos de chamada nativos
NativeLibrary: carregamento explícito, resolução e resolvedor personalizado para controlar caminhos entre plataformas
Perguntas Frequentes
A aula “Fundamentos de P/Invoke” é grátis?
Sim — o texto completo de “Fundamentos de P/Invoke” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.
O que vou aprender em “Fundamentos de P/Invoke”?
Declare e chame funções nativas usando DllImport e entenda o marshaling de tipos primitivos, strings e structs. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar C# Academy?
Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.
Quanto tempo leva a aula “Fundamentos de P/Invoke”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de C# Academy?
Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Fundamentos de P/Invoke
- LibraryImport e P/Invoke gerado pela fonte
- Código não seguro, ponteiros e buffers fixos
- Interoperação com COM e wrappers chamáveis em tempo de execução