Compartilhar via


Como realizar marshaling de matrizes usando P/Invoke

Você pode chamar funções nativas que aceitem cadeias de caracteres no estilo C usando o tipo String de cadeia de caracteres CLR ao usar o suporte para invocação de plataforma do .NET Framework. Incentivamos você a usar os recursos de interoperabilidade do C++, em vez de P/Invoke, quando possível. O P/Invoke fornece pouco relatório de erros em tempo de compilação, não é fortemente tipado e a implementação dele pode ser entediante. Se a API não gerenciada for empacotada como uma DLL e o código-fonte não estiver disponível, P/Invoke será a única opção. Caso contrário, confira Usando a interoperabilidade com C++ (P/Invoke implícito)).

Exemplo

Como matrizes nativas e gerenciadas são dispostas de forma diferente na memória, passá-las com êxito pelo limite gerenciado/não gerenciado requer conversão ou marshaling. Este artigo demonstra como uma matriz de itens simples (blitable) pode ser passada do código gerenciado para funções nativas.

Como acontece com o marshaling de dados gerenciado/não gerenciado em geral, o atributo DllImportAttribute é usado para criar um ponto de entrada gerenciado para cada função nativa usada. Em funções que usam matrizes como argumentos, o atributo MarshalAsAttribute deve ser usado para especificar como fazer marshaling dos dados. No exemplo a seguir, a enumeração UnmanagedType é usada para indicar que a matriz gerenciada tem o marshaling realizado como uma matriz de estilo C.

O código a seguir consiste em um módulo gerenciado e um não gerenciado. O módulo não gerenciado é uma DLL que define uma função que aceita uma matriz de inteiros. O segundo módulo é um aplicativo de linha de comando gerenciado que importa essa função, mas a define em termos de uma matriz gerenciada. Ele usa o atributo MarshalAsAttribute para especificar que a matriz deve ser convertida em uma matriz nativa quando chamada.

// TraditionalDll4.cpp
// compile with: /LD /EHsc
#include <iostream>

#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif

extern "C" {
   TRADITIONALDLL_API void TakesAnArray(int len, int[]);
}

void TakesAnArray(int len, int a[]) {
   printf_s("[unmanaged]\n");
   for (int i=0; i<len; i++)
      printf("%d = %d\n", i, a[i]);
}

O módulo gerenciado é compilado usando /clr.

// MarshalBlitArray.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;

value struct TraditionalDLL {
   [DllImport("TraditionalDLL4.dll")]
   static public void TakesAnArray(
   int len,[MarshalAs(UnmanagedType::LPArray)]array<int>^);
};

int main() {
   array<int>^ b = gcnew array<int>(3);
   b[0] = 11;
   b[1] = 33;
   b[2] = 55;
   TraditionalDLL::TakesAnArray(3, b);

   Console::WriteLine("[managed]");
   for (int i=0; i<3; i++)
      Console::WriteLine("{0} = {1}", i, b[i]);
}

Nenhuma parte da DLL é exposta ao código gerenciado por meio da diretiva #include tradicional. Na verdade, a DLL é acessada somente no runtime. Portanto, problemas em funções importadas usando DllImportAttribute não podem ser detectados no tempo de compilação.

Confira também

Usando P/Invoke explícito no C++ (atributo DllImport)