Маршалинг по умолчанию для массивов
Обновлен: Ноябрь 2007
В приложении, целиком состоящем из управляемого кода, среда CLR передает типы массивов как параметры In/Out. В противоположность этому, упаковщик взаимодействия по умолчанию передает массив как параметры In.
При использовании оптимизации закрепления непреобразуемый тип при взаимодействии с объектами в том же подразделении может выглядеть (с точки зрения обработки) как параметр In/Out. Но, если позднее экспортировать этот код в библиотеку типов, использованную для создания межкомпьютерного посредника и использовать эту библиотеку для маршалинга вызовов между подразделениями, эти вызовы могут вернуться к истинному поведению параметра In.
Массивы сложны по своей природе, и различия между управляемым и неуправляемым массивами обуславливают необходимость большего количества данных, чем для других преобразуемых типов. В данном разделе представлены следующие сведения о маршалинге массивов.
Управляемые массивы
Неуправляемые массивы
Передача параметров массива в код .NET
Передача массивов в COM
Управляемые массивы
Типы управляемых массивов могут быть различны, но базовым классом для всех типов массивов является класс System.Array. В классе System.Array предусмотрены свойства для определения ранга, длины и верхней и нижней границ массива, а также методы для доступа, сортировки, поиска, копирования и создания массивов.
Эти типы массива являются динамическими и не имеют соответствующих статических типов, определенных в библиотеке базовых классов. Каждое сочетание типа элемента и ранга удобно представлять себе как отдельный тип массива. Таким образом, одномерный массив целых чисел и одномерный массив чисел двойной точности — это разные типы. Аналогично двумерный массив целых чисел отличается от одномерного массива целых чисел. При сравнении типов границы массива не учитываются.
Как показано в следующей таблице, любой экземпляр управляемого массива должен описываться определенным набором данных: тип элемента, ранг и нижняя граница.
Тип управляемого массива |
Тип элемента |
Ранг |
Нижняя граница |
Запись подписи |
---|---|---|---|---|
ELEMENT_TYPE_ARRAY |
Задается типом. |
Задается рангом. |
Задается границами (необязательно) |
type[n,m] |
ELEMENT_TYPE_CLASS |
Неизвестное значение |
Неизвестное значение |
Неизвестное значение |
System.Array |
ELEMENT_TYPE_SZARRAY |
Задается типом. |
1 |
0 |
type[n] |
Неуправляемые массивы
Неуправляемые массивы — это любые безопасные массивы в COM-стиле или массивы в стиле языка C с фиксированной или переменной длиной. Безопасные массивы — это массивы с самоописанием, хранящие данные о своем типе, ранге и границах связанного массива. Массивы стиля C — это одномерные массивы с определенным типом и фиксированной нижней границей, равной 0. Служба маршалинга имеет ограниченную поддержку для массивов обоих типов.
Передача параметров массива в код .NET
И массив в стиле языка C, и безопасный массив можно передать в код .NET из неуправляемого кода либо как безопасный массив, либо как массив в стиле языка C. В следующей таблице показаны значение неуправляемого типа и импортированный тип.
Неуправляемый тип |
Импортированный тип |
---|---|
SafeArray(Type) |
ELEMENT_TYPE_SZARRAY <ПреобразованныйТип> Ранг = 1, нижняя граница = 0. Размер известен, только если он предоставлен в управляемой подписи. Маршалинг в SZARRAY безопасных массивов, ранг которых не равна 1 или нижняя граница не равна 0, не может быть выполнен. |
Тип [] |
ELEMENT_TYPE_SZARRAY <ПреобразованныйТип> Ранг = 1, нижняя граница = 0. Размер известен, только если он предоставлен в управляемой подписи. |
Безопасные массивы
При импорте безопасного массива из библиотеки типов в сборку .NET он преобразуется в одномерный массив известного типа (такого как int). Те же самые правила преобразования типов, которые применяются для параметров, применимы и к элементам массива. Например, безопасный массив элементов типа BSTR становится управляемым массивом строк, а безопасный массив типа variant становится управляемым массивом объектов. Тип элемента SAFEARRAY извлекается и библиотеки типов и сохраняется в значении SAFEARRAY перечисления UnmanagedType.
Поскольку ранг и границы безопасного массива нельзя определить из библиотеки типов, ранг полагается равным 1, а нижняя граница — равной 0. Ранг и границы необходимо определить в управляемой подписи, создаваемой с помощью программы импорта библиотеки типов (Tlbimp.exe). В случае отличия ранг, переданного в метод во время выполнения, вызывается исключение SafeArrayRankMismatchException. Если тип массива, переданный во время выполнения, оказывается иным, вызывается исключение SafeArrayTypeMismatchException. В следующем примере показаны безопасные массивы в управляемом и неуправляемом кодах.
Неуправляемая подпись
HRESULT New1([in] SAFEARRAY( int ) ar);
HRESULT New2([in] SAFEARRAY( DATE ) ar);
HRESULT New3([in, out] SAFEARRAY( BSTR ) *ar);
Управляемая подпись
Sub New1(<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VT_I4)> _
ar() As Integer)
Sub New2(<MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VT_DATE)> _
ar() As DateTime)
Sub New3(ByRef <MarshalAs(UnmanagedType.SafeArray, SafeArraySubType:=VT_BSTR)> _
ar() As String)
void New1([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_I4)] int[] ar) ;
void New2([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_DATE)]
DateTime[] ar);
void New3([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_BSTR)]
ref String[] ar);
Маршалинг многомерных массивов или массивов с ненулевой нижней границей в управляемый код может быть выполнен, если подпись метода, полученная с помощью Tlbimp.exe, изменена таким образом, чтобы она показывала тип элемента ELEMENT_TYPE_ARRAY вместо ELEMENT_TYPE_SZARRAY. Либо можно использовать переключатель /sysarray с программой Tlbimp.exe, чтобы импортировать все массивы как объекты System.Array. В тех случаях, когда известно, что передаваемый массив является многомерным, можно исправить код на промежуточном языке MSIL, созданный Tlbimp.exe, и затем перекомпилировать его. Сведения об изменении кода на языке MSIL см. в разделе Настройка вызываемых оболочек времени выполнения.
Массивы в стиле языка C
При импорте массива в стиле языка C из библиотеки типов в сборку .NET он преобразуется в ELEMENT_TYPE_SZARRAY.
Тип элемента массива определяется из библиотеки типов и сохраняется в течение импорта. Те же самые правила, которые применяются для параметров, применимы и к элементам массива. Например, массив типов LPStr становится массивом типов String. Tlbimp.exe извлекает тип элементов массива и применяет к этому параметру атрибут MarshalAsAttribute.
Ранг массива предполагается равным 1. Если ранг больше 1, выполняется маршалинг в одномерный массив с соблюдением порядка основных столбцов. Нижняя граница всегда равна 0.
Библиотеки типов могут содержать массивы фиксированной или переменной длины. Tlbimp.exe может импортировать из библиотек типов только массивы фиксированной длины, потому что в библиотеках отсутствуют сведения, необходимые для маршалинга массивов переменной длины. Для массивов фиксированной длины размер импортируется из библиотеки типов и сохраняется в атрибуте MarshalAsAttribute, применяемом к параметру.
Библиотеки типов, содержащие массивы переменной длины, необходимо описать вручную, как показано в примере ниже.
Неуправляемая подпись
HRESULT New1(int ar[10]);
HRESULT New2(double ar[10][20]);
HRESULT New3(LPWStr ar[10]);
Управляемая подпись
Sub New1(<MarshalAs(UnmanagedType.LPArray, SizeConst=10)> _
ar() As Integer)
Sub New2(<MarshalAs(UnmanagedType.LPArray, SizeConst=200)> _
ar() As Double)
Sub New2(<MarshalAs(UnmanagedType.LPArray, _
ArraySubType=UnmanagedType.LPWStr, SizeConst=10)> _
ar() As String)
void New1([MarshalAs(UnmanagedType.LPArray, SizeConst=10)] int[] ar);
void New2([MarshalAs(UnmanagedType.LPArray, SizeConst=200)] double[] ar);
void New2([MarshalAs(UnmanagedType.LPArray,
ArraySubType=UnmanagedType.LPWStr, SizeConst=10)] String[] ar);
Хотя для передачи размера клиенту к массиву в исходном коде на языке IDL можно применить атрибуты size_is или length_is, компилятор языка MIDL не передает эти данные в библиотеку типов. Без знания размера служба маршалинга взаимодействия не может выполнить маршалинг элементов массива. Соответственно, массивы переменной длины импортируются как ссылочные аргументы. Пример.
Неуправляемая подпись
HRESULT New1(int ar[]);
HRESULT New2(int ArSize, [size_is(ArSize)] double ar[]);
HRESULT New3(int ElemCnt, [length_is(ElemCnt)] LPStr ar[]);
Управляемая подпись
Sub New1(ByRef ar As Integer)
Sub New2(ByRef ar As Double)
Sub New3(ByRef ar As String)
void New1(ref int ar);
void New2(ref double ar);
void New3(ref String ar);
Размер массива можно предоставить упаковщику, отредактировав код на промежуточном языке MSIL, полученный с помощью Tlbimp.exe, и затем перекомпилировав его. Сведения об изменении кода на языке MSIL см. в разделе Настройка вызываемых оболочек времени выполнения. Чтобы указать число элементов в массиве, примените MarshalAsAttribute к параметру массива в описании управляемого метода, сделав это одним из следующих способов:
Определите другой параметр, содержащий число элементов в массиве. Эти параметры идентифицируются по положению, начиная с первого параметра, которому присваивается значение 0. Контролируемый массив нельзя передавать как параметры ref или out. Аналогично должен быть передан передать по значению и параметр, содержащий размер массива (поле SizeParamIndex не может ссылаться на параметр ref или out). [Visual Basic]
Sub [New](ElemCnt As Integer, _ <MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> _ ar() As Integer)
void New( int ElemCnt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] int[] ar );
Определите размер массива как константу. Пример
Sub [New](<MarshalAs(UnmanagedType.LPArray, SizeConst:=128)> _ ar() As Integer)
void New( [MarshalAs(UnmanagedType.LPArray, SizeConst=128)] int[] ar );
При маршалинге массивов из неуправляемого в управляемый код упаковщик, чтобы определить размер массива, проверяет атрибут MarshalAsAttribute, связанный с параметром. Если размер массива не задан, выполняется маршалинг только одного элемента.
Примечание. |
---|
Атрибут MarshalAsAttribute не влияет на маршалинг управляемых массивов в неуправляемый код. В этом направлении размер массива определяется с помощью проверки. Не существует способа выполнить маршалинг части управляемого массива. |
Упаковщик взаимодействия использует методы CoTaskMemAlloc и CoTaskMemFreeдля выделения и освобождения памяти. Выделение памяти, выполняемое неуправляемым кодом, также должно использовать эти методы.
Передача массивов в COM
Все типы управляемых массивов могут передаваться в неуправляемый код из управляемого кода. Как показано в следующей таблице, в зависимости от управляемого типа и примененных к нему атрибутов, доступ к массиву может быть осуществлен как к безопасному массиву или как к массиву в стиле языка C.
Тип управляемого массива |
Экспортируется как |
---|---|
ELEMENT_TYPE_SZARRAY <тип> |
UnmanagedType.SafeArray(type) UnmanagedType.LPArray Тип предоставляется в подписи. Ранг всегда равен 1, нижняя граница всегда равна 0. Размер всегда известен во время выполнения. |
ELEMENT_TYPE_ARRAY <тип> <ранг>[<граница>] |
UnmanagedType.SafeArray(type) UnmanagedType.LPArray Тип, ранг и границы предоставляются в подписи. Размер всегда известен во время выполнения. |
ELEMENT_TYPE_CLASS <System.Array> |
UT_Interface UnmanagedType.SafeArray(type) Тип, ранг, границы и размер всегда известны во время выполнения. |
У OLE-автоматизации имеется ограничение, связанное с массивами структур, которые содержат LPSTR или LPWSTR. Поэтому поля String должны маршалироваться как UnmanagedType.BSTR. В противном случае будет создаваться исключение.
ELEMENT_TYPE_SZARRAY
При экспорте метода, содержащего параметр ELEMENT_TYPE_SZARRAY (одномерный массив) из сборки .NET в библиотеку типов, этот параметр массива преобразуется в SAFEARRAY заданного типа. Те же самые правила преобразования применимы к типам элемента массива. Содержимое управляемого массива автоматически копируется из управляемой памяти в SAFEARRAY. Пример.
Управляемая подпись
Sub [New](ar() As Long)
Sub [New](ar() As String)
void New(long[] ar );
void New(String[] ar );
Неуправляемая подпись
HRESULT New([in] SAFEARRAY( long ) ar);
HRESULT New([in] SAFEARRAY( BSTR ) ar);
Ранг безопасных массивов всегда равен 1. а нижняя граница всегда равна 0. Размер определяется во время выполнения по размеру передаваемого управляемого массива.
С помощью атрибута MarshalAsAttribute массив также может быть маршалирован как массив стиля C. Пример.
Управляемая подпись
Sub [New](<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> _
ar() As Long, size as Integer )
Sub [New](<MarshalAs(UnmanagedType.LPArray, SizeParamIndex:=1)> _
ar() As String, size as Integer )
Sub [New](<MarshalAs(UnmanagedType.LPArray, _
ArraySubType= UnmanagedType.LPStr, SizeParamIndex:=1)> _
ar() As String, size as Integer )
void New([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
long [] ar, int size );
void New([MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
String [] ar, int size );
void New([MarshalAs(UnmanagedType.LPArray, ArraySubType=
UnmanagedType.LPStr, SizeParamIndex=1)]
String [] ar, int size );
Неуправляемая подпись
HRESULT New(long ar[]);
HRESULT New(BSTR ar[]);
HRESULT New(LPStr ar[]);
Хотя этот упаковщик обладает сведениями о длине, необходимыми для маршалинга массива, длина массива обычно передается как отдельный аргумент, сообщающий эту длину вызываемому объекту.
ELEMENT_TYPE_ARRAY
При экспорте метода, содержащего параметр ELEMENT_TYPE_ARRAY из сборки .NET в библиотеку типов, этот параметр массива преобразуется в SAFEARRAY заданного типа. Содержимое управляемого массива автоматически копируется из управляемой памяти в SAFEARRAY. Пример.
Управляемая подпись
Sub [New]( ar(,) As Long )
Sub [New]( ar(,) As String )
void New( long [,] ar );
void New( String [,] ar );
Неуправляемая подпись
HRESULT New([in] SAFEARRAY( long ) ar);
HRESULT New([in] SAFEARRAY( BSTR ) ar);
Ранг, размер и границы безопасных массивов определяются во время выполнения из характеристик управляемого массива.
С помощью атрибута MarshalAsAttribute массив также может быть маршалирован как массив стиля C. Пример.
Управляемая подпись
Sub [New]( <MarshalAs(UnmanagedType.LPARRAY, SizeParamIndex:=1)> _
ar(,) As Long, size As Integer)
Sub [New]( <MarshalAs(UnmanagedType.LPARRAY, _
ArraySubType:=UnmanagedType.LPStr, SizeParamIndex:=1)> _
ar(,) As String, size As Integer)
void New([MarshalAs(UnmanagedType.LPARRAY, SizeParamIndex=1)]
long [,] ar, int size );
void New([MarshalAs(UnmanagedType.LPARRAY,
ArraySubType= UnmanagedType.LPStr, SizeParamIndex=1)]
String [,] ar, int size );
Неуправляемая подпись
HRESULT New(long ar[]);
HRESULT New(LPStr ar[]);
Маршалинг вложенных массивов невозможен. Например, следующая подпись вызовет ошибку при экспорте с помощью программы экспорта библиотеки типов (Tlbexp.exe).
Управляемая подпись
Sub [New]( ar()()() As Long )
void New(long [][][] ar );
ELEMENT_TYPE_CLASS <System.Array>
При экспорте метода, содержащего параметр System.Array, из сборки .NET в библиотеку типов, этот параметр массива преобразуется в интерфейс _Array. Содержимое этого управляемого массива доступно только через методы и свойства интерфейса _Array. System.Array также может быть маршалирован как SAFEARRAY с помощью атрибута MarshalAsAttribute. При маршалинге в качестве безопасного массива элементы массива упаковываются и передаются как объекты variant. Пример.
Управляемая подпись
Sub New1( ar As System.Array )
Sub New2( <MarshalAs(UnmanagedType.Safe array)> ar As System.Array )
void New1( System.Array ar );
void New2( [MarshalAs(UnmanagedType.Safe array)] System.Array ar );
Неуправляемая подпись
HRESULT New([in] _Array *ar);
HRESULT New([in] SAFEARRAY(VARIANT) ar);
Массивы внутри структур
Неуправляемые структуры могут содержать вложенные массивы. По умолчанию эти вложенные поля массива маршалируются как SAFEARRAY. В следующем примере s1 — это вложенный массив, распределенный непосредственно в самой структуре.
Неуправляемое представление
struct MyStruct {
short s1[128];
}
Массивы могут быть маршалированы как UnmanagedType.ByValArray, что требует от разработчика установки поля MarshalAsAttribute.SizeConst. Размер может быть передан только как константа. В следующем коде представлено соответствующее управляемое определение MyStruct.
Public Structure <StructLayout(LayoutKind.Sequential)> MyStruct
Public <MarshalAs(UnmanagedType.ByValArray, SizeConst := 128)> _
s1() As Short
End Structure
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct {
[MarshalAs(UnmanagedType.ByValArray, SizeConst=128)] public short[] s1;
}
См. также
Основные понятия
Преобразуемые и непреобразуемые типы