Padrão de empacotamento para objetos
Parâmetros e campos digitados como System.Object podem ser expostos ao código não gerenciado como um dos seguintes tipos:
Uma variante quando o objeto é um parâmetro.
Uma interface quando o objeto é uma estrutura de campo.
Somente COM interop oferece suporte para empacotamento de tipos de objeto. O comportamento padrão é empacotar objetos de variantes de COM. Essas regras se aplicam somente ao tipo de objeto e não se aplicam a objetos com rigidez de tipos que derivam de objeto classe.
Este tópico fornece as seguintes informações adicionais sobre o empacotamento de tipos de objeto:
Opções de empacotamento.
Objeto Interface de empacotamento.
Objeto de empacotamento para Variant
Variante de objeto de empacotamento.
Variantes de ByRef de empacotamento.
Opções de empacotamento.
A tabela a seguir mostra as opções de empacotamento para o objeto o tipo de dados. O MarshalAsAttribute atributo fornece vários UnmanagedType valores de enumeração para empacotar objetos.
Tipo de enumeração |
Descrição do formato não gerenciado |
---|---|
UnmanagedType.Struct (padrão para parâmetros) |
Uma variante de estilo COM. |
UnmanagedType.Interface |
Um IDispatch de interface, se possível; Caso contrário, um IUnknown interface. |
UnmanagedType.IUnknown (padrão para campos) |
Um IUnknown interface. |
UnmanagedType.IDispatch |
Um IDispatch interface. |
O exemplo a seguir mostra a definição de interface gerenciada para MarshalObject.
Interface MarshalObject
Sub SetVariant(o As Object)
Sub SetVariantRef(ByRef o As Object)
Function GetVariant() As Object
Sub SetIDispatch( <MarshalAs(UnmanagedType.IDispatch)> o As Object)
Sub SetIDispatchRef(ByRef <MarshalAs(UnmanagedType.IDispatch)> o _
As Object)
Function GetIDispatch() As <MarshalAs(UnmanagedType.IDispatch)> Object
Sub SetIUnknown( <MarshalAs(UnmanagedType.IUnknown)> o As Object)
Sub SetIUnknownRef(ByRef <MarshalAs(UnmanagedType.IUnknown)> o _
As Object)
Function GetIUnknown() As <MarshalAs(UnmanagedType.IUnknown)> Object
End Interface
interface MarshalObject {
void SetVariant(Object o);
void SetVariantRef(ref Object o);
Object GetVariant();
void SetIDispatch ([MarshalAs(UnmanagedType.IDispatch)]Object o);
void SetIDispatchRef([MarshalAs(UnmanagedType.IDispatch)]ref Object o);
[MarshalAs(UnmanagedType.IDispatch)] Object GetIDispatch();
void SetIUnknown ([MarshalAs(UnmanagedType.IUnknown)]Object o);
void SetIUnknownRef([MarshalAs(UnmanagedType.IUnknown)]ref Object o);
[MarshalAs(UnmanagedType.IUnknown)] Object GetIUnknown();
}
O código a seguir exporta o MarshalObject interface para uma biblioteca de tipo.
interface MarshalObject {
HRESULT SetVariant([in] VARIANT o);
HRESULT SetVariantRef([in,out] VARIANT *o);
HRESULT GetVariant([out,retval] VARIANT *o)
HRESULT SetIDispatch([in] IDispatch *o);
HRESULT SetIDispatchRef([in,out] IDispatch **o);
HRESULT GetIDispatch([out,retval] IDispatch **o)
HRESULT SetIUnknown([in] IUnknown *o);
HRESULT SetIUnknownRef([in,out] IUnknown **o);
HRESULT GetIUnknown([out,retval] IUnknown **o)
}
Observação
O empacotador de interoperabilidade libera automaticamente qualquer objeto alocado dentro da variante após a chamada.
O exemplo a seguir mostra um tipo de valor formatado.
Public Structure ObjectHolder
Dim o1 As Object
<MarshalAs(UnmanagedType.IDispatch)> Public o2 As Object
End Structure
public struct ObjectHolder {
Object o1;
[MarshalAs(UnmanagedType.IDispatch)]public Object o2;
}
O código a seguir exporta o tipo de formatação para uma biblioteca de tipos.
struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
Objeto Interface de empacotamento.
Quando um objeto é exposto a COM como uma interface, interface é a interface de classe de tipo gerenciado Object (a _Object interface). Essa interface é digitada como uma IDispatch (UnmanagedType.IDispatch) ou um IUnknown (UnmanagedType.IUnknown) na biblioteca de tipos resultante. Clientes COM dinamicamente podem chamar os membros da classe gerenciada ou quaisquer membros implementados por suas classes derivadas, por meio de _Object interface. O cliente também pode chamar QueryInterface para obter qualquer outra interface explicitamente implementada pelo tipo gerenciado.
Objeto de empacotamento para Variant
Quando um objeto é empacotado para uma variante, o tipo de variante interno é determinado em tempo de execução, com base nas seguintes regras:
Se a referência de objeto é nula (nada em Visual Basic), o objeto é empacotado para uma variante do tipo VT_EMPTY.
Se o objeto for uma instância de qualquer tipo, listado na tabela a seguir, o tipo de variante resultante é determinado pelas regras incorporada ao empacotamento e mostrado na tabela.
Outros objetos que precisam controlar explicitamente o comportamento de empacotamento podem implementar a IConvertible interface. Nesse caso, o tipo de variante é determinado pelo código de tipo retornado a partir de IConvertible.GetTypeCode método. Caso contrário, o objeto é empacotado como uma variante do tipo VT_UNKNOWN.
Empacotamento de tipos de sistema como Variant
A tabela a seguir mostra os tipos de objeto gerenciado e seus tipos de variant COM correspondentes. Esses tipos são convertidos somente quando a assinatura do método sendo chamado é do tipo System.Object.
Tipo de objeto |
Tipo variant COM |
---|---|
Nulo de referência de objeto (nada em Visual Basic). |
VT_EMPTY |
VT_NULL |
|
VT_ERROR |
|
VT_ERROR com E_PARAMNOTFOUND |
|
VT_DISPATCH |
|
VT_UNKNOWN |
|
VT_CY |
|
VT_BOOL |
|
VT_I1 |
|
VT_UI1 |
|
VT_I2 |
|
VT_UI2 |
|
VT_L4 |
|
VT_UI4 |
|
VT_I8 |
|
VT_UI8 |
|
VT_R4 |
|
VT_R8 |
|
VT_DECIMAL |
|
VT_DATE |
|
VT_BSTR |
|
VT_INT |
|
VT_UINT |
|
VT_ARRAY |
Usando o MarshalObject interface definida no exemplo anterior, o exemplo de código a seguir demonstra como passar vários tipos de variantes para um servidor COM.
Dim mo As New MarshalObject()
mo.SetVariant(Nothing) ' Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value) ' Marshal as variant of type VT_NULL.
mo.SetVariant(CInt(27)) ' Marshal as variant of type VT_I2.
mo.SetVariant(CLng(27)) ' Marshal as variant of type VT_I4.
mo.SetVariant(CSng(27.0)) ' Marshal as variant of type VT_R4.
mo.SetVariant(CDbl(27.0)) ' Marshal as variant of type VT_R8.
MarshalObject mo = new MarshalObject();
mo.SetVariant(null); // Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value); // Marshal as variant of type VT_NULL.
mo.SetVariant((int)27); // Marshal as variant of type VT_I2.
mo.SetVariant((long)27); // Marshal as variant of type VT_I4.
mo.SetVariant((single)27.0); // Marshal as variant of type VT_R4.
mo.SetVariant((double)27.0); // Marshal as variant of type VT_R8.
Tipos COM que não têm correspondentes de tipos gerenciados podem ser empacotados usando classes de invólucro, como ErrorWrapper, DispatchWrapper, UnknownWrapper, e CurrencyWrapper. O exemplo de código a seguir demonstra como usar esses invólucros para passar vários tipos de variantes para um servidor COM.
Imports System.Runtime.InteropServices
' Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(New UnknownWrapper(inew))
' Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(New DispatchWrapper(inew))
' Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(New ErrorWrapper(&H80054002))
' Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(New CurrencyWrapper(New Decimal(5.25)))
using System.Runtime.InteropServices;
// Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(new UnknownWrapper(inew));
// Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(new DispatchWrapper(inew));
// Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(new ErrorWrapper(0x80054002));
// Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(new CurrencyWrapper(new Decimal(5.25)));
As classes de invólucro são definidas no System.Runtime.InteropServices namespace.
A Interface IConvertible Variant de empacotamento.
Tipos diferentes de aqueles listados na seção anterior podem controlar como eles são empacotados Implementando o IConvertible interface. Se o objeto que implementa o IConvertible em tempo de execução, a interface, o tipo de variante COM é determinado pelo valor da TypeCode retornada de enumeração o IConvertible.GetTypeCode método.
A tabela a seguir mostra os valores possíveis para o TypeCode enumeração e o tipo de variante COM a correspondente para cada valor.
TypeCode |
Tipo variant COM |
---|---|
TypeCode.Empty |
VT_EMPTY |
TypeCode.Object |
VT_UNKNOWN |
TypeCode.DBNull |
VT_NULL |
TypeCode.Boolean |
VT_BOOL |
TypeCode.Char |
VT_UI2 |
TypeCode.Sbyte |
VT_I1 |
TypeCode.Byte |
VT_UI1 |
TypeCode.Int16 |
VT_I2 |
TypeCode.UInt16 |
VT_UI2 |
TypeCode.Int32 |
VT_L4 |
TypeCode.UInt32 |
VT_UI4 |
TypeCode.Int64 |
VT_I8 |
TypeCode.UInt64 |
VT_UI8 |
TypeCode.Single |
VT_R4 |
TypeCode.Double |
VT_R8 |
TypeCode.Decimal |
VT_DECIMAL |
TypeCode.DateTime |
VT_DATE |
TypeCode.String |
VT_BSTR |
Sem suporte. |
VT_INT |
Sem suporte. |
VT_UINT |
Sem suporte. |
VT_ARRAY |
Sem suporte. |
VT_RECORD |
Sem suporte. |
VT_CY |
Sem suporte. |
VT_VARIANT |
O valor da variante COM é determinado chamando o IConvertible.Totipo interface, onde paratipo de é a rotina de conversão que corresponde ao tipo que foi retornado de IConvertible.GetTypeCode. Por exemplo, um objeto que retorna TypeCode.Double de IConvertible.GetTypeCode é empacotado como uma variante de COM do tipo VT_R8. Você pode obter o valor da variante (armazenados na dblVal campo da variante COM) com a conversão para o IConvertible interface e a chamada a ToDouble método.
Variante de objeto de empacotamento.
Quando uma variante de um objeto, o tipo e às vezes o valor da variante empacotado de empacotamento determina o tipo de objeto produzido. A tabela a seguir identifica cada tipo de variante e o tipo de objeto correspondente, o empacotador cria quando uma variante é passada do COM para o.NET Framework.
Tipo variant COM |
Tipo de objeto |
---|---|
VT_EMPTY |
Nulo de referência de objeto (nada em Visual Basic). |
VT_NULL |
|
VT_DISPATCH |
System.__ComObject ou null se (pdispVal = = null) |
VT_UNKNOWN |
System.__ComObject ou null se (punkVal = = null) |
VT_ERROR |
|
VT_BOOL |
|
VT_I1 |
|
VT_UI1 |
|
VT_I2 |
|
VT_UI2 |
|
VT_L4 |
|
VT_UI4 |
|
VT_I8 |
|
VT_UI8 |
|
VT_R4 |
|
VT_R8 |
|
VT_DECIMAL |
|
VT_DATE |
|
VT_BSTR |
|
VT_INT |
|
VT_UINT |
|
VT_ARRAY | VT_ *. |
|
VT_CY |
|
VT_RECORD |
Tipo de valor convertidos de correspondentes. |
VT_VARIANT |
Sem suporte. |
Tipos Variant passados do COM para código gerenciado e, em seguida, voltar ao COM podem não manter o mesmo tipo de variante para a duração da chamada. Considere o que acontece quando uma variante do tipo VT_DISPATCH é passado do COM para o.NET Framework. Durante o empacotamento, a variante é convertida em um System.Object. Se o objeto é então passado ao COM, empacotado novamente para uma variante do tipo VT_UNKNOWN. Não há nenhuma garantia de que a variante produzida quando um objeto é empacotado do código gerenciado para COM será o mesmo tipo de variante usado inicialmente para produzir o objeto.
Variantes de ByRef de empacotamento.
Embora variantes em si podem ser passados por valor ou referência, o VT_BYREF sinalizador também pode ser usado com qualquer tipo de variant para indicar que o conteúdo da variante está sendo passado por referência em vez de por valor. A diferença entre as variantes de empacotamento por referência e empacotamento de uma variante com o VT_BYREF o conjunto de sinalizador pode ser confuso. A ilustração a seguir explica as diferenças.
Variantes passaram por valor e referência
Comportamento padrão para objetos e variantes de empacotamento por valor
Ao passar objetos de código gerenciado para COM, o conteúdo do objeto é copiado de uma nova variante criada pelo empacotador, usando as regras definidas no O objeto de empacotamento a variante. As alterações feitas no lado não gerenciado com a variante não são propagadas para o objeto original no retorno de chamada.
Ao passar o variantes de COM para código gerenciado, o conteúdo da variante é copiado para um objeto recém-criado, usando as regras definidas no Variante de empacotamento para o objeto. As alterações feitas no objeto no lado gerenciado não são propagadas para a variante original no retorno de chamada.
Comportamento padrão para objetos e variantes de empacotamento por referência
Para propagar as alterações de volta para o chamador, os parâmetros devem ser passados por referência. Por exemplo, você pode usar o ref palavra-chave em C# (ou ByRef na Visual Basic de código gerenciado) para passar parâmetros por referência. No COM, os parâmetros de referência são passados usando um ponteiro como um variante *.
Ao passar um objeto para COM por referência, o empacotador cria uma nova variante e copia o conteúdo de referência de objeto para a variante, antes da chamada é feita. A variante é passada para a função não gerenciada, onde o usuário está livre para alterar o conteúdo da variante. No retorno de chamada, as alterações feitas no lado não gerenciado com a variante são propagadas para o objeto original. Se o tipo de variante for diferente do tipo da variante passada para a chamada, as alterações são propagadas para um objeto de um tipo diferente. Ou seja, o tipo do objeto passado para a chamada pode diferir do tipo de objeto retornado da chamada.
Ao passar uma variante para código gerenciado por referência, o empacotador cria um novo objeto e copia o conteúdo da variante no objeto antes de fazer a chamada. Uma referência ao objeto é passada para a função gerenciada, onde o usuário está livre para alterar o objeto. No retorno de chamada, quaisquer alterações feitas no objeto referenciado são propagadas para a variante original. Se o tipo de objeto for diferente do tipo do objeto passado para a chamada, o tipo de variante original é alterado e o valor é propagado de volta para a variante. Novamente, o tipo de variante passada para a chamada pode diferir do tipo de variante retornado da chamada.
O comportamento padrão para o empacotamento de uma variante com o sinalizador VT_BYREF definido
Uma variante que está sendo passada para código gerenciado por valor pode ter o VT_BYREF o sinalizador será definido para indicar que a variante contém uma referência em vez de um valor. Nesse caso, a variante ainda é empacotada em um objeto porque a variante está sendo passada por valor. O empacotador automaticamente o conteúdo da variante cancela a referência e a copia para um objeto recém-criado antes de fazer a chamada. O objeto é então passado para a função gerenciada; No entanto, no retorno de chamada, o objeto não é propagado a variante original de volta. As alterações feitas no objeto gerenciado são perdidas.
Observação
Não é possível alterar o valor de uma variante passada por valor, mesmo que a variante tem a VT_BYREF o sinalizador será definido.
Uma variante que está sendo passada para código gerenciado por referência também pode ter o VT_BYREF o sinalizador será definido para indicar que a variante contém outra referência. Em caso afirmativo, a variante é empacotada para um ref porque a variante está sendo passada por referência. O empacotador automaticamente o conteúdo da variante cancela a referência e a copia para um objeto recém-criado antes de fazer a chamada. No retorno de chamada, o valor do objeto é propagado de volta para a referência da variante original somente se o objeto é o mesmo tipo, como o objeto passado. Ou seja, a propagação não altera o tipo de uma variante com o VT_BYREF o sinalizador será definido. Se o tipo de objeto for alterado durante a chamada, um InvalidCastException ocorre no retorno de chamada.
A tabela a seguir resume as regras de propagação de objetos e variantes.
From |
Para |
Alterações propagadas de volta |
---|---|---|
Variante v |
Objeto o |
Nunca |
Objeto o |
Variante v |
Nunca |
Variante *VP |
O objeto de ref o |
Sempre |
O objeto de ref o |
Variante *VP |
Sempre |
Variante v(VT_BYREF|VT_*) |
Objeto o |
Nunca |
Variante v(VT_BYREF|VT_) |
O objeto de ref o |
Somente se o tipo não foi alterado. |
Consulte também
Conceitos
Blittable e tipos de não-Blittable