Unsafe - P/INOVKE | CSharp
- Unsafe - P/INOVKE | CSharp
⚙️ 1. Unsafe Code (Código Não Seguro)
🧩 O que é
O código unsafe permite que você use ponteiros, endereços de memória e operações aritméticas diretas, assim como em C ou C++.
Por padrão, o C# gerencia a memória automaticamente com o Garbage Collector (GC). Mas em cenários críticos de performance (como processamento de imagem, jogos, engine física, ou interoperabilidade nativa), você pode precisar acessar a memória manualmente.
🔐 Como habilitar
Para usar código unsafe, é necessário:
- Marcar o método, bloco ou tipo com a palavra-chave
unsafe. - Ativar a opção "Allow unsafe code" no projeto (ou no
.csproj):
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
💻 Exemplo de código unsafe
unsafe
{
int valor = 10;
int* ptr = &valor; // pega o endereço de memória
Console.WriteLine($"Endereço: {(long)ptr}");
Console.WriteLine($"Valor original: {*ptr}");
*ptr = 20; // altera o valor diretamente na memória
Console.WriteLine($"Novo valor: {valor}");
}
🧠 Explicação:
&valor→ obtém o endereço do valor.int* ptr→ cria um ponteiro para um inteiro.*ptr→ desreferencia o ponteiro (lê ou escreve o valor da memória).
⚡ Quando usar unsafe
✅ Cenários recomendados:
- Operações matemáticas intensivas (ex: manipulação de pixels ou buffers).
- Comunicação direta com hardware.
- Interop com bibliotecas C/C++ que exigem ponteiros.
❌ Evite se:
- O ganho de performance for mínimo.
- Você puder resolver com
Span<T>,Memory<T>oustackalloc(C# moderno oferece opções seguras).
🧩 2. P/Invoke (Platform Invocation Services) | Projeto do Mouse
- Link de um projeto que usa essa função: https://github.com/erdincdegirmenci/mouse-jiggler
📦 O que é
O P/Invoke permite chamar funções nativas (C/C++) a partir do .NET, usando DllImport.
Isso é útil quando você precisa usar uma DLL nativa (como kernel32.dll, user32.dll, opencv_world.dll, etc).
💻 Exemplo simples: chamar função nativa do Windows
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int MessageBox(IntPtr hWnd, string text, string caption, int type);
static void Main()
{
MessageBox(IntPtr.Zero, "Olá do código nativo!", "P/Invoke Demo", 0);
}
}
🧠 Aqui:
DllImportindica a DLL nativa.- O método
MessageBoxé importado e pode ser chamado como uma função C# normal. - O .NET faz o marshalling automático dos tipos (converte tipos gerenciados ↔ nativos).
⚙️ Exemplo com ponteiros e unsafe
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int puts(string str);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int strlen(byte* str);
unsafe
{
puts("Hello from native C!");
byte[] texto = System.Text.Encoding.ASCII.GetBytes("Teste\0");
fixed (byte* ptr = texto)
{
Console.WriteLine($"Comprimento: {strlen(ptr)}");
}
}
🧩 Este exemplo:
- Chama funções da biblioteca C padrão (
msvcrt.dll). - Usa ponteiros para medir o tamanho de uma string em memória nativa.
🚀 Resumo comparativo
| Recurso | Propósito | Segurança | Uso comum |
|---|---|---|---|
| Unsafe code | Acesso direto à memória via ponteiros | ⚠️ Não seguro | Processamento de baixo nível |
| P/Invoke | Chamar código nativo (C/C++) | ✅ Seguro se usado corretamente | Integração com DLLs externas |
🧠 Boas práticas
- Prefira
Span<T>/Memory<T>(C# 7.2+) quando possível — eles oferecem acesso rápido e seguro à memória. - Use
unsafeapenas em partes isoladas e bem testadas. - Documente sempre as chamadas P/Invoke, incluindo convenção de chamada e alinhamento de tipos.
- Sempre valide parâmetros antes de passá-los para código nativo.
Construindo um exemplo
Vamos construir um exemplo completo que mostra o uso combinado de
👉 unsafe (ponteiros e memória direta) e
👉 P/Invoke (chamada de código nativo C)
em um cenário realista de processamento de imagem em memória.
🧩 Cenário
Suponha que temos uma função nativa em C que inverte as cores de uma imagem (transforma RGB → 255 - RGB).
Queremos chamá-la a partir do C# usando P/Invoke, e passar o buffer de pixels via ponteiro (unsafe).
⚙️ 1. Código Nativo em C
Crie um arquivo chamado image_ops.c:
#include <stdint.h>
// Função simples que inverte as cores de uma imagem (RGB)
__declspec(dllexport)
void InvertColors(uint8_t* pixels, int length)
{
for (int i = 0; i < length; i++)
{
pixels[i] = 255 - pixels[i];
}
}
💡 Compile esse código como uma DLL no Windows:
cl /LD image_ops.c /Fe:image_ops.dllIsso vai gerar
image_ops.dll.
⚙️ 2. Código em C# (chamando a DLL nativa)
Agora no seu projeto C#, crie o arquivo Program.cs:
using System;
using System.Runtime.InteropServices;
class Program
{
// Importa a função nativa da DLL
[DllImport("image_ops.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern unsafe void InvertColors(byte* pixels, int length);
static unsafe void Main()
{
// Simula uma "imagem" simples de 3 pixels RGB
byte[] imageData = { 10, 100, 200, 50, 150, 250, 0, 128, 255 };
Console.WriteLine("🔹 Antes da inversão:");
PrintBytes(imageData);
// Fixamos o array na memória para obter o ponteiro
fixed (byte* p = imageData)
{
// Chama a função C nativa
InvertColors(p, imageData.Length);
}
Console.WriteLine("\n🔹 Depois da inversão:");
PrintBytes(imageData);
}
static void PrintBytes(byte[] data)
{
Console.WriteLine(string.Join(", ", data));
}
}
🧠 Explicação passo a passo
🧩 Etapa 1 — DllImport
[DllImport("image_ops.dll", CallingConvention = CallingConvention.Cdecl)]
- Diz ao .NET onde encontrar a função (
image_ops.dll). - Define o tipo de chamada (
Cdecl→ convenção padrão em C).
⚙️ Etapa 2 — unsafe + fixed
fixed (byte* p = imageData)
{
InvertColors(p, imageData.Length);
}
fixedimpede que o GC mova o array na memória.byte*fornece o endereço real da memória onde estão os pixels.- A função nativa C opera diretamente sobre o buffer.
🧮 Resultado esperado
Antes:
10, 100, 200, 50, 150, 250, 0, 128, 255
Depois:
245, 155, 55, 205, 105, 5, 255, 127, 0
✨ As cores foram invertidas com sucesso — tudo via ponteiro direto e código nativo!
🚀 Por que isso é poderoso
✅ O buffer é processado sem cópias — extremamente eficiente. ✅ C# controla o fluxo, mas C executa a operação pesada. ✅ Pode ser usado para:
- Filtros de imagem (blur, sharpen, grayscale).
- Processamento de áudio, vídeo ou sensores.
- Interoperar com APIs legadas em C/C++.
🧠 Dica bônus
Se quiser tornar o código multiplataforma:
-
No Linux/macOS, compile a biblioteca como
.soou.dylib. -
Ajuste o
[DllImport]:[DllImport("libimage_ops.so", EntryPoint = "InvertColors", CallingConvention = CallingConvention.Cdecl)]