Share via


Como: PInvoke usando estruturas de empacotamento

Este tópico explica como nativas funções que aceitam seqüências de caracteres de estilo C podem ser chamadas a partir gerenciado funções fornecendo uma instância de String usando P/Invoke. O Visual C++ programadores são incentivados a usar os recursos de interoperabilidade C++ em vez disso porque P/Invoke fornece relatórios de erro em time de compilar pouco, não é fortemente tipado e pode ser entediante implementar.Se a API não gerenciada é empacotada sistema autônomo uma DLL e o código-fonte não está disponível, P/Invoke é a única opção.Caso contrário, consulte os tópicos a seguir:

Por padrão, nativo e estruturas gerenciadas são dispostas diferente na memória, passar estruturas tão com êxito por limite gerenciado/requer etapas adicionais para preservar a integridade dos dados.

Este tópico explica as etapas necessárias para definir gerenciado equivalentes de nativo estruturas e como as estruturas resultantes podem ser passadas para funções não gerenciadas.Este tópico pressupõe que simples estruturas — aqueles que não contêm seqüências de caracteres ou ponteiros — são usados.Para obter informações sobre interoperabilidade não blittable, consulte Usando a interoperabilidade de C++ (PInvoke implícita).

Simples de marshaling, estruturas blittable por limite gerenciado/primeiro requer que gerenciados versões de cada nativo estrutura ser definida.Essas estruturas podem ter qualquer nome legal; não há nenhuma relação entre o nativo e gerenciado versão das duas estruturas de seu layout de dados.Portanto, é vital para a versão do gerenciado contém campos que são do mesmo dimensionar e na mesma ordem sistema autônomo o nativo versão.(Não há nenhum mecanismo para garantir que o gerenciado e nativo versões da estrutura são equivalentes, portanto, incompatibilidades não tornarão aparentes até o time de execução.(É responsabilidade do programador para garantir que as duas estruturas tenham o mesmo layout de dados.)

Porque os membros do gerenciado estruturas são reorganizadas, às vezes, para fins de desempenho, é necessário usar o StructLayoutAttribute atributo para indicar a estrutura são dispostos em seqüência. Também é uma mercadoria idéia conjunto explicitamente a estrutura de configuração para ser o mesmo de remessa sistema autônomo que usados pela estrutura nativa.(Embora, por padrão, o Visual C++ usa uma estrutura de 8 byte de remessa para os dois código gerenciado.)

  1. Em seguida, use DllImportAttribute para declarar os pontos de entrada que correspondem a qualquer un gerenciado funções que aceitam a estrutura, mas usam o gerenciado versão da estrutura das assinaturas de função, que é um controverso se você usar o mesmo nome para as duas versões da estrutura.

  2. Agora o código gerenciado pode passar a versão gerenciada da estrutura para funções não gerenciadas sistema autônomo se estivessem funções gerenciadas, na verdade.Essas estruturas podem ser passadas por valor ou por referência, sistema autônomo demonstrado no exemplo a seguir.

Exemplo

O código a seguir consiste em um não-gerenciados e um módulo gerenciado.O módulo não gerenciado é uma DLL que define uma estrutura chamada local e uma função chamada GetDistance aceita duas instâncias da estrutura local.O segundo módulo é um aplicativo de linha de comando gerenciado que importa a função GetDistance, mas define em termos de um equivalente gerenciado da estrutura de local, MLocation.Na prática o mesmo nome provavelmente é usado para as duas versões da estrutura; no entanto, um nome diferente é usado aqui para demonstrar que o protótipo DllImport é definido em termos de versão gerenciada.

O módulo gerenciado é compilado com/CLR, mas com/CLR: pura funciona bem.

Observe que nenhuma parte da DLL seja exposta a código gerenciado usando o # tradicional diretiva include.Na verdade, a DLL é acessada no time de execução somente, para que problemas com funções importados com DllImport não serão detectados no momento da compilar.

// TraditionalDll3.cpp
// compile with: /LD /EHsc
#include <iostream>
#include <stdio.h>
#include <math.h>

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

#pragma pack(push, 8)
struct Location {
   int x;
   int y;
};
#pragma pack(pop)

extern "C" {
   TRADITIONALDLL_API double GetDistance(Location, Location);
   TRADITIONALDLL_API void InitLocation(Location*);
}

double GetDistance(Location loc1, Location loc2) {
   printf_s("[unmanaged] loc1(%d,%d)", loc1.x, loc1.y);
   printf_s(" loc2(%d,%d)\n", loc2.x, loc2.y);

   double h = loc1.x - loc2.x;
   double v = loc1.y = loc2.y;
   double dist = sqrt( pow(h,2) + pow(v,2) );

   return dist;
}

void InitLocation(Location* lp) {
   printf_s("[unmanaged] Initializing location...\n");
   lp->x = 50;
   lp->y = 50;
}

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

[StructLayout(LayoutKind::Sequential, Pack=8)]
value struct MLocation {
   int x;
   int y;
};

value struct TraditionalDLL {
   [DllImport("TraditionalDLL3.dll")]
   static public double GetDistance(MLocation, MLocation);
   [DllImport("TraditionalDLL3.dll")]
   static public double InitLocation(MLocation*);
};

int main() {
   MLocation loc1;
   loc1.x = 0;
   loc1.y = 0;

   MLocation loc2;
   loc2.x = 100;
   loc2.y = 100;

   double dist = TraditionalDLL::GetDistance(loc1, loc2);
   Console::WriteLine("[managed] distance = {0}", dist);

   MLocation loc3;
   TraditionalDLL::InitLocation(&loc3);
   Console::WriteLine("[managed] x={0} y={1}", loc3.x, loc3.y);
}

[unmanaged] loc1(0,0) loc2(100,100) [managed] distance = 141.42135623731 [unmanaged] Initializing location... [managed] x=50 y=50

Consulte também

Outros recursos

Usar PInvoke Explicit no C++ (atributo DllImport)