Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Параметры и поля, типизированные как System.Object, могут предоставляться в неуправляемый код в виде одного из следующих типов:
Вариант, если объект является параметром.
Интерфейс, если объект является полем структуры.
Только COM-взаимодействие поддерживает маршаллирование для типов объектов. По умолчанию выполняется маршалинг объектов в варианты COM. Эти правила применяются только к типу Object и не применяются к строго типизированному объекту, который является производным от класса Object.
Параметры маршаллинга
В следующей таблице показаны возможности для маршалинга типа данных Object. Атрибут MarshalAsAttribute предоставляет несколько значений перечисления UnmanagedType для маршалинга объектов.
| Тип перечисления | Описание неуправляемого формата |
|---|---|
|
UnmanagedType.Struct (по умолчанию для параметров) |
Вариант в стиле 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)
}
Примечание.
Маршализатор взаимодействия автоматически освобождает любой выделенный объект внутри варианта после вызова.
В следующем примере показан форматированный тип значения.
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) или IUnknown (unmanagedType.IUnknown) в результирующей библиотеке типов. COM-клиенты могут динамически вызывать члены управляемого класса или любые члены, реализованные производными классами, через интерфейс _Object. Клиент также может вызвать QueryInterface, чтобы получить любой другой интерфейс, явно реализованный управляемым типом.
Маршалирование объекта в variant
Когда объект маршалируется в вариант, внутренний тип варианта определяется во время выполнения программы на основе следующих правил:
Если ссылка на объект имеет значение NULL (Nothing в Visual Basic), объект маршалируется в вариант типа VT_EMPTY.
Если объект является экземпляром любого типа, указанного в следующей таблице, результирующий тип варианта определяется правилами, встроенными в маршаллировщик и показанными в таблице.
Другие объекты, которые должны явно контролировать поведение маршаллинга, могут реализовать IConvertible интерфейс. В этом случае тип варианта определяется в соответствии с кодом типа, возвращаемым из метода IConvertible.GetTypeCode. В противном случае объект маршалируется как вариант типа VT_UNKNOWN.
Маршалирование типов систем в variant
В следующей таблице показаны управляемые типы объектов и соответствующие им варианты модели COM. Эти типы преобразуются только в том случае, если сигнатура вызываемого метода относится к типу System.Object.
| Тип объекта | Тип варианта COM |
|---|---|
| Ссылка на объект NULL (Nothing в Visual Basic). | VT_EMPTY |
| System.DBNull | VT_NULL |
| System.Runtime.InteropServices.ErrorWrapper | VT_ERROR |
| System.Reflection.Missing | VT_ERROR с 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 |
В следующем примере кода с помощью интерфейса MarshalObject демонстрируется передача различных типов вариантов на 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, , DispatchWrapperUnknownWrapperи CurrencyWrapper. В следующем примере кода демонстрируется использование этих классов-оболочек для передачи различных типов вариантов на 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 интерфейс, тип COM-варианта определяется во время выполнения по значению TypeCode перечисления, возвращаемого методом IConvertible.GetTypeCode .
В следующей таблице показаны возможные значения перечисления TypeCode и соответствующего типа COM-варианта для каждого значения.
| Код типа | Тип варианта 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 |
Значение COM-варианта определяется путем вызова метода интерфейса IConvertible.ToType, где Toтип — это подпрограмма преобразования, соответствующая типу, возвращенному из IConvertible.GetTypeCode. Например, объект, возвращающий TypeCode.Double из IConvertible.GetTypeCode, маршалируется как COM-вариант типа VT_R8. Вы можете получить значение варианта (хранящегося в поле dblVal COM-варианта), приведя его к интерфейсу IConvertible и вызвав метод ToDouble.
Маршалирование вариантов в объект
При маршалинге варианта в объект тип, а иногда и значение маршаллированного варианта определяет тип создаваемого объекта. В следующей таблице определяется каждый тип варианта и соответствующий тип объекта, который маршаллерирует m, когда вариант передается из COM в платформа .NET Framework.
| Тип варианта COM | Тип объекта |
|---|---|
| VT_EMPTY | Ссылка на объект NULL (Nothing в Visual Basic). |
| VT_NULL | System.DBNull |
| VT_DISPATCH | System.__ComObject или значение NULL если (pdispVal == null) |
| VT_UNKNOWN | System.__ComObject или значение NULL если (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 | Соответствующий тип упакованного значения. |
| VT_VARIANT | Не поддерживается. |
Типы вариантов, передаваемые из модели COM в управляемый код и обратно в COM, могут не сохранять тип варианта во время вызова. Рассмотрим, что происходит, когда вариант типа VT_DISPATCH передается из COM в .NET Framework. Во время маршаллинга вариант преобразуется в .System.Object Если Object затем передается обратно в COM, он маршалируется обратно в вариант типа VT_UNKNOWN. Нет никаких гарантий, что вариант, созданный при маршалинге объекта из управляемого кода в COM, будет таким же типом, как и вариант, изначально используемый для создания объекта.
Маршаллирование вариантов ByRef
Хотя сами варианты могут передаваться по значению или по ссылке, флаг также можно использовать с любым типом варианта, VT_BYREF чтобы указать, что содержимое варианта передается по ссылке, а не по значению. Разница между маршаллингом вариантами по ссылке и маршаллингом варианта с установленным флагом VT_BYREF может быть запутанной. На следующем рисунке показаны различия между этими способами:
Варианты, передаваемые по значению и по ссылке
Поведение по умолчанию для маршалинга объектов и вариантов по значению
При передаче объектов из управляемого кода в COM содержимое объекта копируется в новый вариант, созданный маршаллизатором, с помощью правил, определенных в объекте Маршаллинга в Variant. Изменения, внесенные в вариант в неуправляемом коде, не применяются к исходному объекту после возврата из вызова.
При передаче вариантов из COM в управляемый код содержимое варианта копируется в только что созданный объект с помощью правил, определенных в маршалинга varianting Variant to Object. Изменения, внесенные в объект в управляемом коде, не применяются к исходному варианту после возврата из вызова.
Поведение по умолчанию для маршалинга объектов и вариантов по ссылке
Для распространения изменений в вызывающем объекте параметры необходимо передавать по ссылке. Например, ключевое ref слово в C# (или ByRef управляемом коде Visual Basic) можно использовать для передачи параметров по ссылке. В COM параметры ссылок передаются с помощью указателя, например варианта *.
При передаче объекта в COM по ссылке маршаллизатор создает новый вариант и копирует содержимое ссылки на объект в вариант перед вызовом. Вариант передается в неуправляемую функцию, в которой пользователь может изменять его содержимое. После возврата из вызова изменения, внесенные в вариант в неуправляемом коде, применяются к исходному объекту. Если тип варианта отличается от типа варианта, переданного в вызов, изменения применяются к объекту другого типа. Таким образом, тип переданного в вызов объекта может отличаться от типа объекта, возвращаемого из вызова.
При передаче варианта управляемому коду по ссылке маршаллизатор создает новый объект и копирует содержимое варианта в объект перед вызовом. Ссылка на объект передается в управляемую функцию, в которой пользователь может изменять сам объект. После возврата из вызова изменения, внесенные в указываемый по ссылке объект, применяются к исходному варианту. Если тип объекта отличается от типа объекта, переданного в вызов, тип исходного варианта изменяется и значение передается обратно в вариант. Аналогичным образом, тип переданного в вызов варианта может отличаться от типа варианта, возвращаемого из вызова.
Поведение по умолчанию для маршалирования варианта с набором флагов VT_BYREF
Вариант, передаваемый в управляемый код по значению, может иметь
VT_BYREFфлаг, указывающий, что вариант содержит ссылку вместо значения. В этом случае вариант по-прежнему маршалируется объекту, так как вариант передается по значению. Маршаллизатор автоматически разыменовывает содержимое варианта и копирует его в только что созданный объект перед вызовом. После этого объект передается в управляемую функцию. Тем не менее при возврате из вызова объект не применяется к исходному варианту. Изменения, внесенные в управляемый объект, утрачиваются.Внимание
Изменить значение варианта, передаваемого по значению, невозможно, даже если у него установлен флаг
VT_BYREF.Вариант, передаваемый в управляемый код по ссылке, также может иметь флаг
VT_BYREF, указывающий, что вариант содержит другую ссылку. Если это так, вариант маршалируется кrefобъекту, так как вариант передаётся по ссылке. Маршаллизатор автоматически разыменовывает содержимое варианта и копирует его в только что созданный объект перед вызовом. При возврате из вызова значение объекта возвращается по ссылке с исходным вариантом только в том случае, если тип объекта совпадает с типом переданного объекта. То есть распространение не изменяет тип варианта с набором флаговVT_BYREF. Если во время вызова тип объекта изменяется, при возврате из него возникает исключение InvalidCastException.
В следующей таблице описываются общие правила распространения для вариантов и объектов.
| С дт. | По | Возвращаемые изменения |
|---|---|---|
| Variantv | Объектo | Никогда |
| Объектo | Variantv | Никогда |
| Вариант*pv | Объект Refo | Всегда |
| Объект Refo | Вариант*pv | Всегда |
| Variantv(VT_BYREF VT_|*) | Объектo | Никогда |
| Variantv(VT_BYREF VT_|) | Объект Refo | Только если тип не был изменен. |