Marshalling predefinito per i tipi di valore

Aggiornamento: novembre 2007

La maggior parte dei tipi valore, quali i valori integer e i numeri a virgola mobile, sono copiabili e non richiedono il marshalling. Altri tipi non copiabili hanno rappresentazioni diverse nella memoria gestita e non gestita e richiedono il marshalling. Per altri tipi ancora occorre una formattazione esplicita attraverso il limite di interoperabilità.

In questo argomento sono disponibili le informazioni indicate di seguito sui tipi valore:

  • Tipi di valore utilizzati nel richiamo piattaforma

  • Tipi di valore utilizzati nell'interoperabilità COM

Oltre alla descrizione dei tipi formattati, in questo argomento sono specificati i tipi di valore del sistema con un comportamento di marshalling insolito.

Un tipo formattato è un tipo complesso contenente informazioni che controllano in modo esplicito il layout dei membri in memoria. Le informazioni sul layout dei membri vengono fornite mediante l'attributo StructLayoutAttribute. Il layout può assumere uno dei seguenti valori di enumerazione LayoutKind:

  • LayoutKind.Automatic

    Indica che i membri del tipo possono essere riordinati in Common Language Runtime per ragioni di efficienza. Tuttavia, quando un tipo di valore viene passato al codice non gestito, il layout dei membri viene previsto. Il tentativo di eseguire il marshalling di una struttura di questo tipo provoca un'eccezione.

  • LayoutKind.Sequential

    Indica che i membri del tipo devono essere disposti nella memoria non gestita nello stesso ordine in cui appaiono nella definizione del tipo gestito.

  • LayoutKind.Explicit

    Indica che i membri sono disposti in base all'attributo FieldOffsetAttribute fornito con ogni campo.

Tipi di valore utilizzati nel richiamo piattaforma

Nell'esempio riportato di seguito i tipi Point e Rect forniscono informazioni sul layout dei membri mediante StructLayoutAttribute.

Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential)> Public Structure Point
   Public x As Integer
   Public y As Integer
End Structure
<StructLayout(LayoutKind.Explicit)> Public Structure 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
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;
}

Per questi tipi formattati il marshalling nel codice non gestito viene eseguito come strutture di tipo C. Si tratta di un modo semplice per chiamare un'API non gestita con argomenti di struttura. Le strutture POINT e RECT, ad esempio, possono essere passate alla funzione PtInRect dell'API Microsoft Win32 come illustrato di seguito:

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

È possibile passare strutture mediante la definizione di richiamo piattaforma riportata di seguito:

Class Win32API    
   Declare Auto Function PtInRect Lib "User32.dll" _
    (ByRef r As Rect, p As Point) As Boolean
End Class
class Win32API {
   [DllImport("User32.dll")]
   public static extern Bool PtInRect(ref Rect r, Point p);
}

Il tipo di valore Rect deve essere passato per riferimento, in quanto l'API non gestita prevede che alla funzione venga passato un puntatore a RECT. Il tipo di valore Point viene passato per valore, in quanto l'API non gestita prevede che POINT venga passato sullo stack. Questa sottile differenza è molto importante. I riferimenti vengono passati al codice non gestito come puntatori, mentre i valori vengono passati al codice non gestito sullo stack.

Nota:

In caso di marshalling di un tipo formattato come struttura, risultano accessibili solo i campi all'interno del tipo. Eventuali metodi, proprietà o eventi del tipo sono inaccessibili dal codice non gestito.

È anche possibile eseguire il marshalling delle classi nel codice non gestito come strutture di tipo C, purché abbiano un layout dei membri fisso. Le informazioni sul layout dei membri per una classe vengono anche fornite con l'attributo StructLayoutAttribute. La differenza principale tra i tipi di valore con layout fisso e le classi con lo stesso layout sta nel modo in cui viene eseguito il marshalling nel codice non gestito. I tipi di valore sono passati sullo stack per valore e, di conseguenza, le eventuali modifiche apportate ai membri del tipo dal chiamato non sono visibili al chiamante. Poiché invece i tipi di riferimento sono passati per riferimento (un riferimento al tipo viene passato sullo stack), tutte le modifiche apportate dal chiamato ai membri di tipo copiabile di un tipo sono visibili al chiamante.

