Compartilhar via


Chamar funções nativas do código gerenciado

O common language runtime fornece serviços de invocação de plataforma, ou PInvoke, que permite que o código gerenciado para chamar funções ctype de estilo em bibliotecas dinâmico vinculadas nativo (DLLs).Mesmo empacotamento de dados é usado para quanto à interoperabilidade COM com o tempo de execução e funciona apenas para “,” ou IJW, mecanismo.

Para obter mais informações, consulte

Os exemplos nesta seção apenas ilustram como PInvoke pode ser usado.PInvoke pode simplificar os dados personalizados empacotamento porque você fornece informações de empacotamento declarativamente em atributos em vez de escrever o código procedural empacotamento.

ObservaçãoObservação

A biblioteca de empacotamento fornece uma maneira alternativa para dados de empacotar entre o nativo e ambientes gerenciados em uma forma otimizada.Consulte Visão geral do empacotamento em C++ para obter mais informações sobre empacotamento da biblioteca.A biblioteca de empacotamento é útil para dados somente, e não para funções.

PInvoke e o atributo DllImport

O exemplo a seguir mostra o uso de PInvoke em um programa em Visual C++.A função nativo coloca é definida em msvcrt.dll.O DllImport que o atributo é usado para a declaração de coloca.

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

[DllImport("msvcrt", CharSet=CharSet::Ansi)]
extern "C" int puts(String ^);

int main() {
   String ^ pStr = "Hello World!";
   puts(pStr);
}

O exemplo a seguir é equivalente ao exemplo anterior, mas para usa IJW.

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

#include <stdio.h>

int main() {
   String ^ pStr = "Hello World!";
   char* pChars = (char*)Marshal::StringToHGlobalAnsi(pStr).ToPointer(); 
   puts(pChars);
   
   Marshal::FreeHGlobal((IntPtr)pChars);
}

ms235282.collapse_all(pt-br,VS.110).gifVantagens de IJW

  • Não é necessário escrever a declarações de atributos de DLLImport para APIs não gerenciado o programa.Inclui apenas o arquivo e o link de cabeçalho com a biblioteca de importação.

  • O mecanismo de IJW é ligeiramente mais rápido (por exemplo, os modelos de IJW não precisam para verificar que a necessidade fixe ou copiar itens de dados porque isso é feito explicitamente pelo desenvolvedor).

  • Ilustra claramente problemas de desempenho.Nesse caso, o fato que você estiver convertendo de uma cadeia de caracteres Unicode para uma cadeia de caracteres ANSI e que você tenha uma alocação de memória e uma desalocação assistentes.Nesse caso, um desenvolvedor que escrever código que usa IJW realizaria que chamar _putws e usar PtrToStringChars é melhor para desempenho.

  • Se você chamar APIs muitos não gerenciado usando os mesmos dados, o empacotamento uma vez e passar a cópia é empacotada é muito mais eficiente do que a organização de cada vez.

ms235282.collapse_all(pt-br,VS.110).gifDesvantagens de IJW

  • Empacotamento deve ser especificado explicitamente no código em vez pelos atributos (que geralmente têm opções apropriadas.)

  • O código empacotamento é embutido, onde é mais invasor no fluxo da lógica do aplicativo.

  • Como APIs de empacotamento explícito retornam tipos de IntPtr de 32 bits para a portabilidade de 64 bits, você deve usar chamadas de ToPointer extras.

O método específico expostos pelo método C++ é mais eficiente, mais explícito, sob pena de alguma complexidade adicionais.

Se o aplicativo usa especialmente tipos de dados não gerenciado ou se chama um APIs mais não gerenciado que APIs do .NET Framework, é recomendável usar o recurso de IJW.Para chamar a API não gerenciado casual em um aplicativo basicamente gerenciado, a opção é mais sutil.

PInvoke com APIs do Windows

PInvoke é conveniente para funções de chamada no Windows.

Nesse exemplo, um programa em Visual C++ interopera com a função de MessageBox que é parte da API do Win32.

// platform_invocation_services_4.cpp
// compile with: /clr /c
using namespace System;
using namespace System::Runtime::InteropServices;
typedef void* HWND;
[DllImport("user32", CharSet=CharSet::Ansi)]
extern "C" int MessageBox(HWND hWnd, String ^ pText, String ^ pCaption, unsigned int uType);

int main() {
   String ^ pText = "Hello World! ";
   String ^ pCaption = "PInvoke Test";
   MessageBox(0, pText, pCaption, 0);
}

A saída é um caixa de mensagem que tenha o teste de PInvoke de título e contém o!texto hello.

Informações de empacotamento também é usada por PInvoke para pesquisar funções no DLL.No user32.dll na verdade não há nenhuma função de MessageBox, mas CharSet=CharSet::Ansi permite PInvoke para usar MessageBoxA, a versão ANSI, em vez de MessageBoxW, que é a versão Unicode.Geralmente, é recomendável usar versões Unicode de APIs não gerenciado porque isso elimina a sobrecarga de tradução de formato nativo Unicode de objetos de cadeia de caracteres do .NET Framework para ANSI.

Quando para não usar PInvoke

Usar PInvoke não é apropriado para todas as funções ctype de estilo em dlls.Por exemplo, suponha que há uma função MakeSpecial em mylib.dll declarado como segue:

char * MakeSpecial(char * pszString);

Se usamos PInvoke em um aplicativo Visual C++, nós pudemos escrever algo semelhante ao seguinte:

[DllImport("mylib")]

