Compartilhar via


Como: estruturas de empacotamento usando PInvoke

Este documento explica como nativas funções que aceitam seqüências de caracteres de estilo c podem ser chamadas de funções gerenciadas que fornecem uma instância de String , usando P/Invoke.Embora nós recomendamos que você use os recursos de interoperabilidade de C++ em vez de P/Invoke porque P/Invoke fornece pouco tempo de compilação relatório de erro, não é tipo seguro e pode ser entediante implementar, se a API não gerenciada é empacotada como uma DLL e o código-fonte não está disponível, P/Invoke é a única opção.Caso contrário, consulte os seguintes documentos:

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

Este documento explica as etapas necessárias para definir gerenciados equivalentes de estruturas nativas e como estruturas resultantes podem ser passadas para funções não gerenciadas.Este documento presume que simples estruturas — aqueles que não contêm seqüências de caracteres ou ponteiros — são usados.Para obter informações sobre interoperabilidade de não-blittable, consulte Usando interoperabilidade C++ (PInvoke implícita).P/Invoke não pode ter tipos blittable não como um valor de retorno.Tipos blittable têm a mesma representação no código gerenciado e não gerenciado.Para mais informações, consulte Blittable e tipos não-Blittable.

Empacotamento simples, blittable estruturas limite gerenciado/primeiro requer gerenciadas versões de cada estrutura nativa ser definido.Essas estruturas podem ter qualquer nome legal; Não há nenhuma relação entre a versão nativa e gerenciada de duas estruturas diferentes de seu layout de dados.Portanto, é vital que a versão gerenciada contém campos que são do mesmo tamanho e na mesma ordem como a versão nativa.(Não há nenhum mecanismo para garantir que as versões gerenciadas e nativas da estrutura são equivalentes, portanto incompatibilidades não se tornará aparentes até o tempo de execução.É responsabilidade do programador para garantir que as duas estruturas tenham o mesmo layout de dados.)

Porque os membros do estruturas gerenciados às vezes são reorganizados para fins de desempenho, é necessário usar o StructLayoutAttribute atributo para indicar a estrutura são dispostos seqüencialmente.Também é uma boa idéia definir explicitamente a estrutura de configuração para ser o mesmo usado pela estrutura nativa.(Embora, por padrão, Visual C++ usa uma estrutura de 8 bytes para ambos os código gerenciado).

  1. Em seguida, use DllImportAttribute para declarar os pontos de entrada que correspondem a quaisquer funções não gerenciadas que aceitam a estrutura, mas usar a versão gerenciada da estrutura de assinaturas de função, que é um tema controverso se você usar o mesmo nome para as duas versões da estrutura.

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

Exemplo

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

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

Observe que nenhuma parte da DLL é exposto para código gerenciado usando # tradicional incluir diretiva.Na verdade, a DLL é acessada no tempo de execução somente para que problemas com funções importados com DllImport não serão detectados em tempo de compilação.

// 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);
}
  
  

Consulte também

Outros recursos

Usando PInvoke Explicit no C++ (atributo DllImport)