Compartilhar via


Criando protótipos em 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 de método no código gerenciado. Para obter exemplos que demonstram como construir declarações baseadas no .NET a serem usadas com a invocação de plataforma, consulte Realizar marshaling de dados com a invocação de plataforma.

Antes de poder acessar uma função de DLL não gerenciada no 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 que você libere a cadeia de caracteres usando um método como LocalFree. A invocação de plataforma manipula esses parâmetros de maneira diferente. Para chamadas de invocação de plataforma, torne o parâmetro um tipo IntPtr, em vez de um tipo String. Use métodos que são fornecidos pela classe System.Runtime.InteropServices.Marshal para converter o tipo em uma cadeia de caracteres manualmente e libere-a manualmente.

Noções básicas sobre a declaração

As definições gerenciadas para funções não gerenciadas são dependentes de idioma, como é possível 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, use 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

Independentemente de 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. Altere esse comportamento padrão ajustando os valores de um ou mais campos. Em muitos casos, use o DllImportAttribute para definir um valor.

A tabela a seguir lista o conjunto completo de campos de atributo que pertencem à invocação de 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 ao passar argumentos de método. O padrão é WinAPI, que corresponde a __stdcall nas plataformas 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 de DLL a ser chamado.
ExactSpelling Controla se um ponto de entrada deve ser modificado para que ele corresponda ao conjunto de caracteres. O valor padrão varia de acordo com a linguagem de programação.
PreserveSig Controla se a assinatura de método gerenciada deve ser transformada em uma assinatura não gerenciada que retorna um HRESULT e que tem um argumento adicional [out, retval] para o valor retornado.

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

Para obter informações detalhadas sobre a referência, 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 da invocação de plataforma e em instruções COM da linguagem IDL.

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 modificador Demand no exemplo a seguir é aceito.

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

Os modificadores SecurityAction funcionam corretamente se são colocados em uma classe que contém (encapsula) a chamada da invocação de 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 modificador Demand não é aceito em cenários de declaração da 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();  
}  

Confira também