extern "C" String * MakeSpecial([MarshalAs(UnmanagedType::LPStr)] String ^);

A dificuldade aqui é que nós podemos não excluir a memória para a cadeia de caracteres retornada por MakeSpecial não gerenciado.Outras funções chamadas através de PInvoke retornam um ponteiro para um buffer interno que não tem que ser desalocado pelo usuário.Nesse caso, usar o recurso de IJW é a opção óbvia.

Limitações de PInvoke

Você não pode retornar o mesmo ponteiro exato de uma função nativo que você utiliza um como um parâmetro.Se uma função nativo retorna o ponteiro que são empacotados a ela por PInvoke, danos e as exceções de memória podem seguir.

__declspec(dllexport)
char* fstringA(char* param) {
   return param;
}

O exemplo seguinte exibe este problema, e mesmo que o programa pode parecer dar saída corretas, a saída são provenientes de memória que tenha sido liberado.

// platform_invocation_services_5.cpp
// compile with: /clr /c
using namespace System;
using namespace System::Runtime::InteropServices;
#include <limits.h>

ref struct MyPInvokeWrap {
public:
   [ DllImport("user32.dll", EntryPoint = "CharLower", CharSet = CharSet::Ansi) ]
   static String^ CharLower([In, Out] String ^);
};

int main() {
   String ^ strout = "AabCc";
   Console::WriteLine(strout);
   strout = MyPInvokeWrap::CharLower(strout);
   Console::WriteLine(strout);
}

Argumentos de empacotamento

Com PInvoke, nenhum empacotamento é necessário entre códigos gerenciados e tipos primitivos nativos C++ com o mesmo formulário.Por exemplo, nenhum empacotamento é necessário entre Int32 e int, ou entre o tipo double e o double.

No entanto, você deve os tipos de empacotar que não têm o mesmo formulário.Isso inclui o caractere, a cadeia de caracteres, e os tipos de estrutura.A tabela a seguir mostra os mapeamentos usados pelo empacotador para vários tipos:

wtypes.h

Visual C++

Visual C++ com /clr

Common language runtime

ALÇA

vácuo *

vácuo *

IntPtr, UIntPtr

BYTE

gráfico não assinado

gráfico não assinado

Byte

CURTO

short

short

Int16

PALAVRA

unsigned short

unsigned short

UInt16

INT

int

int

Int32

UINT

unsigned int

unsigned int

UInt32

POR MUITO TEMPO

long

long

Int32

BOOL

long

bool

Booleano

Dword

unsigned long

unsigned long

UInt32

ULONG

unsigned long

unsigned long

UInt32

CHAR

char

char

Char

LPCSTR

char *

String ^ [in], StringBuilder ^ [in, out]

String ^ [in], StringBuilder ^ [in, out]

LPCSTR

char const *

^ De cadeia de caracteres

Cadeia de caracteres

LPWSTR

wchar_t *

String ^ [in], StringBuilder ^ [in, out]

String ^ [in], StringBuilder ^ [in, out]

LPCWSTR

wchar_t const *

^ De cadeia de caracteres

Cadeia de caracteres

FLUTUAR

float

float

Single

DUPLO

double

double

Double

O marshaler fixa automaticamente a memória alocada no heap de tempo de execução se seu endereço é passado para uma função não gerenciado.Fixar-se impede que o coletor de lixo mova o bloco de atributo de memória durante a consolidação.

No exemplo mostrado anteriormente neste tópico, o parâmetro de CharSet DllImport especifica como as cadeias de caracteres gerenciados devem ser empacotado; nesse caso, devem ser empacotado em cadeias de caracteres ANSI para o lado nativo.

Você pode especificar informações empacotamento para argumentos individuais de uma função nativo usando o atributo MarshalAs.Há várias opções para empacotamento uma cadeia de caracteres * argumento: BStr, ANSIBStr, TBStr, LPStr, LPWStr, e LPTStr.O padrão é LPStr.

Nesse exemplo, a cadeia de caracteres é empacotada como uma cadeia de caracteres Unicode de dois bytes, LPWStr.A saída é a primeira letra de hello world! porque o segundo bytes de cadeia de caracteres é empacotada é nulo, e coloque-os interpretam isso como o marcador final da cadeia de caracteres.

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

[DllImport("msvcrt", EntryPoint="puts")]
extern "C" int puts([MarshalAs(UnmanagedType::LPWStr)] String ^);

int main() {
   String ^ pStr = "Hello World!";
   puts(pStr);
}

O atributo MarshalAs está no namespace de System::Runtime::InteropServices.O atributo pode ser usado com outros tipos de dados como matrizes.

Como mencionado anteriormente no tópico, a biblioteca de empacotamento fornece um novo método, otimizado de empacotamento de dados entre o nativo e ambientes gerenciados.Para obter mais informações, consulte Visão geral do empacotamento em C++.

Considerações sobre desempenho

PInvoke tem uma sobrecarga entre 10 e 30 de instruções x86 pela chamada.Além desses custo fixos, empacotamento cria a sobrecarga adicional.Não há custos de empacotamento entre os tipos blittables que têm a mesma representação no código gerenciado e não gerenciado.Por exemplo, não há custos ao converter entre int e o Int32.

Para obter um melhor desempenho, tem menos chamadas de dados que PInvoke empacotar tantos quanto possível, em vez de mais chamadas empacotar o menos dados pela chamada.

Consulte também

Outros recursos

Nativa e.Interoperabilidade.