Como realizar marshaling de ponteiros inseridos usando PInvoke
As funções que são implementadas na DLL não gerenciado podem ser chamadas de código gerenciado usando a funcionalidade de invocação de plataforma (P/Invoke). Se o código-fonte do DLL não estiver disponível, P/Invoke é a única opção para interoperar. No entanto, diferentemente de outras linguagens .NET, Visual C++ fornece uma alternativa a P/Invoke. Para obter mais informações, consulte Usando interop C++ (PInvoke implícito) e Como realizar marshaling de ponteiros inseridos usando interop C++.
Exemplo
Passe estruturas em código nativo que requer uma estrutura gerenciado que é equivalente em termos de layout de dados à estrutura nativo é criada. No entanto, as estruturas que contêm ponteiros exigem tratamento especial. Para cada ponteiro inserido na estrutura nativo, a versão gerenciado da estrutura deve conter uma instância do tipo de IntPtr . Além disso, a memória para essas instâncias deve ser atribuída explicitamente, inicializado, e liberada usando AllocCoTaskMem, StructureToPtr, e os métodos de FreeCoTaskMem .
O código a seguir consiste em um módulo não gerenciado e gerenciado. O módulo não gerenciado é uma DLL que define uma função que aceita uma estrutura chamada ListString que contém um ponteiro, e uma função chamada TakesListStruct. O módulo gerenciado é um aplicativo de linha de comando que importa a função de TakesListStruct e define uma estrutura chamada MListStruct que é equivalente a ListStruct nativo com exceção de que o double* é representado por uma instância de IntPtr . Antes de chamar TakesListStruct, a função principal e atribui inicializa a memória que faz referência a esse campo.
O módulo gerenciado é compilado com /clr, mas trabalho de /clr:pure também.
// TraditionalDll6.cpp
// compile with: /EHsc /LD
#include <stdio.h>
#include <iostream>
#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif
#pragma pack(push, 8)
struct ListStruct {
int count;
double* item;
};
#pragma pack(pop)
extern "C" {
TRADITIONALDLL_API void TakesListStruct(ListStruct);
}
void TakesListStruct(ListStruct list) {
printf_s("[unmanaged] count = %d\n", list.count);
for (int i=0; i<list.count; i++)
printf_s("array[%d] = %f\n", i, list.item[i]);
}
// EmbeddedPointerMarshalling.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;
[StructLayout(LayoutKind::Sequential, Pack=8)]
value struct MListStruct {
int count;
IntPtr item;
};
value struct TraditionalDLL {
[DllImport("TraditionalDLL6.dll")]
static public void TakesListStruct(MListStruct);
};
int main() {
array<double>^ parray = gcnew array<double>(10);
Console::WriteLine("[managed] count = {0}", parray->Length);
Random^ r = gcnew Random();
for (int i=0; i<parray->Length; i++) {
parray[i] = r->NextDouble() * 100.0;
Console::WriteLine("array[{0}] = {1}", i, parray[i]);
}
int size = Marshal::SizeOf(double::typeid);
MListStruct list;
list.count = parray->Length;
list.item = Marshal::AllocCoTaskMem(size * parray->Length);
for (int i=0; i<parray->Length; i++) {
IntPtr t = IntPtr(list.item.ToInt32() + i * size);
Marshal::StructureToPtr(parray[i], t, false);
}
TraditionalDLL::TakesListStruct( list );
Marshal::FreeCoTaskMem(list.item);
}
Observe que nenhuma parte da DLL está exposta ao código gerenciado usando a diretiva tradicional de #include. De fato, a DLL é acessado em tempo de execução, apenas assim que os problemas com as funções com DllImportAttribute importadas não serão detectados em tempo de compilação.