Nota:

Se un tipo riferimento ha membri di tipi non copiabili, la conversione è necessaria due volte: la prima volta quando si passa un argomento al lato non gestito e la seconda volta in risposta alla chiamata. A causa di questo ulteriore sovraccarico, i parametri In/Out devono essere applicati in modo esplicito a un argomento se il chiamante desidera vedere le modifiche apportate dal chiamato.

Nell'esempio riportato di seguito, la classe SystemTime ha un layout dei membri sequenziale e può essere passata alla funzione GetSystemTime dell'API Win32.

<StructLayout(LayoutKind.Sequential)> Public Class SystemTime
   Public wYear As System.UInt16
   Public wMonth As System.UInt16
   Public wDayOfWeek As System.UInt16
   Public wDay As System.UInt16
   Public wHour As System.UInt16
   Public wMinute As System.UInt16
   Public wSecond As System.UInt16
   Public wMilliseconds As System.UInt16
End Class
[StructLayout(LayoutKind.Sequential)]
   public class SystemTime {
   public ushort wYear; 
   public ushort wMonth;
   public ushort wDayOfWeek; 
   public ushort wDay; 
   public ushort wHour; 
   public ushort wMinute; 
   public ushort wSecond; 
   public ushort wMilliseconds; 
}

La funzione GetSystemTime è definita come segue:

void GetSystemTime(SYSTEMTIME* SystemTime);

La definizione di richiamo piattaforma equivalente per GetSystemTime è la seguente:

Public Class Win32
   Declare Auto Sub GetSystemTime Lib "Kernel32.dll" (ByVal sysTime _
   As SystemTime)
