Compartilhar via


Estruturas de passagem

Muitas funções não gerenciadas esperam que você passe, como um parâmetro para a função, membros de estruturas (tipos definidos pelo usuário em Visual Basic) ou membros de classes definidas no código gerenciado. Quando as estruturas de passagem ou classes para código não gerenciado usando a plataforma chamar, você deve fornecer informações adicionais para preservar o layout original e o alinhamento. Este tópico apresenta o StructLayoutAttribute atributo, que você usa para definir tipos formatado. Para classes e estruturas gerenciadas, você pode selecionar vários comportamentos de layout previsível fornecidos pelo LayoutKind enumeração.

Central para os conceitos apresentados neste tópico é uma diferença importante entre tipos de estrutura e a classe. Estruturas são tipos de valor e classes são tipos de referência — classes sempre fornecem pelo menos um nível de indireção de memória (um ponteiro para um valor). Essa diferença é importante porque as funções não gerenciadas geralmente demandam indireto, conforme mostrado pelas assinaturas na primeira coluna da tabela a seguir. As declarações de classe em que as colunas restantes e a estrutura gerenciada mostram o grau ao qual você pode ajustar o nível de indireção na sua declaração.

Assinatura não gerenciada

Declaração gerenciada:

Nenhum caminho indireto

struct MyStruct(…);

Declaração gerenciada:

um nível de indireção

class MyStruct(…);

DoWork(MyStruct x);

Demandas de níveis de indireção zero.

DoWork(ByVal x As MyStruct)

Adiciona o zero níveis de indireção.

Não é possível porque já existe um nível de indireção.

DoWork(MyStruct* x);

Exige um nível de indireção.

DoWork(ByRef x As MyStruct)

Adiciona um nível de indireção.

DoWork(ByVal x As MyStruct)

Adiciona o zero níveis de indireção.

DoWork(MyStruct** x);

Exige dois níveis de indireção.

Não é possível porque ByRef ByRef não pode ser usado.

DoWork(ByRef x As MyStruct)

Adiciona um nível de indireção.

A tabela descreve as diretrizes a seguir para declarações de invocação de plataforma:

  • Use uma estrutura passada por valor quando a função não gerenciada não exige nenhum indireção.

  • Use uma estrutura passados por referência ou uma classe passado por valor quando a função não gerenciada exige um nível de indireção.

  • Use uma classe passada por referência, quando a função não gerenciada exige dois níveis de indireção.

Declarando e passar estruturas

O exemplo a seguir mostra como definir o Point e Rect estruturas no código gerenciado e passar os tipos como parâmetro para o PtInRect a função no arquivo User32. dll. PtInRect tem a seguinte assinatura não gerenciada:

BOOL PtInRect(const RECT *lprc, POINT pt);

Observe que você deve passar a estrutura Rect por referência, desde que a função espera um ponteiro para um tipo RECT.

Imports System.Runtime.InteropServices

<StructLayout(LayoutKind.Sequential)> Public Structure Point
    Public x As Integer
    Public y As Integer
End Structure

Public Structure <StructLayout(LayoutKind.Explicit)> Rect
    <FieldOffset(0)> Public left As Integer
    <FieldOffset(4)> Public top As Integer
    <FieldOffset(8)> Public right As Integer
    <FieldOffset(12)> Public bottom As Integer
End Structure

Class Win32API    
    Declare Auto Function PtInRect Lib "user32.dll" _
    (ByRef r As Rect, p As Point) As Boolean
End Class
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct Point {
    public int x;
    public int y;
}   

[StructLayout(LayoutKind.Explicit)]
public struct Rect {
    [FieldOffset(0)] public int left;
    [FieldOffset(4)] public int top;
    [FieldOffset(8)] public int right;
    [FieldOffset(12)] public int bottom;
}   

class Win32API {
    [DllImport("User32.dll")]
    public static extern bool PtInRect(ref Rect r, Point p);
}

Declarando e passando de Classes

Você pode passar a membros de uma classe para uma função DLL não gerenciada, como a classe tem um layout fixo do membro. O exemplo a seguir demonstra como passar os membros do MySystemTime classe, que são definidos em ordem seqüencial, para o GetSystemTime no arquivo User32. dll. GetSystemTime tem a seguinte assinatura não gerenciada:

void GetSystemTime(SYSTEMTIME* SystemTime);

Ao contrário dos tipos de valor, classes sempre tem pelo menos um nível de indireção.

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic

<StructLayout(LayoutKind.Sequential)> Public Class MySystemTime
    Public wYear As Short
    Public wMonth As Short
    Public wDayOfWeek As Short 
    Public wDay As Short
    Public wHour As Short
    Public wMinute As Short
    Public wSecond As Short
    Public wMiliseconds As Short
End Class

Public Class Win32
    Declare Auto Sub GetSystemTime Lib "Kernel32.dll"(sysTime _
        As MySystemTime)
    Declare Auto Function MessageBox Lib "User32.dll"(hWnd As IntPtr, _
        txt As String, caption As String, Typ As Integer) As Integer
End Class

Public Class TestPlatformInvoke    
    Public Shared Sub Main()
        Dim sysTime As New MySystemTime()
        Win32.GetSystemTime(sysTime)

        Dim dt As String
        dt = "System time is:" & ControlChars.CrLf & _
              "Year: " & sysTime.wYear & _
              ControlChars.CrLf & "Month: " & sysTime.wMonth & _
              ControlChars.CrLf & "DayOfWeek: " & sysTime.wDayOfWeek & _
              ControlChars.CrLf & "Day: " & sysTime.wDay
        Win32.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0)      
    End Sub
End Class
[StructLayout(LayoutKind.Sequential)]
public class MySystemTime {
    public ushort wYear; 
    public ushort wMonth;
    public ushort wDayOfWeek; 
    public ushort wDay; 
    public ushort wHour; 
    public ushort wMinute; 
    public ushort wSecond; 
    public ushort wMilliseconds; 
}
class Win32API {
    [DllImport("Kernel32.dll")]
    public static extern void GetSystemTime(MySystemTime st);

    [DllImport("user32.dll", CharSet=CharSet.Auto)]
     public static extern int MessageBox(IntPtr hWnd,
         string text, string caption, int options);
}

public class TestPlatformInvoke
{
    public static void Main()
    {
        MySystemTime sysTime = new MySystemTime();
        Win32API.GetSystemTime(sysTime);

        string dt;
        dt = "System time is: \n" +
              "Year: " + sysTime.wYear + "\n" +
              "Month: " + sysTime.wMonth + "\n" +
              "DayOfWeek: " + sysTime.wDayOfWeek + "\n" +
              "Day: " + sysTime.wDay;
        Win32API.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0);
    }
}

Consulte também

Referência

Classe de StructLayoutAttribute

Classe de StructLayoutAttribute

Classe de FieldOffsetAttribute

Outros recursos

Chamando uma função DLL