Интерфейс System.Runtime.InteropServices.ICustomMarshaler
В этой статье приводятся дополнительные замечания к справочной документации по этому API.
Интерфейс ICustomMarshaler предоставляет пользовательские оболочки для обработки вызовов методов.
Маршаллизатор предоставляет мост между функциональностью старых и новых интерфейсов. Пользовательское маршалинг обеспечивает следующие преимущества:
- Он позволяет клиентским приложениям, предназначенным для работы со старым интерфейсом, также работать с серверами, реализующими новый интерфейс.
- Это позволяет клиентским приложениям работать с новым интерфейсом для работы с серверами, реализующими старый интерфейс.
Если у вас есть интерфейс, который вводит другое поведение маршалинга или который предоставляется объектной модели компонента (COM), можно создать пользовательский маршализатор вместо использования маршализатора взаимодействия. С помощью пользовательского маршаллатора можно свести к минимуму различие между новыми компонентами платформа .NET Framework и существующими COM-компонентами.
Например, предположим, что вы разрабатываете управляемый интерфейс INew
. Если этот интерфейс предоставляется com через стандартную вызываемую оболочку COM (CCW), он имеет те же методы, что и управляемый интерфейс и использует правила маршалинга, встроенные в маршализатор взаимодействия. Теперь предположим, что известный COM-интерфейс IOld
уже предоставляет те же функции, что INew
и интерфейс. Создав пользовательский маршаллировщик, вы можете предоставить неуправляемую реализацию IOld
, которая просто делегирует вызовы управляемой INew
реализации интерфейса. Поэтому пользовательский маршаллировщик выступает в качестве моста между управляемыми и неуправляемными интерфейсами.
Примечание.
Пользовательские маршаллеры не вызываются при вызове управляемого кода в неуправляемый код в интерфейсе только для отправки.
Определение типа маршалинга
Прежде чем создать пользовательский маршаллировщик, необходимо определить управляемые и неуправляемые интерфейсы, которые будут маршалированы. Эти интерфейсы обычно выполняют ту же функцию, но предоставляются по-разному для управляемых и неуправляемых объектов.
Управляемый компилятор создает управляемый интерфейс из метаданных, а полученный интерфейс выглядит как любой другой управляемый интерфейс. В следующем примере показан типичный интерфейс.
public interface INew
{
void NewMethod();
}
Public Interface INew
Sub NewMethod()
End Interface
Вы определяете неуправляемый тип в языке определения интерфейса (IDL) и компилируете его с помощью компилятора языка определения интерфейса (MIDL). Вы определяете интерфейс в инструкции библиотеки и назначаете ему идентификатор интерфейса с атрибутом универсального уникального идентификатора (UUID), как показано в следующем примере.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
[uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
interface IOld : IUnknown
HRESULT OldMethod();
}
Компилятор MIDL создает несколько выходных файлов. Если интерфейс определен в Old.idl, выходной файл Old_i.c определяет const
переменную с идентификатором интерфейса (IID) интерфейса, как показано в следующем примере.
const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};
Файл Old.h также создается MIDL. Он содержит определение интерфейса C++, которое может быть включено в исходный код C++ .
Реализация интерфейса ICustomMarshaler
Пользовательский маршаллировщик должен реализовать ICustomMarshaler интерфейс, чтобы предоставить соответствующие оболочки для среды выполнения.
В следующем коде C# отображается базовый интерфейс, который должен быть реализован всеми пользовательскими маршаллировщиками.
public interface ICustomMarshaler
{
Object MarshalNativeToManaged(IntPtr pNativeData);
IntPtr MarshalManagedToNative(Object ManagedObj);
void CleanUpNativeData(IntPtr pNativeData);
void CleanUpManagedData(Object ManagedObj);
int GetNativeDataSize();
}
Public Interface ICustomMarshaler
Function MarshalNativeToManaged( pNativeData As IntPtr ) As Object
Function MarshalManagedToNative( ManagedObj As Object ) As IntPtr
Sub CleanUpNativeData( pNativeData As IntPtr )
Sub CleanUpManagedData( ManagedObj As Object )
Function GetNativeDataSize() As Integer
End Interface
Интерфейс ICustomMarshaler включает методы, обеспечивающие поддержку преобразования, поддержку очистки и сведения о данных, которые необходимо маршалировать.
Тип операции | Метод ICustomMarshaler | Description |
---|---|---|
Преобразование (из машинного кода в управляемый код) | MarshalNativeToManaged | Маршалирует указатель на собственные данные в управляемый объект. Этот метод возвращает вызываемую оболочку среды выполнения (RCW), которая может маршалить неуправляемый интерфейс, передаваемый в качестве аргумента. Маршаллизатор должен возвращать экземпляр пользовательского RCW для этого типа. |
Преобразование (из управляемого в машинный код) | MarshalManagedToNative | Маршалирует управляемый объект в указатель на собственные данные. Этот метод возвращает настраиваемую вызываемую оболочку COM(CCW), которая может маршалал управляемого интерфейса, передаваемого в качестве аргумента. Маршаллизатор должен возвращать экземпляр пользовательского CCW для этого типа. |
Очистка (машинного кода) | CleanUpNativeData | Позволяет маршаллировщику очистить собственные данные (CCW), возвращаемые методом MarshalManagedToNative . |
Очистка (управляемого кода) | CleanUpManagedData | Позволяет маршаллизатору очистить управляемые данные (RCW), возвращаемые методом MarshalNativeToManaged . |
Сведения (о машинный код) | GetNativeDataSize | Возвращает размер неуправляемых данных, которые необходимо маршалировать. |
Преобразование
ICustomMarshaler.MarshalNativeToManaged
Маршалирует указатель на собственные данные в управляемый объект. Этот метод возвращает вызываемую оболочку среды выполнения (RCW), которая может маршалить неуправляемый интерфейс, передаваемый в качестве аргумента. Маршаллизатор должен возвращать экземпляр пользовательского RCW для этого типа.
ICustomMarshaler.MarshalManagedToNative
Маршалирует управляемый объект в указатель на собственные данные. Этот метод возвращает настраиваемую вызываемую оболочку COM(CCW), которая может маршалал управляемого интерфейса, передаваемого в качестве аргумента. Маршаллизатор должен возвращать экземпляр пользовательского CCW для этого типа.
Очистка
ICustomMarshaler.CleanUpNativeData
Позволяет маршаллировщику очистить собственные данные (CCW), возвращаемые методом MarshalManagedToNative .
ICustomMarshaler.CleanUpManagedData
Позволяет маршаллизатору очистить управляемые данные (RCW), возвращаемые методом MarshalNativeToManaged .
Сведения о размере
ICustomMarshaler.GetNativeDataSize
Возвращает размер неуправляемых данных, которые необходимо маршалировать.
Примечание.
Если пользовательский маршаллировщик вызывает любые методы, которые задают последнюю ошибку P/Invoke при маршалинге из собственного кода в управляемый или при очистке, значение Marshal.GetLastWin32Error() , возвращаемое и Marshal.GetLastPInvokeError() будет представлять вызов в вызовах маршалинга или очистки. Это может привести к пропуску ошибок при использовании пользовательских маршаллеров с P/Invokes с DllImportAttribute.SetLastError заданным значением true
. Чтобы сохранить последнюю ошибку P/Invoke, используйте Marshal.GetLastPInvokeError()Marshal.SetLastPInvokeError(Int32) методы в ICustomMarshaler реализации.
Реализация метода GetInstance
Помимо реализации ICustomMarshaler интерфейса настраиваемые маршаллеры должны реализовать static
метод, который GetInstance
принимает в String качестве параметра и имеет тип возвращаемого ICustomMarshalerзначения. Этот static
метод вызывается уровнем COM-взаимодействия среды CLR для создания экземпляра пользовательского маршаллатора. Передаваемая строка представляет собой файл cookie, который GetInstance
метод может использовать для настройки возвращаемого пользовательского маршаллера. В следующем примере показана минимальная, но полная реализация ICustomMarshaler .
public class NewOldMarshaler : ICustomMarshaler
{
public static ICustomMarshaler GetInstance(string pstrCookie)
=> new NewOldMarshaler();
public Object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException();
public IntPtr MarshalManagedToNative(Object ManagedObj) => throw new NotImplementedException();
public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException();
public void CleanUpManagedData(Object ManagedObj) => throw new NotImplementedException();
public int GetNativeDataSize() => throw new NotImplementedException();
}
Применение marshalAsAttribute
Чтобы использовать пользовательский маршализатор, необходимо применить MarshalAsAttribute атрибут к параметру или полю, который маршалируется.
Необходимо также передать UnmanagedType.CustomMarshaler значение перечисления конструктору MarshalAsAttribute . Кроме того, необходимо указать MarshalType поле с одним из следующих именованных параметров:
MarshalType (обязательно): полное имя настраиваемого маршала. Имя должно содержать пространство имен и класс пользовательского маршаллатора. Если пользовательский маршаллировщик не определен в сборке, в которой он используется, необходимо указать имя сборки, в которой она определена.
Примечание.
Вместо поля можно использовать MarshalTypeRef поле MarshalType . MarshalTypeRef принимает тип, который проще указать.
MarshalCookie (необязательно): файл cookie, передаваемый пользовательскому маршалу. Файл cookie можно использовать для предоставления дополнительных сведений маршаллизатору. Например, если тот же маршаллизатор используется для предоставления ряда оболочк, файл cookie определяет конкретную оболочку. Файл cookie передается
GetInstance
методу маршаллатора.
Атрибут MarshalAsAttribute определяет пользовательский маршализатор, чтобы он смог активировать соответствующую оболочку. Затем служба взаимодействия среды cl language проверяет атрибут и создает пользовательский маршализатор при первом маршале аргумента (параметр или поле).
Затем среда выполнения вызывает MarshalNativeToManaged методы и MarshalManagedToNative методы пользовательского маршаллатора, чтобы активировать правильную оболочку для обработки вызова.
Использование пользовательского маршаллатора
После завершения пользовательского маршаллатора его можно использовать в качестве пользовательской оболочки для определенного типа. В следующем примере показано определение управляемого IUserData
интерфейса:
interface IUserData
{
void DoSomeStuff(INew pINew);
}
Public Interface IUserData
Sub DoSomeStuff(pINew As INew)
End Interface
В следующем примере интерфейс использует NewOldMarshaler
пользовательский маршализатор для IUserData
включения неуправляемых клиентских приложений для передачи IOld
интерфейса методуDoSomeStuff
. Управляемое описание DoSomeStuff
метода принимает интерфейс, как показано в предыдущем примере, в то время как неуправляемая версия DoSomeStuff
IOld
принимает INew
указатель интерфейса, как показано в следующем примере.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
[uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
interface IUserData : IUnknown
HRESULT DoSomeStuff(IUnknown* pIOld);
}
Библиотека типов, созданная при экспорте управляемого IUserData
определения, дает неуправляемые определения, показанные в этом примере вместо стандартного определения. Атрибут, MarshalAsAttribute применяемый к INew
аргументу в управляемом определении DoSomeStuff
метода, указывает, что аргумент использует пользовательский маршализатор, как показано в следующем примере.
using System.Runtime.InteropServices;
Imports System.Runtime.InteropServices
interface IUserData
{
void DoSomeStuff(
[MarshalAs(UnmanagedType.CustomMarshaler,
MarshalType="NewOldMarshaler")]
INew pINew
);
}
Public Interface IUserData
Sub DoSomeStuff( _
<MarshalAs(UnmanagedType.CustomMarshaler, _
MarshalType := "MyCompany.NewOldMarshaler")> pINew As INew)
End Interface
В предыдущих примерах первый параметр, предоставленный MarshalAsAttribute атрибуту, является UnmanagedType.CustomMarshaler значение UnmanagedType.CustomMarshaler
перечисления.
Второй параметр — это MarshalType поле, которое предоставляет полное имя настраиваемого маршала. Это имя состоит из пространства имен и класса пользовательского маршаллатора (MarshalType="MyCompany.NewOldMarshaler"
).