Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Параметры и поля, типизированные как 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, DispatchWrapper, UnknownWrapper и 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.
Маршалирование вариантов в объект
При маршалинге варианта в объект тип, а иногда и значение маршаллированного варианта определяет тип создаваемого объекта. В следующей таблице определяется каждый тип варианта и соответствующий тип объекта, который создает маршаллер, когда вариант передается из 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 в управляемый код содержимое варианта копируется в только что созданный объект с помощью правил, определенных в Маршалинг варианта в объект. Изменения, внесенные в объект на управляемой стороне, не применяются к исходному варианту после возвращения из вызова.
Поведение по умолчанию для маршалинга объектов и вариантов по ссылке
Для распространения изменений к вызывающему объекту параметры необходимо передавать по ссылке. Например, ключевое 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 | Только если тип не был изменен. |