Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Parâmetros e campos digitados como System.Object podem ser expostos a código não gerenciado como um dos seguintes tipos:
Uma variante quando o objeto é um parâmetro.
Uma interface quando o objeto é um campo de estrutura.
Somente a interoperabilidade COM suporta empacotamento para tipos de objeto. O comportamento padrão é empacotar objetos para variantes COM. Estas regras aplicam-se apenas ao tipo Object e não se aplicam a objetos fortemente tipados que derivam da Object classe.
Opções de Marshalling
A tabela seguinte mostra as opções de marshalling para o Object tipo de dado. O MarshalAsAttribute atributo fornece vários UnmanagedType valores de enumeração para organizar objetos.
| Tipo de enumeração | Descrição do formato não gerenciado |
|---|---|
|
UnmanagedType.Struct (padrão para parâmetros) |
Uma variante ao estilo COM. |
| UnmanagedType.Interface | Uma IDispatch interface, se possível; caso contrário, uma IUnknown interface. |
|
UnmanagedType.IUnknown (padrão para campos) |
Uma interface IUnknown. |
| UnmanagedType.IDispatch | Uma interface IDispatch. |
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 a MarshalObject interface para uma biblioteca de tipos.
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)
}
Nota
O marshaller 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 formatado para uma biblioteca de tipos.
struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
Marshalling Objeto para Interface
Quando um objeto é exposto ao COM como uma interface, essa interface é a interface de classe para o tipo Object gerido (a _Object interface). Esta interface é tipada como um IDispatch (UnmanagedType) ou um IUnknown (UnmanagedType.IUnknown) na biblioteca de tipos resultante. Os clientes COM podem invocar dinamicamente os membros da classe gerida ou quaisquer membros implementados pelas suas classes derivadas através da _Object interface. O cliente também pode chamar QueryInterface para obter qualquer outra interface explicitamente implementada pelo tipo gerido.
Marshalling Objeto para Variante
Quando um objeto é marshallado para uma variante, o tipo interno da variante é determinado em tempo de execução, com base nas seguintes regras:
Se a referência de objeto for nula (Nothing no Visual Basic), o objeto será 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 será determinado pelas regras incorporadas no marshaller e mostradas 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 do IConvertible.GetTypeCode método. Caso contrário, o objeto é empacotado como uma variante do tipo VT_UNKNOWN.
Marshalling Tipos de sistema para variante
A tabela a seguir mostra os tipos de objeto gerenciado e seus tipos de variantes COM correspondentes. Esses tipos são convertidos somente quando a assinatura do método que está sendo chamado é do tipo System.Object.
| Tipo de objeto | Tipo de variante COM |
|---|---|
| Referência de objeto nulo (Nothing no Visual Basic). | VT_EMPTY |
| System.DBNull | VT_NULL |
| System.Runtime.InteropServices.ErrorWrapper | VT_ERROR |
| System.Reflection.Missing | VT_ERROR com E_PARAMNOTFOUND |
| System.Runtime.InteropServices.DispatchWrapper | VT_DISPATCH |
| System.Runtime.InteropServices.UnknownWrapper | VT_UNKNOWN |
| System.Runtime.InteropServices.CurrencyWrapper | VT_CY |
| System.Boolean | VT_BOOL |
| System.SByte | VT_I1 |
| System.Byte | VT_UI1 |
| System.Int16 | VT_I2 |
| System.UInt16 | VT_UI2 |
| System.Int32 | VT_I4 |
| System.UInt32 | VT_UI4 |
| System.Int64 | VT_I8 |
| System.UInt64 | VT_UI8 |
| System.Single | VT_R4 |
| System.Double | VT_R8 |
| System.Decimal | VT_DECIMAL |
| System.DateTime | VT_DATE |
| System.String | VT_BSTR |
| System.IntPtr | VT_INT |
| System.UIntPtr | VT_UINT |
| System.Array | VT_ARRAY |
Usando a 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.
Os tipos COM que não têm tipos gerenciados correspondentes podem ser empacotados usando classes de wrapper como ErrorWrapper, DispatchWrapper, UnknownWrappere CurrencyWrapper. O exemplo de código a seguir demonstra como usar esses wrappers 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 wrapper são definidas no System.Runtime.InteropServices namespace.
Manipulação da Interface IConvertible para Variante
Tipos diferentes dos listados na seção anterior podem controlar como eles são organizados implementando a IConvertible interface. Se o objeto implementar a interface IConvertible, o tipo variante COM é determinado em tempo de execução pelo valor da enumeração TypeCode devolvido pelo método IConvertible.GetTypeCode.
A tabela seguinte mostra os valores possíveis para a TypeCode enumeração e o tipo de variante COM correspondente para cada valor.
| Código do tipo | Tipo de variante 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 |
| TipoCode.Int16 | VT_I2 |
| TipoCode.UInt16 | VT_UI2 |
| TypeCode.Int32 | VT_I4 |
| TypeCode.UInt32 | VT_UI4 |
| TipoCode.Int64 | VT_I8 |
| TipoCode.UInt64 | VT_UI8 |
| TypeCode.Single | VT_R4 |
| TypeCode.Double | VT_R8 |
| TypeCode.Decimal | VT_DECIMAL |
| TypeCode.DateTime | VT_DATE |
| TypeCode.String | VT_BSTR |
| Não suportado. | VT_INT |
| Não suportado. | VT_UINT |
| Não suportado. | VT_ARRAY |
| Não suportado. | VT_RECORD |
| Não suportado. | VT_CY |
| Não suportado. | VT_VARIANT |
O valor da variante COM é determinado ao chamar a interface IConvertible.ToType , onde ToType é a rotina de conversão que corresponde ao Type que foi devolvido de IConvertible.GetTypeCode. Por exemplo, um objeto que retorna TypeCode.Double de IConvertible.GetTypeCode é empacotado como uma variante COM do tipo VT_R8. Pode obter o valor da variante (armazenada no campo dblVal da variante COM) fazendo casting para a interface IConvertible e chamar o método ToDouble.
Marshalling variante para objeto
Ao empacotar uma variante a um objeto, o tipo, e às vezes o valor, da variante empacotada determina o tipo de objeto produzido. A tabela a seguir identifica cada tipo de variante e o tipo de objeto correspondente que o m marshallerreates quando uma variante é passada de COM para o .NET Framework.
| Tipo de variante COM | Tipo de objeto |
|---|---|
| VT_EMPTY | Referência de objeto nulo (Nothing no Visual Basic). |
| VT_NULL | System.DBNull |
| VT_DISPATCH | System.__ComObject ou null if (pdispVal == null) |
| VT_UNKNOWN | System.__ComObject ou null if (punkVal == null) |
| VT_ERROR | System.UInt32 |
| VT_BOOL | System.Boolean |
| VT_I1 | System.SByte |
| VT_UI1 | System.Byte |
| VT_I2 | System.Int16 |
| VT_UI2 | System.UInt16 |
| VT_I4 | System.Int32 |
| VT_UI4 | System.UInt32 |
| VT_I8 | System.Int64 |
| VT_UI8 | System.UInt64 |
| VT_R4 | System.Single |
| VT_R8 | System.Double |
| VT_DECIMAL | System.Decimal |
| VT_DATE | System.DateTime |
| VT_BSTR | System.String |
| VT_INT | System.Int32 |
| VT_UINT | System.UInt32 |
| | VT_ARRAY VT_* | System.Array |
| VT_CY | System.Decimal |
| VT_RECORD | Tipo de valor em caixa correspondente. |
| VT_VARIANT | Não suportado. |
Os tipos de variante passados de COM para código gerenciado e, em seguida, de volta para COM podem não manter o mesmo tipo de variante durante a chamada. Considere o que acontece quando uma variante do tipo VT_DISPATCH é passada do COM para o .NET Framework. Durante o marshalling, a variante é convertida em .System.Object Se depois Object for devolvido ao COM, é redirecionado para uma variante do tipo VT_UNKNOWN. Não há garantia de que a variante produzida quando um objeto é empacotado do código gerenciado para COM será do mesmo tipo que a variante usada inicialmente para produzir o objeto.
Marshalling ByRef Variantes
Embora as próprias variantes possam ser transmitidas por valor ou por referência, a VT_BYREF bandeira também pode ser usada com qualquer tipo de variante para indicar que o conteúdo da variante está a ser transmitido por referência em vez de por valor. A diferença entre o marshalling de variantes por referência e o marshalling de uma variante com a flag definida VT_BYREF pode causar confusão. A ilustração a seguir esclarece as diferenças:
Variantes passadas por valor e por referência
Comportamento padrão para empacotar objetos e variantes por valor
Ao passar objetos do código gerenciado para COM, o conteúdo do objeto é copiado para uma nova variante criada pelo marshaller, usando as regras definidas em Marshalling Object to Variant. As alterações feitas na variante no lado não gerenciado não são propagadas de volta ao objeto original no retorno da chamada.
Ao passar variantes de COM para código gerenciado, o conteúdo da variante é copiado para um objeto recém-criado, usando as regras definidas em Marshalling Variant to Object. As alterações feitas no objeto no lado gerenciado não são propagadas de volta para a variante original no retorno da chamada.
Comportamento padrão para empacotar objetos e variantes por referência
Para propagar as alterações de volta para o chamador, os parâmetros devem ser passados por referência. Por exemplo, pode usar a ref palavra-chave em C# (ou ByRef no código gerido do Visual Basic) para passar parâmetros por referência. Em COM, os parâmetros de referência são passados usando um ponteiro, como uma variante *.
Ao passar um objeto para COM por referência, o marshaller cria uma nova variante e copia o conteúdo da referência de objeto para a variante antes que a chamada seja feita. A variante é passada para a função não gerenciada, onde o usuário é livre para alterar o conteúdo da variante. Ao retornar da chamada, todas as alterações feitas na variante no lado não gerenciado são propagadas de volta ao objeto original. Se o tipo da variante for diferente do tipo da variante passada para a chamada, as alterações serão propagadas de volta para um objeto de um tipo diferente. Ou seja, o tipo do objeto passado para a chamada pode diferir do tipo do objeto retornado da chamada.
Ao passar uma variante para o código gerenciado por referência, o marshaller cria um novo objeto e copia o conteúdo da variante para o objeto antes de fazer a chamada. Uma referência ao objeto é passada para a função gerenciada, onde o usuário é livre para alterar o objeto. Ao retornar da chamada, todas as alterações feitas no objeto referenciado são propagadas de volta para a variante original. Se o tipo do objeto for diferente do tipo do objeto passado para a chamada, o tipo da variante original será alterado e o valor será propagado de volta para a variante. Novamente, o tipo da variante passada para a chamada pode diferir do tipo da variante retornada da chamada.
Comportamento padrão para empacotar uma variante com o sinalizador VT_BYREF definido
Uma variante passada para o código gerido por valor pode ter a
VT_BYREFflag definida para indicar que a variante contém uma referência em vez de um valor. Neste caso, a variante ainda é empacotada para um objeto porque a variante está sendo passada por valor. O marshaller automaticamente desreferencia o conteúdo da variante e copia-o para um objeto recém-criado antes de fazer a chamada. O objeto é então passado para a função gerenciada; no entanto, ao retornar da chamada, o objeto não é propagado de volta para a variante original. As alterações feitas no objeto gerenciado são perdidas.Atenção
Não há forma de alterar o valor de uma variante passada por valor, mesmo que a variante tenha o
VT_BYREFflag definido.Uma variante a ser passada para código gerido por referência pode também ter a
VT_BYREFflag definida para indicar que a variante contém outra referência. Se o fizer, a variante é convertida para um objetorefuma vez que a variante está a ser passada por referência. O marshaller automaticamente desreferencia o conteúdo da variante e copia-o para um objeto recém-criado antes de fazer a chamada. Ao retornar da chamada, o valor do objeto é propagado de volta para a referência dentro da variante original somente se o objeto for do mesmo tipo que o objeto passado. Ou seja, a propagação não altera o tipo de variante com aVT_BYREFbandeira definida. Se o tipo do objeto for alterado durante a chamada, ocorrerá um InvalidCastException no retorno da chamada.
A tabela a seguir resume as regras de propagação para variantes e objetos.
| De | Para | Alterações propagadas de volta |
|---|---|---|
| Variantev | Objetoo | Nunca |
| Objetoo | Variantev | Nunca |
| Variante*pv | Ref Objetoo | Sempre |
| Ref objetoo | Variante*pv | Sempre |
| VarianteV(VT_BYREF|VT_*) | Objetoo | Nunca |
| VarianteV(VT_BYREF|VT_) | Ref Objetoo | Apenas se o tipo não tiver sido alterado. |