Padrão de empacotamento de tipos de valor

A maioria dos tipos de valor, como, por exemplo, inteiros e números de ponto flutuante são blittable e não necessitam de empacotamento. Outros não é blittable ter representações diferentes na memória gerenciada e de tipos e exigem o empacotamento. Ainda outros tipos requerem formatação explícito entre o limite de interoperação.

Este tópico fornece as informações de acompanhamento em tipos de valor formatado:

  • Tipos de valor usados na plataforma Invoke

  • Tipos de valor usados na interoperabilidade COM

Para além de descrever tipos formatados, este tópico identifica Tipos de valor de sistema que têm comportamento incomum de empacotamento.

Um tipo de formatação é um tipo complexo que contém informações que explicitamente controla o layout de seus membros na memória. As informações de layout do membro são fornecidas usando o StructLayoutAttribute atributo. O layout pode ser uma das seguintes LayoutKind valores de enumeração:

  • LayoutKind.Automatic

    Indica que o common language runtime está livre para reorganizar os membros do tipo para a eficiência. No entanto, quando um tipo de valor é passado para código não gerenciado, o layout dos membros é previsível. Uma tentativa de empacotar dessa estrutura automaticamente faz com que uma exceção.

  • LayoutKind

    Indica que os membros do tipo devem ser dispostos em memória não gerenciada na mesma ordem em que aparecem na definição de tipo gerenciado.

  • Usando LayoutKind. Explicit

    Indica que os membros são dispostos acordo com o FieldOffsetAttribute fornecido com cada campo.

Tipos de valor usados na plataforma Invoke

No exemplo a seguir a Point e Rect tipos de membro de fornecer informações de layout usando o 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;
}

Quando empacotado para código não gerenciado, esses tipos de formatação são empacotados como estruturas de estilo C. Isso fornece uma maneira fácil de chamar uma API não gerenciada que tem os argumentos de estrutura. Por exemplo, o POINT e RECT estruturas podem ser passadas para o Microsoft Win32 API PtInRect funciona da seguinte maneira:

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

Você pode passar estruturas usando a seguinte invocação de plataforma definição:

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

O Rect o tipo de valor deve ser passado por referência, porque a API não gerenciada é esperado um ponteiro para uma RECT a serem passados para a função. O Point tipo de valor é passado por valor, porque a API não gerenciada espera que o POINT a serem passados na pilha. Essa diferença sutil é muito importante. Referências são passadas para código não gerenciado como ponteiros. Valores são passados para código não gerenciado na pilha.

Observação

Quando um tipo formatado é empacotado como uma estrutura, somente os campos dentro do tipo são acessíveis.Se o tipo tem métodos, propriedades ou eventos, são inacessíveis a partir do código não gerenciado.

Classes também podem ser empacotados para código não gerenciado como estruturas de estilo C, fornecidos they have fixed layout do membro. As informações de layout de membro de uma classe também são fornecidas com o StructLayoutAttribute atributo. A principal diferença entre os tipos de valor com fixa o layout e as classes com layout fixo é a maneira na qual são empacotados para código não gerenciado. Tipos de valor são passados por valor (na pilha) e, conseqüentemente, qualquer alteração feita pelo receptor os membros do tipo não é vistas pelo chamador. Tipos de referência são passados por referência (uma referência para o tipo passada na pilha); Conseqüentemente, todas as alterações feitas pelo receptor membros de tipo blittable de um tipo são vistas pelo chamador.

Observação

Se um tipo de referência tiver membros de tipos de não-blittable, a conversão é necessária duas vezes: na primeira vez em quando um argumento é passado para o lado de não gerenciado e pela segunda vez no retorno da chamada.Devido a isso maior sobrecarga, parâmetros In/Out deve ser explicitamente aplicado a um argumento se o chamador deseja ver as alterações feitas pelo receptor.

No exemplo a seguir, o SystemTime classe tem um layout de membro seqüencial e pode ser passado para a API do Win32 GetSystemTime função.

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

O GetSystemTime função é definida da seguinte maneira:

void GetSystemTime(SYSTEMTIME* SystemTime);

O equivalente invocação de plataforma definição para GetSystemTime é o seguinte:

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

Observe que o SystemTime argumento não for digitado como um argumento de referência porque SystemTime é uma classe, não um valor tipo. Ao contrário dos tipos de valor, classes sempre são passados por referência.

O exemplo de código a seguir mostra um diferente Point classe que tem um método chamado SetXY. Porque o tipo tem layout seqüencial, pode ser passado para código não gerenciado e empacotado como uma estrutura. No entanto, o SetXY membro é não é acessível a partir do código não gerenciado, mesmo que o objeto é passado por referência.

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

Tipos de valor usados na interoperabilidade COM

Tipos de formatação também podem ser passados para chamadas de método de interoperabilidade COM. Na verdade, quando exportados para uma biblioteca de tipos, tipos de valor são convertidos automaticamente para estruturas. Como mostra o exemplo a seguir, o Point o tipo de valor se torna uma definição de tipo (typedef) com o nome Point. Todas as referências para o Point tipo de valor em outro lugar na biblioteca de tipos são substituídos com o Point typedef.

Representação de biblioteca de tipo

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

As mesmas regras usadas para empacotar os valores e referências a plataforma chamam chamadas são usadas quando o empacotamento de interfaces COM. Por exemplo, quando uma instância de Point tipo de valor é passado do.NET Framework para COM, o Point é passado por valor. Se a Point o tipo de valor é passado por referência, um ponteiro para uma Point é passado na pilha. O empacotador de interoperabilidade não oferece suporte a altos níveis de indireção (ponto **) em qualquer direção.

Observação

Estruturas tendo o LayoutKind o valor de enumeração definida como Explicit não pode ser usado na interoperabilidade COM, porque a biblioteca de tipos exportados não pode expressar um layout explícito.

Tipos de valor do sistema

O System espaço para nome tem vários tipos de valor que representam o formulário in a box de tipos de primitivos de runtime. Por exemplo, o tipo de valor System.Int32 estrutura representa a forma in a box de ELEMENT_TYPE_I4. Em vez de empacotamento desses tipos de estruturas, como outros tipos de formatação, você empacotá-los da mesma forma como os tipos primitivos, que eles caixa. Int32 , portanto, é empacotado como ELEMENT_TYPE_I4 em vez de como uma estrutura que contém um único membro do tipo longo. A tabela a seguir contém uma lista dos tipos de valor da sistema namespace são representações in a box de tipos primitivos.

Tipo de valor do sistema

Tipo de 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

Tipos de algum outro valor na sistema espaço para nome são tratados de maneira diferente. Porque o código não gerenciado já possui formatos bem estabelecidos para esses tipos, o empacotador tem regras especiais para empacotamento-los. A tabela a seguir lista os tipos de valor especial no sistema namespace, bem como o tipo não gerenciado, eles são empacotados para.

Tipo de valor do sistema

Tipo IDL

System.DateTime

DATA

System.Decimal

DECIMAL

System.Guid

GUID

System.Drawing.Color

OLE_COLOR

O código a seguir mostra a definição dos tipos de não gerenciados Data de, GUID, DECIMAL, e OLE_COLOR na biblioteca de tipos Stdole2.

Representação de biblioteca de tipo

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;

O código a seguir mostra as definições correspondentes no gerenciados IValueTypes interface.

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

Representação de biblioteca de tipo

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

Consulte também

Conceitos

Blittable e tipos de não-Blittable

Atributos direcionais

Copiando e fixando

Outros recursos

Comportamento de empacotamento padrão