Skip to main content

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:

  1. Marcar o método, bloco ou tipo com a palavra-chave unsafe.
  2. 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> ou stackalloc (C# moderno oferece opções seguras).

🧩 2. P/Invoke (Platform Invocation Services) | Projeto do Mouse

📦 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:

  • DllImport indica 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

RecursoPropósitoSegurançaUso comum
Unsafe codeAcesso direto à memória via ponteiros⚠️ Não seguroProcessamento de baixo nível
P/InvokeChamar código nativo (C/C++)✅ Seguro se usado corretamenteIntegraçã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 unsafe apenas 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 RGB255 - 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.dll

Isso 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);
}
  • fixed impede 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 .so ou .dylib.

  • Ajuste o [DllImport]:

    [DllImport("libimage_ops.so", EntryPoint = "InvertColors", CallingConvention = CallingConvention.Cdecl)]