Passaggio di strutturePassing Structures

Molte funzioni non gestite prevedono il passaggio, come parametro della funzione, di membri di strutture (tipi definiti dall'utente in Visual Basic) o membri di classi definiti nel codice gestito.Many unmanaged functions expect you to pass, as a parameter to the function, members of structures (user-defined types in Visual Basic) or members of classes that are defined in managed code. Quando si passano strutture o classi al codice non gestito mediante platform invoke, è necessario fornire informazioni aggiuntive per mantenere il layout e l'allineamento originali.When passing structures or classes to unmanaged code using platform invoke, you must provide additional information to preserve the original layout and alignment. In questo argomento viene presentato l'attributo StructLayoutAttribute, usato per definire i tipi formattati.This topic introduces the StructLayoutAttribute attribute, which you use to define formatted types. Per le classi e le strutture gestite, è possibile selezionare tra diversi comportamenti di layout prevedibili forniti dall'enumerazione LayoutKind.For managed structures and classes, you can select from several predictable layout behaviors supplied by the LayoutKind enumeration.

Un aspetto fondamentale per i concetti presentati in questo argomento è l'importante differenza esistente tra i tipi di strutture e classi.Central to the concepts presented in this topic is an important difference between structure and class types. Le strutture sono tipi valore e le classi sono tipi riferimento. Le classi forniscono sempre almeno un livello di riferimento indiretto di memoria (un puntatore a un valore).Structures are value types and classes are reference types — classes always provide at least one level of memory indirection (a pointer to a value). Questa differenza è importante perché le funzioni non gestite spesso richiedono riferimenti indiretti, come illustrato dalle firme nella prima colonna della tabella seguente.This difference is important because unmanaged functions often demand indirection, as shown by the signatures in the first column of the following table. La struttura gestita e le dichiarazioni di classe nelle colonne rimanenti mostrano il grado di cui è possibile modificare il livello di riferimento indiretto nella dichiarazione. Le dichiarazioni sono disponibili sia per Visual Basic che per Visual C#.The managed structure and class declarations in the remaining columns show the degree to which you can adjust the level of indirection in your declaration.Declarations are provided for both Visual Basic and Visual C#.

Firma non gestitaUnmanaged signature Dichiarazione gestita:Managed declaration:
nessun riferimento indirettono indirection
Structure MyType
struct MyType;
Dichiarazione gestita:Managed declaration:
un livello di riferimento indirettoone level of indirection
Class MyType
class MyType;
DoWork(MyType x);

Richiede zero livelli di riferimento indiretto.Demands zero levels of indirection.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Non aggiunge alcun livello di riferimento indiretto.Adds zero levels of indirection.
Non possibile perché esiste già un livello di riferimento indiretto.Not possible because there is already one level of indirection.
DoWork(MyType* x);

Richiede un livello di riferimento indiretto.Demands one level of indirection.
DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Aggiunge un livello di riferimento indiretto.Adds one level of indirection.
DoWork(ByVal x As MyType)
DoWork(MyType x)

Non aggiunge alcun livello di riferimento indiretto.Adds zero levels of indirection.
DoWork(MyType** x);

Richiede due livelli di riferimento indiretto.Demands two levels of indirection.
Impossibile perché non è possibile usare ByRef ByRef o ref ref.Not possible because ByRef ByRef or ref ref cannot be used. DoWork(ByRef x As MyType)
DoWork(ref MyType x)

Aggiunge un livello di riferimento indiretto.Adds one level of indirection.

La tabella descrive le linee guida seguenti per le dichiarazioni di platform invoke:The table describes the following guidelines for platform invoke declarations:

  • Usare una struttura passata per valore quando la funzione non gestita non richiede alcun riferimento indiretto.Use a structure passed by value when the unmanaged function demands no indirection.

  • Usare una struttura passata per riferimento o una classe passata per valore quando la funzione non gestita richiede un livello di riferimento indiretto.Use either a structure passed by reference or a class passed by value when the unmanaged function demands one level of indirection.

  • Usare una classe passata per riferimento quando la funzione non gestita richiede due livelli di riferimento indiretto.Use a class passed by reference when the unmanaged function demands two levels of indirection.

Dichiarazione e passaggio di struttureDeclaring and Passing Structures

L'esempio seguente mostra come definire le strutture Point e Rect nel codice gestito e passare i tipi come parametro alla funzione PtInRect nel file User32.dll.The following example shows how to define the Point and Rect structures in managed code, and pass the types as parameter to the PtInRect function in the User32.dll file. PtInRect include la firma non gestita seguente:PtInRect has the following unmanaged signature:

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

Si noti che è necessario passare la struttura Rect per riferimento, perché la funzione si aspetta un puntatore a un tipo RECT.Notice that you must pass the Rect structure by reference, since the function expects a pointer to a RECT type.

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);  
}  

Dichiarazione e passaggio di classiDeclaring and Passing Classes

È possibile passare i membri di una classe a una funzione DLL non gestita, a condizione che la classe abbia un layout di membri fisso.You can pass members of a class to an unmanaged DLL function, as long as the class has a fixed member layout. L'esempio seguente dimostra come passare membri alla classe MySystemTime, definiti in ordine sequenziale, a GetSystemTime nel file User32.dll.The following example demonstrates how to pass members of the MySystemTime class, which are defined in sequential order, to the GetSystemTime in the User32.dll file. GetSystemTime include la firma non gestita seguente:GetSystemTime has the following unmanaged signature:

void GetSystemTime(SYSTEMTIME* SystemTime);  

Diversamente dai tipi valore, le classi hanno sempre almeno un livello di riferimento indiretto.Unlike value types, classes always have at least one level of indirection.

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);  
    }  
}  

Vedere ancheSee also