Изменения маршалинга
В следующих разделах представлен избранный набор изменений, которые можно выполнить для решения ряда конкретных проблем, возникающих в результате процесса импорта:
Массивы, совместимые со стилем языка 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 (программа экспорта библиотек типов)