Compartilhar via


Criando protótipos no código gerenciado

Este tópico descreve como acessar funções não gerenciadas e apresenta vários campos de atributo que anotam a definição do método no código gerenciado. Para exemplos que demonstram como construir declarações baseadas em .NET para serem usadas com a invocação de plataforma, consulte Marshalling Data with Platform Invoke.

Antes de acessar uma função DLL não gerenciada do código gerenciado, você precisa saber o nome da função e o nome da DLL que a exporta. Com essas informações, você pode começar a escrever a definição gerenciada para uma função não gerenciada que é implementada em uma DLL. Além disso, você pode ajustar a maneira como essa invocação de plataforma cria a função e realiza marshaling dos dados bidirecionalmente na função.

Observação

As funções de API do Windows que alocam uma cadeia de caracteres permitem liberar a cadeia de caracteres usando um método como LocalFree. A invocação de plataforma manipula esses parâmetros de forma diferente. Para chamadas de invocação da plataforma, transforme o parâmetro em um tipo IntPtr em vez de um tipo String. Use métodos fornecidos pela System.Runtime.InteropServices.Marshal classe para converter o tipo em uma cadeia de caracteres manualmente e liberá-lo manualmente.

Noções básicas de declaração

As definições gerenciadas para funções não gerenciadas dependem da linguagem, como você pode ver nos exemplos a seguir. Para obter exemplos de código mais completos, consulte Exemplos de Invocação de Plataforma.

Friend Class NativeMethods
    Friend Declare Auto Function MessageBox Lib "user32.dll" (
        ByVal hWnd As IntPtr,
        ByVal lpText As String,
        ByVal lpCaption As String,
        ByVal uType As UInteger) As Integer
End Class

Para aplicar os campos DllImportAttribute.BestFitMapping, DllImportAttribute.CallingConvention, DllImportAttribute.ExactSpelling, DllImportAttribute.PreserveSig, DllImportAttribute.SetLastError ou DllImportAttribute.ThrowOnUnmappableChar a uma declaração do Visual Basic, você deve usar o atributo DllImportAttribute em vez da instrução Declare.

Imports System.Runtime.InteropServices

Friend Class NativeMethods
    <DllImport("user32.dll", CharSet:=CharSet.Auto)>
    Friend Shared Function MessageBox(
        ByVal hWnd As IntPtr,
        ByVal lpText As String,
        ByVal lpCaption As String,
        ByVal uType As UInteger) As Integer
    End Function
End Class
using System;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("user32.dll")]
    internal static extern int MessageBox(
        IntPtr hWnd, string lpText, string lpCaption, uint uType);
}
using namespace System;
using namespace System::Runtime::InteropServices;

[DllImport("user32.dll")]
extern "C" int MessageBox(
    IntPtr hWnd, String* lpText, String* lpCaption, unsigned int uType);

Ajustando a definição

Se você defini-los explicitamente ou não, os campos de atributo estão trabalhando definindo o comportamento do código gerenciado. A invocação de plataforma funciona de acordo com os valores padrão definidos em vários campos que existem como metadados em um assembly. Você pode alterar esse comportamento padrão ajustando os valores de um ou mais campos. Em muitos casos, você usa o DllImportAttribute para definir um valor.

A tabela a seguir lista o conjunto completo de campos de atributo que pertencem à invocação da plataforma. Para cada campo, a tabela inclui o valor padrão e um link para informações sobre como usar esses campos para definir funções de DLL não gerenciadas.

Campo Descrição
BestFitMapping Habilita ou desabilita o mapeamento de melhor ajuste.
CallingConvention Especifica a convenção de chamada a ser usada na passagem de argumentos de método. O padrão é WinAPI, que corresponde a __stdcall para as plataformas baseadas em Intel de 32 bits.
CharSet Controla a desconfiguração de nome e a maneira como os argumentos de cadeia de caracteres devem ter o marshaling realizado para a função. O padrão é CharSet.Ansi.
EntryPoint Especifica o ponto de entrada da DLL a ser chamado.
ExactSpelling Controla se um ponto de entrada deve ser modificado para corresponder ao conjunto de caracteres. O valor padrão varia de acordo com a linguagem de programação.
PreserveSig Controla se a assinatura do método gerenciado deve ser transformada em uma assinatura não gerenciada que retorna um HRESULT e tem um argumento adicional [out, retval] para o valor retornado.