End Class
class Win32API {
   [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
   public static extern void GetSystemTime(SystemTime st);
}

Si noti che l'argomento SystemTime non è tipizzato come argomento di riferimento, in quanto SystemTime è una classe, non un tipo di valore. A differenza dei tipi di valore, le classi vengono passate sempre per riferimento.

Nell'esempio di codice riportato di seguito viene mostrata una classe Point diversa che ha un metodo denominato SetXY. Poiché il tipo ha un layout sequenziale, è possibile passarlo al codice non gestito ed eseguirne il marshalling come struttura. Tuttavia, il membro SetXY non può essere chiamato dal codice non gestito, anche se l'oggetto è passato per riferimento.

<StructLayout(LayoutKind.Sequential)> Public Class Point
   Private x, y As Integer
   Public Sub SetXY(x As Integer, y As Integer)
      Me.x = x
      Me.y = y
   End Sub
End Class
[StructLayout(LayoutKind.Sequential)]
public class Point {
   int x, y;
   public void SetXY(int x, int y){ 
      this.x = x;
      this.y = y;
   }
}

Tipi di valore utilizzati nell'interoperabilità COM

I tipi formattati possono anche essere passati alle chiamate ai metodi di interoperabilità COM. In realtà, quando esportati in una libreria dei tipi, i tipi di valore vengono convertiti automaticamente in strutture. Come illustrato nell'esempio riportato di seguito, il tipo di valore Point diventa una definizione di tipo (typedef) denominata Point. Tutti i riferimenti al tipo di valore Point in un punto diverso della libreria dei tipi vengono sostituiti dalla definizione typedef Point.

Rappresentazione di libreria dei tipi

typedef struct tagPoint {
   int x;
   int y;
} Point;
interface _Graphics {
   …
   HRESULT SetPoint ([in] Point p)
   HRESULT SetPointRef ([in,out] Point *p)
   HRESULT GetPoint ([out,retval] Point *p)
}

Per il marshalling tramite le interfacce COM valgono le stesse regole del marshalling di valori e riferimenti nel richiamo piattaforma. Ad esempio, quando un'istanza del tipo di valore Point viene passata da .NET Framework a COM, Point viene passato per valore. Se il tipo di valore Point è passato per riferimento, un puntatore a Point viene passato sullo stack. Il gestore di marshalling di interoperabilità non supporta livelli superiori di riferimenti indiretti (Point **) in entrambe le direzioni.

Nota:

Le strutture con il valore di enumerazione LayoutKind impostato su Explicit non possono essere utilizzate nell'interoperabilità COM perché la libreria dei tipi esportata non può esprimere un layout esplicito.

Tipi di valore del sistema

Nello spazio dei nomi System sono presenti vari tipi di valore che rappresentano la forma boxed dei tipi primitivi della libreria di runtime. Il tipo di valore della struttura System.Int32 rappresenta ad esempio la forma boxed di ELEMENT_TYPE_I4. Il marshalling di questi tipi non viene eseguito come strutture, allo stesso modo di altri tipi formattati, ma come i tipi primitivi di cui costituiscono la forma boxed. Viene quindi eseguito il marshalling di System.Int32 come ELEMENT_TYPE_I4, anziché come struttura contenente un singolo membro di tipo long. Nella tabella riportata di seguito vengono elencati i tipi di valore dello spazio dei nomi System che rappresentano la forma boxed dei tipi primitivi.

Tipi di valore del sistema

Tipo di elemento

System.Boolean

ELEMENT_TYPE_BOOLEAN

System.SByte

ELEMENT_TYPE_I1

System.Byte

ELEMENT_TYPE_UI1

System.Char

ELEMENT_TYPE_CHAR

System.Int16

ELEMENT_TYPE_I2

System.UInt16

ELEMENT_TYPE_U2

System.Int32

ELEMENT_TYPE_I4

System.UInt32

ELEMENT_TYPE_U4

System.Int64

ELEMENT_TYPE_I8

System.UInt64

ELEMENT_TYPE_U8

System.Single

ELEMENT_TYPE_R4

System.Double

ELEMENT_TYPE_R8

System.String

ELEMENT_TYPE_STRING

System.IntPtr

ELEMENT_TYPE_I

System.UIntPtr

ELEMENT_TYPE_U

Alcuni degli altri tipi di valore dello spazio dei nomi System sono gestiti in modo diverso. Poiché il codice non gestito possiede già formati ben definiti per questi tipi, il gestore di marshalling applica regole speciali. Nella tabella riportata di seguito sono elencati i tipi di valore speciali nello spazio dei nomi System, nonché il tipo non gestito sul quale viene eseguito il marshalling.

Tipi di valore del sistema

Tipo IDL

System.DateTime

DATE

System.Decimal

DECIMAL

System.Guid

GUID

System.Drawing.Color

OLE_COLOR

Nel codice riportato di seguito viene mostrata la definizione dei tipi non gestiti DATE, GUID, DECIMAL e OLE_COLOR nella libreria dei tipi Stdole2.

Rappresentazione di libreria dei tipi

typedef double DATE;
typedef DWORD OLE_COLOR;

typedef struct tagDEC {
    USHORT    wReserved;
    BYTE      scale;
    BYTE      sign;
    ULONG     Hi32;
    ULONGLONG Lo64;
} DECIMAL;

typedef struct tagGUID {
    DWORD Data1;
    WORD  Data2;
    WORD  Data3;
    BYTE  Data4[ 8 ];
} GUID;

Nel codice che segue vengono mostrate le definizioni corrispondenti nell'interfaccia IValueTypes gestita.

Public Interface IValueTypes
   Sub M1(d As System.DateTime)
   Sub M2(d As System.Guid)
   Sub M3(d As System.Decimal)
   Sub M4(d As System.Drawing.Color)
End Interface
public interface IValueTypes {
   void M1(System.DateTime d);
   void M2(System.Guid d);
   void M3(System.Decimal d);
   void M4(System.Drawing.Color d);
}

Rappresentazione di libreria dei tipi

[…]
interface IValueTypes : IDispatch {
   HRESULT M1([in] DATE d);
   HRESULT M2([in] GUID d);
   HRESULT M3([in] DECIMAL d);
   HRESULT M4([in] OLE_COLOR d);
};

Vedere anche

Concetti

Tipi copiabili e non copiabili

Attributi direzionali

Copia e blocco

Altre risorse

Comportamento di marshalling predefinito