Поделиться через


Изменения маршалинга

В следующих разделах представлен избранный набор изменений, которые можно выполнить для решения ряда конкретных проблем, возникающих в результате процесса импорта:

  • Массивы, совместимые со стилем языка C

  • Массивы ввода и вывода в стиле языка C

  • Многомерные массивы в стиле языка C

  • Параметр SAFEARRAY с ненулевой границей

  • Сохранение сигнатуры

  • Передача значения Null вместо ссылки на тип значения

В этих разделах не представлены все случаи редактирования сборки взаимодействия. Например, сборку взаимодействия можно также изменить для упрощения ее использования. Единственным способом определения необходимости настройки является реальное написание кода, использующего сборку взаимодействия. Инструкции по редактированию сборок взаимодействия см. в разделе Практическое руководство. Редактирование сборок взаимодействия.

На маршалинг влияет ситуация, когда клиент и сервер оказываются в несовместимых подразделениях. В следующих примерах большинство маршалируемых параметров не совместимо с автоматизацией и требует выполнения одного из следующих действий.

  • Убедиться, что и клиент, и сервер находятся в совместимых подразделениях (и, следовательно, не требуется никакого маршалинга COM).

  • Зарегистрировать прокси-сервер и заглушку, созданные из языка IDL. Регистрация библиотеки типов в этих случаях не помогает, потому что большая часть сведений, требующихся для маршалинга, не распространяется из языка IDL в библиотеку типов.

Массивы, совместимые со стилем языка C

Следующее объявление языка IDL описывает массив в стиле языка C.

HRESULT ConformantArray([in] int cElems, [in, size_is(cElems)] int 
aConf[]);

Поскольку этот тип не совместим с автоматизацией, сведения о размере массива, такие как связь между первым и вторым параметром, не могут быть представлены в библиотеке типов. Программа импорта библиотек типов (Tlbimp.exe) импортирует второй параметр как ссылку на целое число, а не как управляемый массив. Этот параметр можно откорректировать, изменив MSIL.

Найти MSIL для

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32& aConf) runtime managed internalcall

Заменить на

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32[] marshal([]) aConf) runtime managed internalcall

Для вызова из управляемого кода

int[] param1 = { 11, 22, 33 };
tstArrays.ConformantArray( 3, param1 );

Массивы ввода и вывода в стиле языка C

Следующее объявление языка IDL описывает массив ввода-вывода в стиле языка C.

HRESULT InOutArray([in, out] int* pcElems, [in, out, size_is(,*pcElems)] 
int** ppInOut);

В этом случае можно изменить размер массива и возвратить новый размер. Поскольку этот тип не совместим с автоматизацией, сведения о размере массива, такие как связь между первым и вторым параметром, не могут быть представлены в библиотеке типов. Программа Tlbimp.exe импортирует второй параметр как IntPtr. Хотя этот метод все же можно вызывать из управляемого кода, для изменения размера массива потребуется изменить MSIL и использовать методы из класса Marshal, чтобы выделить и освободить память вручную.

Найти MSIL для

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int ppInOut) runtime managed internalcall

Заменить на

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int& ppInOut) runtime managed internalcall

Для вызова из управляемого кода

int[] inArray = { 11, 22, 33 };
int arraySize = inArray.Length;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( typeof( int )) * inArray.Length );
Marshal.Copy( inArray, 0, buffer, inArray.Length );
tstArrays.InOutArray( ref arraySize, ref buffer );
if( arraySize > 0 )
{
int[] arrayRes = new int[ arraySize ];
Marshal.Copy( buffer, arrayRes, 0, arraySize );
Marshal.FreeCoTaskMem( buffer );
}

Многомерные массивы в стиле языка C

Следующее объявление языка IDL описывает двумерный массив в стиле языка C.

HRESULT TwoDimArray([in] int cDim, [in, size_is(cDim)] int aMatrix[][3]);

Поскольку этот тип несовместим с автоматизацией, сведения о размере и размерности массива, такие как связь между первым и вторым параметром, не могут быть представлены в библиотеке типов. Программа Tlbimp.exe импортирует второй параметр как тип IntPtr, а не как управляемый многомерный массив. Этот параметр можно откорректировать, изменив MSIL.

Найти MSIL для

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] native int aMatrix) runtime managed internalcall

Заменить на

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] int32[,] marshal([]) aMatrix) runtime managed internalcall

Для вызова из управляемого кода

int[,] param = {{ 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 }};
tstArrays.TwoDimArray( 3, param );

