Pasar estructuras

Actualización: noviembre 2007

Muchas funciones no administradas esperan que el usuario pase, como parámetro de la función, miembros de estructuras (tipos definidos por el usuario en Visual Basic) o miembros de clases definidos en código administrado. Al pasar estructuras o clases al código no administrado mediante la invocación de plataforma, debe proporcionarse información adicional para mantener la distribución y alineación originales. En este tema se presenta el atributo StructLayoutAttribute, que se utiliza para definir tipos con formato. Para estructuras y clases administradas, se puede seleccionar entre varios comportamientos de distribución previsibles que proporciona la enumeración LayoutKind.

Un punto fundamental de los conceptos presentados en este tema es la importante diferencia que hay entre los tipos de estructura y clase. Las estructuras son tipos de valores y las clases son tipos de referencia. Las clases siempre proporcionan al menos un nivel de direccionamiento indirecto de memoria (un puntero a un valor). Esta diferencia es importante porque las funciones no administradas exigen a menudo direccionamiento indirecto, como muestran los prototipos de la primera columna en la tabla siguiente. Las declaraciones administradas de estructura y clase de las columnas restantes muestran el grado hasta el que se puede ajustar el nivel de direccionamiento indirecto en la declaración.

Prototipo no administrado

Declaración administrada:

ningún direccionamiento indirecto

struct MyStruct(…);

Declaración administrada:

uno nivel de direccionamiento indirecto

class MyStruct(…);

DoWork(MyStruct x);

No requiere ningún nivel de direccionamiento indirecto.

DoWork(ByVal x As MyStruct)

No agrega ningún nivel de direccionamiento indirecto.

No es posible porque ya hay un nivel de direccionamiento indirecto.

DoWork(MyStruct* x);

Requiere un nivel de direccionamiento indirecto.

DoWork(ByRef x As MyStruct)

Agrega un nivel de direccionamiento indirecto.

DoWork(ByVal x As MyStruct)

No agrega ningún nivel de direccionamiento indirecto.

DoWork(MyStruct** x);

Requiere dos niveles de direccionamiento indirecto.

No es posible porque no se puede utilizar ByRef ByRef.

DoWork(ByRef x As MyStruct)

Agrega un nivel de direccionamiento indirecto.

La tabla describe las siguientes directrices para las declaraciones de invocación de plataforma:

  • Utilice una estructura pasada por valor cuando la función no administrada no requiera direccionamiento indirecto.

  • Utilice una estructura pasada por referencia o una clase pasada por valor cuando la función no administrada requiera un nivel de direccionamiento indirecto.

  • Utilice una clase pasada por referencia cuando la función no administrada requiera dos niveles de direccionamiento indirecto.

Declarar y pasar estructuras

En el siguiente ejemplo se muestra la forma de definir las estructuras Point y Rect en código administrado y la forma de pasar los tipos como parámetros a la función PtInRect en el archivo User32.dll. PtInRect tiene el siguiente prototipo no administrado:

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

Tenga en cuenta que debe pasar la estructura Rect por referencia, ya que la función espera un puntero que seleccione el 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);
}

Declarar y pasar clases

Los miembros de una clase pueden pasarse a una función no administrada de un archivo DLL, siempre que la clase tenga una distribución de miembro fija. En el ejemplo siguiente se muestra la forma de pasar miembros de la clase MySystemTime, que están definidos en orden secuencial, a GetSystemTime en el archivo User32.dll. GetSystemTime tiene el siguiente prototipo no administrado:

void GetSystemTime(SYSTEMTIME* SystemTime);

A diferencia de los tipos de valor, las clases siempre tienen al menos un nivel de direccionamiento indirecto.

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 Integer, _
        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(0, 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);
}

Vea también

Referencia

Clase StructLayoutAttribute

Clase StructLayoutAttribute

Clase FieldOffsetAttribute

Otros recursos

Llamar a una función DLL