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 ByRef ByRef ou ref ref 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);
}
}