Partilhar 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 no Visual Basic) ou membros de classes que são definidas em código gerenciado. Ao passar estruturas ou classes para código não gerenciado usando invocar plataforma, você deve fornecer informações adicionais para preservar o layout e o alinhamento originais. Este tópico apresenta o atributo StructLayoutAttribute , que você usa para definir tipos formatados. Para estruturas e classes gerenciadas, você pode selecionar entre vários comportamentos de layout previsíveis fornecidos pela enumeração LayoutKind .

Central para os conceitos apresentados neste tópico é uma diferença importante entre estrutura e tipos de classe. Estruturas são tipos de valor e classes são tipos de referência — as 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 exigem indireção, como mostram as assinaturas na primeira coluna da tabela a seguir. A estrutura gerenciada e as declarações de classe nas colunas restantes mostram o grau em que você pode ajustar o nível de indireção em sua declaração. As declarações são fornecidas para Visual Basic e Visual C#.

Assinatura não gerenciada Declaração gerenciada:
sem indireção
Structure MyType
struct MyType;
Declaração gerenciada:
um nível de indireção
Class MyType
class MyType;
DoWork(MyType x);

Exige zero níveis de indireção.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Adiciona zero níveis de indireção.
Não é possível porque já existe um nível de indireção.
DoWork(MyType* x);

Exige um nível de indireção.
DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Adiciona um nível de indireção.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Adiciona zero níveis de indireção.
DoWork(MyType** x);

Exige dois níveis de indireção.
Não é possível porque ByRefByRef ou refref não pode ser usado. DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Adiciona um nível de indireção.

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

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

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

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

Declarando e Passando Estruturas

O exemplo a seguir mostra como definir as Point estruturas e Rect no código gerenciado e passar os tipos como parâmetro para a função PtInRect 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, já 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  
  
Friend Class NativeMethods
    Friend 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;  
}
  
internal static class NativeMethods
{  
    [DllImport("User32.dll")]  
    internal static extern bool PtInRect(ref Rect r, Point p);  
}  

Declarando e Passando em Classes

Você pode passar membros de uma classe para uma função DLL não gerenciada, desde que a classe tenha um layout de membro fixo. O exemplo a seguir demonstra como passar membros da MySystemTime classe, que são definidos em ordem sequencial, 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, as classes sempre têm pelo menos um nível de indireção.

Imports System.Runtime.InteropServices  
  
<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  
  
Friend Class NativeMethods  
    Friend Declare Auto Sub GetSystemTime Lib "Kernel32.dll" (
        sysTime As MySystemTime)  
    Friend Declare Auto Function MessageBox Lib "User32.dll" (
        hWnd As IntPtr, lpText As String, lpCaption As String, uType As UInteger) As Integer  
End Class  
  
Public Class TestPlatformInvoke
    Public Shared Sub Main()  
        Dim sysTime As New MySystemTime()  
        NativeMethods.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  
        NativeMethods.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;
}  
internal static class NativeMethods
{  
    [DllImport("Kernel32.dll")]  
    internal static extern void GetSystemTime(MySystemTime st);  
  
    [DllImport("user32.dll", CharSet = CharSet.Auto)]  
    internal static extern int MessageBox(
        IntPtr hWnd, string lpText, string lpCaption, uint uType);  
}  
  
public class TestPlatformInvoke  
{  
    public static void Main()  
    {  
        MySystemTime sysTime = new MySystemTime();  
        NativeMethods.GetSystemTime(sysTime);  
  
        string dt;  
        dt = "System time is: \n" +  
              "Year: " + sysTime.wYear + "\n" +  
              "Month: " + sysTime.wMonth + "\n" +  
              "DayOfWeek: " + sysTime.wDayOfWeek + "\n" +  
              "Day: " + sysTime.wDay;  
        NativeMethods.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0);  
    }  
}  

Consulte também