Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье приводятся дополнительные замечания к справочной документации по этому 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 | Описание |
---|---|---|
Преобразование (из машинного кода в управляемый код) | 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. Этот static
метод вызывается слоем COM-взаимодействия в CLR для создания экземпляра пользовательского маршаллера. Строка, передаваемая в 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 определяет конкретную оболочку. Куки передаётся в метод
GetInstance
обработчика.
Атрибут MarshalAsAttribute определяет пользовательский маршализатор, чтобы он смог активировать соответствующую оболочку. Затем служба взаимодействия общей среды выполнения проверяет атрибут и создает пользовательский механизм маршалинга при первом случае, когда требуется маршалинг аргумента (параметра или поля).
Затем среда выполнения вызывает методы MarshalNativeToManaged и MarshalManagedToNative на пользовательском маршаллаторе, чтобы активировать нужную оболочку для обработки вызова.
Используйте пользовательский маршаллер
После завершения пользовательского обработчика маршаллинга его можно использовать в качестве пользовательской оболочки для определенного типа. В следующем примере показано определение управляемого IUserData
интерфейса:
interface IUserData
{
void DoSomeStuff(INew pINew);
}
Public Interface IUserData
Sub DoSomeStuff(pINew As INew)
End Interface
В следующем примере интерфейс IUserData
использует NewOldMarshaler
пользовательский маршализатор, чтобы позволить неуправляемым клиентским приложениям передавать интерфейс IOld
методу DoSomeStuff
. Управляемое описание метода DoSomeStuff
принимает интерфейс INew
, как показано в предыдущем примере, тогда как неуправляемая версия DoSomeStuff
принимает указатель интерфейса IOld
, как показано в следующем примере.
[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.CustomMarshalerUnmanagedType.CustomMarshaler
.
Второй параметр — это поле MarshalType, которое предоставляет полное имя сборки настраиваемого маршализатора. Это имя состоит из пространства имен и класса пользовательского маршаллера (MarshalType="MyCompany.NewOldMarshaler"
).