O padrão é true (a assinatura não deve ser transformada).
SetLastError Permite que o chamador use a Marshal.GetLastWin32Error função de API para determinar se ocorreu um erro durante a execução do método. No Visual Basic, o padrão é true; em C# e C++, o padrão é false.
ThrowOnUnmappableChar Controla a geração de uma exceção quando um caractere Unicode não mapeável é convertido em um caractere ANSI "?".

Para obter informações de referência detalhadas, consulte DllImportAttribute.

Considerações sobre segurança da invocação de plataforma

Os membros Assert, Deny e PermitOnly da enumeração SecurityAction são chamados de modificadores de movimentação de pilha. Esses membros serão ignorados se forem usados como atributos declarativos em declarações de invocação de plataforma e instruções IDL (Linguagem de Definição de Interface COM).

Exemplos de invocação de plataforma

As amostras de invocação de plataforma desta seção ilustram o uso do atributo RegistryPermission com os modificadores de movimentação de pilha.

No exemplo a seguir, os modificadores SecurityActionAssert, Deny e PermitOnly são ignorados.

[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
[RegistryPermission(SecurityAction.Assert, Unrestricted = true)]  
    private static extern bool CallRegistryPermissionAssert();  
  
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
[RegistryPermission(SecurityAction.Deny, Unrestricted = true)]  
    private static extern bool CallRegistryPermissionDeny();  
  
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
[RegistryPermission(SecurityAction.PermitOnly, Unrestricted = true)]  
    private static extern bool CallRegistryPermissionDeny();  

No entanto, o Demand modificador no exemplo a seguir é aceito.

[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
    private static extern bool CallRegistryPermissionDeny();  

SecurityAction os modificadores funcionarão corretamente se forem colocados em uma classe que contenha (encapsula) a chamada de invocação da plataforma.

      [RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
public ref class PInvokeWrapper  
{  
public:  
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
    private static extern bool CallRegistryPermissionDeny();  
};  
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
class PInvokeWrapper  
{  
[DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
    private static extern bool CallRegistryPermissionDeny();  
}  

Os modificadores SecurityAction também funcionam corretamente em um cenário aninhado em que são colocados no chamador da chamada de invocação de plataforma:

      {  
public ref class PInvokeWrapper  
public:  
    [DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
    private static extern bool CallRegistryPermissionDeny();  
  
    [RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
    public static bool CallRegistryPermission()  
    {  
     return CallRegistryPermissionInternal();  
    }  
};  
class PInvokeScenario  
{  
    [DllImport("MyClass.dll", EntryPoint = "CallRegistryPermission")]  
    private static extern bool CallRegistryPermissionInternal();  
  
    [RegistryPermission(SecurityAction.Assert, Unrestricted = true)]  
    public static bool CallRegistryPermission()  
    {  
     return CallRegistryPermissionInternal();  
    }  
}  

Exemplos de interoperabilidade COM

As amostras de interoperabilidade COM desta seção ilustram o uso do atributo RegistryPermission com os modificadores de movimentação de pilha.

As declarações da interface de interoperabilidade COM a seguir ignoram os modificadores Assert, Deny e PermitOnly, da mesma forma que os exemplos da invocação de plataforma da seção anterior.

[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]  
interface IAssertStubsItf  
{  
[RegistryPermission(SecurityAction.Assert, Unrestricted = true)]  
    bool CallRegistryPermission();  
[FileIOPermission(SecurityAction.Assert, Unrestricted = true)]  
    bool CallFileIoPermission();  
}  
  
[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]  
interface IDenyStubsItf  
{  
[RegistryPermission(SecurityAction.Deny, Unrestricted = true)]  
    bool CallRegistryPermission();  
[FileIOPermission(SecurityAction.Deny, Unrestricted = true)]  
    bool CallFileIoPermission();  
}  
  
[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]  
interface IAssertStubsItf  
{  
[RegistryPermission(SecurityAction.PermitOnly, Unrestricted = true)]  
    bool CallRegistryPermission();  
[FileIOPermission(SecurityAction.PermitOnly, Unrestricted = true)]  
    bool CallFileIoPermission();  
}  

Além disso, o Demand modificador não é aceito em cenários de declaração de interface de interoperabilidade COM, conforme mostrado no exemplo a seguir.

[ComImport, Guid("12345678-43E6-43c9-9A13-47F40B338DE0")]  
interface IDemandStubsItf  
{  
[RegistryPermission(SecurityAction.Demand, Unrestricted = true)]  
    bool CallRegistryPermission();  
[FileIOPermission(SecurityAction.Demand, Unrestricted = true)]  
    bool CallFileIoPermission();  
}  

Consulte também