Маршалинг по умолчанию для объектов
Параметры и поля, приведенные к типу System.Object, могут быть предоставлены неуправляемому коду как один из следующих типов:
Тип variant, когда объект является параметром.
Интерфейс, когда объект является полем структуры.
Маршалинг для объектных типов поддерживается только COM-взаимодействием. По умолчанию выполняется маршалинг объектов в типы variant модели COM. Эти правила применимы только к типу Object и не применимы для объектов со строгим типом, производных из класса Object.
В данном разделе представлены следующие дополнительные сведения о маршалинге типов объектов.
Параметры маршалинга
Маршалинг объекта в интерфейс
Маршалинг объекта в тип Variant
Маршалинг типа Variant в объект
Маршалинг типа Variant по ссылке
Параметры маршалинга
В следующей таблице показаны варианты маршалинга для типа данных Object. Атрибут MarshalAsAttribute предоставляет определенные значения перечисления UnmanagedType для маршалинга объектов.
Тип перечисления |
Описание неуправляемого формата |
---|---|
UnmanagedType.Struct (параметры по умолчанию) |
Тип Variant в стиле COM. |
UnmanagedType.Interface |
Если возможно — интерфейс IDispatch, в противном случае — интерфейс IUnknown. |
UnmanagedType.IUnknown (поля по умолчанию) |
Интерфейс IUnknown. |
UnmanagedType.IDispatch |
Интерфейс IDispatch. |
В следующем примере показано соответствующее определение управляемого интерфейса для 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();
}
Следующий код выполняет экспорт интерфейса MarshalObject в библиотеку типов.
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)
}
Примечание |
---|
Упаковщик взаимодействия после вызова автоматически освобождает любой объект, распределенный внутри типа Variant. |
В следующем примере показан форматированный тип значения.
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;
}
В следующем коде выполняется экспорт форматированного типа в библиотеку типов.
struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
Маршалинг объекта в интерфейс
Когда объект предоставляется модели COM как интерфейс, этот интерфейс является интерфейсом класса для управляемого типа Object (интерфейс _Object). Этот интерфейс в полученной библиотеке типов типизируется как IDispatch (UnmanagedType.IDispatch) или IUnknown (UnmanagedType.IUnknown). COM-клиенты могут динамически обращаться к членам управляемого класса или любым членам, реализованным его производными классами через интерфейс _Object. Клиент может также вызывать QueryInterface для получения любого другого интерфейса, в явном виде реализованного управляемым типом.
Маршалинг объекта в тип Variant
При маршалинге объекта в тип Variant внутренний тип Variant определяется во время выполнения с использованием следующих правил:
Если ссылка объекта равна null (Nothing в Visual Basic), выполняется маршалинг объекта в Variant типа VT_EMPTY.
Если объект — это экземпляр любого типа из перечисленных в следующей таблице, результирующий тип Variant определяется в соответствии с правилами, встроенными в упаковщик и показанными в таблице.
Другие объекты, которым нужно явно управлять характеристиками маршалинга, могут реализовать интерфейс IConvertible. В этом случае тип Variant определяется кодом типа, возвращенным методом IConvertible.GetTypeCode. В остальных случаях объект маршалируется как Variant типа VT_UNKNOWN.
Маршалинг системных типов в тип Variant
В следующей таблице показаны типы управляемых объектов и соответствующие им типы Variant модели COM. Преобразование этих типов выполняется, только когда сигнатура вызываемого метода относится к типу System.Object.
Тип объекта |
Тип Variant модели COM |
---|---|
Ссылка на пустой объект (Nothing в Visual Basic). |
VT_EMPTY |
VT_NULL |
|
VT_ERROR |
|
VT_ERROR c E_PARAMNOTFOUND |
|
VT_DISPATCH |
|
VT_UNKNOWN |
|
VT_CY |
|
VT_BOOL |
|
VT_I1 |
|
VT_UI1 |
|
VT_I2 |
|
VT_UI2 |
|
VT_I4 |
|
VT_UI4 |
|
VT_I8 |
|
VT_UI8 |
|
VT_R4 |
|
VT_R8 |
|
VT_DECIMAL |
|
VT_DATE |
|
VT_BSTR |
|
VT_INT |
|
VT_UINT |
|
VT_ARRAY |
В следующем примере кода с использованием интерфейса MarshalObject, определенного в предыдущем примере, демонстрируется передача различных типов Variant в 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.
Маршалинг COM-типов, для которых отсутствуют соответствующие управляемые типы, может быть выполнен с помощью классов-оболочек, таких как ErrorWrapper, DispatchWrapper, UnknownWrapper и CurrencyWrapper. В следующем примере кода демонстрируется использование этих оболочек для передачи различных типов Variant 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)));
Классы программы-оболочки определяются в пространстве имен System.Runtime.InteropServices.
Маршалинг интерфейса IConvertible в тип Variant
Типы, отличающиеся от перечисленных в предыдущем разделе, могут управлять своим маршалингом, реализуя интерфейс IConvertible. Если объект реализует интерфейс IConvertible, тип Variant модели COM определяется во время выполнения значением перечисления TypeCode, возвращенным методом IConvertible.GetTypeCode.
В следующей таблице показаны возможные значения перечисления TypeCode и соответствующие типы Variant модели СОМ для каждого из этих значений.
TypeCode |
Тип 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_I4 |
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 |
Не поддерживается. |
VT_INT |
Не поддерживается. |
VT_UINT |
Не поддерживается. |
VT_ARRAY |
Не поддерживается. |
VT_RECORD |
Не поддерживается. |
VT_CY |
Не поддерживается. |
VT_VARIANT |
Значение типа Variant модели COM определяется с помощью вызова интерфейса IConvertible.ToТип, где ToТип — это процедура преобразования, соответствующая типу, который был возвращен из интерфейса IConvertible.GetTypeCode. Например, объект, возвращающий TypeCode.Double из IConvertible.GetTypeCode, маршалируется как тип Variant VT_R8 модели COM. Значение типа Variant (хранящееся в поле dblVal типа Variant модели COM variant) можно получить, выполняя приведение к интерфейсу IConvertible и вызывая метод ToDouble.
Маршалинг типа Variant в объект
При маршалинге типа Variant в объект тип (и иногда значение) маршалируемого типа Variant определяет тип получаемого объекта. В следующей таблице показаны каждый тип Variant и тип соответствующего объекта, создаваемого упаковщиком при передаче типа Variant из COM в .NET Framework.
Тип Variant модели COM |
Тип объекта |
---|---|
VT_EMPTY |
Ссылка на пустой объект (Nothing в Visual Basic). |
VT_NULL |
|
VT_DISPATCH |
System.__ComObject или null, если (pdispVal == null) |
VT_UNKNOWN |
System.__ComObject или null, если (punkVal == null) |
VT_ERROR |
|
VT_BOOL |
|
VT_I1 |
|
VT_UI1 |
|
VT_I2 |
|
VT_UI2 |
|
VT_I4 |
|
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 |
Соответствующий тип упакованного значения. |
VT_VARIANT |
Не поддерживается. |
Типы Variant, переданные из COM в управляемый код и затем обратно в COM, могут не сохранять исходный тип Variant в течение вызова. Рассмотрим, что произойдет при передаче типа Variant VT_DISPATCH из COM в .NET Framework. При маршалинге тип Variant преобразуется в System.Object. Если затем этот Object передается назад в COM, при маршалинге он преобразуется в тип Variant VT_UNKNOWN. Нет никаких гарантий, что тип Variant, созданный при маршалинге объекта из управляемого кода в COM, будет совпадать с типом variant, первоначально использованным для получения этого объекта.
Маршалинг типа Variant по ссылке
Хотя сами типы Variant можно передавать по значению или по ссылке, с любым типом Variant можно также использовать флаг VT_BYREF для указания, что содержимое данного типа передается по ссылке, а не по значению. Различия между маршалингом типа Variant по ссылке и маршалингом типа Variant с установленным флагом VT_BYREF могут привести к путанице. Эти различия разъясняются на следующем рисунке.
Передача типа Variant по значению и по ссылке
Стандартные характеристики маршалинга объектов и типов Variant по значению
При передаче объектов из управляемого кода в COM содержимое объекта копируется в новый тип Variant, созданный упаковщиком, в соответствии с правилами, определенными в разделе Маршалинг объекта в тип Variant. При возврате из вызова изменения типа Variant на неуправляемой стороне не распространяются обратно в исходный объект.
При передаче значений типов Variant из COM в управляемый код содержимое типа Variant копируется в новый объект, созданный в соответствии с правилами, определенными в разделе Маршалинг типа Variant в объект. При возврате из вызова изменения объекта на управляемой стороне не распространяются назад в исходный тип Variant.
Стандартные характеристики маршалинга объектов и типов Variant по ссылке
Чтобы изменения могли передаваться назад в вызывающий объект, параметры необходимо передавать по ссылке. Например, для передачи параметров по ссылке можно использовать ключевое слово ref в C# (или ByRef в управляемом коде Visual Basic). В COM ссылочные параметры передаются с использованием указателя, например variant *.
При передаче объекта в COM по ссылке упаковщик создает новый тип Variant и перед вызовом копирует содержимое ссылочного объекта в тип Variant. Этот тип Variant передается в неуправляемую функцию, в которой пользователь может свободно изменять содержимое этого типа Variant. При возврате из вызова изменения, сделанные в типе Variant на неуправляемой стороне, распространяются обратно в исходный объект. Если тип этого объекта Variant отличается от типа объекта Variant, переданного в вызов, изменения передаются обратно в объект другого типа. Т.е. тип объекта, переданного в вызов, может отличаться от типа объекта, возвращенного из этого вызова.
При передаче объекта Variant в управляемый код по ссылке упаковщик создает новый объект и перед вызовом копирует в него содержимое объекта Variant. Ссылка на этот объект передается в управляемую функцию, в которой пользователь может свободно изменять этот объект. При возврате из вызова изменения, сделанные в ссылочном объекте, передаются назад в исходный объект Variant. Если тип этого объекта отличается от типа объекта, переданного в вызов, тип исходного объекта Variant изменяется и значение возвращается в этот измененный объект Variant. И снова, тип объекта Variant, переданного в вызов, может отличаться от типа объекта Variant, возвращенного из этого вызова.
Стандартные характеристики маршалинга типа Variant с установленным флагом VT_BYREF
Для объекта типа Variant, переданного в управляемый код по значению, может быть установлен флаг VT_BYREF, показывающий, что этот объект типа Variant содержит не значение, а ссылку. В этом случае маршалинг объекта типа Variant все еще выполняется в объект, потому что этот объект типа Variant передан по значению. Упаковщик автоматически разыменовывает содержимое объекта типа Variant и перед вызовом копирует его в заново созданный объект. Затем этот объект передается в управляемую функцию, но при возврате из вызова объект не передается обратно в исходный объект типа variant. Изменения, сделанные в управляемом объекте, теряются.
Внимание Не существует способа изменить значение объекта типа Variant, переданного по значению, даже если для него установлен флаг VT_BYREF.
Для объекта типа Variant, переданного в управляемый код по ссылке, может также быть установлен флаг VT_BYREF, показывающий, что этот объект типа Variant содержит другую ссылку. В этом случае маршалинг такого объекта типа Variant выполняется в объект ref, потому что этот объект типа Variant передан по ссылке. Упаковщик автоматически разыменовывает содержимое объекта типа Variant и перед вызовом копирует его в заново созданный объект. При возврате из вызова значение этого объекта передается обратно по ссылке в исходном объекте типа Variant, только если тип этого объекта совпадает с типом объекта, переданного в вызов. Т. е. обратная передача не изменяет тип Variant с установленным флагом VT_BYREF. Если тип этого объекта изменяется в течение вызова, при возвращении из вызова происходит исключение InvalidCastException.
В следующей таблице приведены правила распространения для типов Variant и объектов.
Исходный тип |
Конечный тип |
Изменения передаются обратно |
---|---|---|
Variant v |
Object o |
Нет |
Object o |
Variant v |
Нет |
Variant *pv |
Ref Object o |
Всегда |
Ref object o |
Variant *pv |
Всегда |
Variant v(VT_BYREF|VT_*) |
Object o |
Нет |
Variant v(VT_BYREF|VT_) |
Ref Object o |
Только если тип не был изменен. |
См. также
Основные понятия
Преобразуемые и непреобразуемые типы