Параметр SAFEARRAY с ненулевой границей

Следующее объявление языка IDL показывает параметр SAFEARRAY.

HRESULT InSArray([in] SAFEARRAY(int) *ppsa);

Следует учесть, что этот параметр SAFEARRAY является массивом с ненулевой границей. В управляемом коде такие массивы представлены типом System.Array. Однако по умолчанию программа импорта преобразует все параметры SAFEARRAY в ссылки на управляемые массивы. Существует два варианта изменения поведения по умолчанию:

  • Импортировать все массивы в библиотеке типов как типы System.Array, использовав для этого программу Tlbimp.exe с ключом /sysarray.

  • Импортировать несколько параметров как типы System.Array, вручную изменив MSIL, как показано в следующем примере.

    Найти MSIL для

    .method public hidebysig newslot virtual 
    instance void  InSArray([in] int32[]&  marshal( safearray int) ppsa) runtime managed internalcall
    

    Заменить на

    .method public hidebysig newslot virtual 
    instance void  InSArray(class [mscorlib]System.Array& marshal( safearray) 
    ppsa) runtime managed internalcall
    

    Вызвать из управляемого кода

    int[] lengthsArray = new int[1] { 3 };   
    int[] boundsArray = new int[1] { -1 };
    Array param2 = Array.CreateInstance( typeof(int), lengthsArray, boundsArray );
    for( int i = param2.GetLowerBound( 0 ); i <= param2.GetUpperBound( 0 ); i++ )
    param2.SetValue( i * 10, i ); 
    sum = tstArrays.InSArray( ref param2 );
    

Сохранение сигнатуры

Следующее объявление языка IDL описывает сигнатуру COM-метода.

HRESULT TestPreserveSig2([in] int inParam, [out,retval] int* outParam);

Программа Tlbimp.exe изменяет сигнатуры COM-методов. Параметры, помеченные в IDL как [out, retval], становятся возвращаемыми значениями управляемых методов. Все значения HRESULT, обозначающие сбой, преобразуются в управляемые исключения. Иногда необходимо сохранить исходную сигнатуру COM-метода, например, когда этот метод возвращает нечто, отличающееся от значений HRESULT, полученный при успешном выполнении. В следующем управляемом представлении показан пример сигнатуры, которую можно изменить

Управляемое представление в MSIL

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam) runtime managed internalcall
{

Заменить на

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam, [out] int32& outParam) runtime managed internalcall preservesig

Просмотр возвращаемого значения HRESULT

int hr = tst.TestPreserveSig2( -3, out retValue );
Console.WriteLine( "Return value is {0}", retValue );
if( hr == 0 )
Console.WriteLine( "HRESULT = S_OK" );
else if ( hr == 1 )
Console.WriteLine( "HRESULT = S_FALSE" );
else
Console.WriteLine( "HRESULT = {0}", hr );

Передача значения Null вместо ссылки на тип значения

Следующее объявление языка IDL описывает указатель IDL на структуру.

HRESULT TestPassingNull([in, unique] Point* refParam);

Программа Tlbimp.exe импортирует этот параметр как ссылку на тип значения Point. В языках C# и Visual Basic 2005 пустую ссылку (Nothing в Visual Basic) нельзя передать как параметр, если ожидается ссылка на тип значения. Если COM-функция требует параметр null (Nothing), можно изменить эту сигнатуру, отредактировав MSIL.

Найти MSIL для

.method public hidebysig newslot virtual 
instance void  TestPassingNull(
[in] valuetype MiscSrv.tagPoint& refParam) 
runtime managed internalcall

Заменить на

.method public hidebysig newslot virtual 
instance void  TestPassingNull([in] native int) runtime managed internalcall

Измененная сигнатура позволяет передать значение null. Но когда нужно передать некоторые реальные значения, следует использовать методы класса Marshal, как показано в следующем примере.

tagPoint p = new tagPoint();
p.x = 3;
p.y = 9;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( p ));
Marshal.StructureToPtr( p, buffer, false );
tst.TestPassingNull( buffer );
Marshal.FreeCoTaskMem( buffer );
tst.TestPassingNull( IntPtr.Zero );

См. также

Задачи

Практическое руководство. Редактирование сборок взаимодействия

Практическое руководство. Создание оболочек вручную

Ссылки

Tlbimp.exe (программа экспорта библиотек типов)

Основные понятия

Настройка вызываемых оболочек времени выполнения

Типы данных COM

Настройка вызываемых оболочек COM