Маршалинг по умолчанию для массивов
Если приложение полностью состоит из управляемого кода, общеязыковая среда выполнения (CLR) передает типы массивов в качестве параметров ввода-вывода. В отличие от этого, маршализатор взаимодействия передает массив, как в параметрах по умолчанию.
В рамках оптимизации закрепления непреобразуемый массив может выступать в качестве параметра ввода-вывода при взаимодействии с объектами в том же подразделении. Тем не менее при последующем экспорте кода в библиотеку типов, используемую для создания учетной записи посредника между компьютерами, если эта библиотека используется для маршалинга вызовов между подразделениями, вызовы могут получать фактические характеристики параметра ввода.
Массивы имеют сложную структуру, поэтому для проведения различий между управляемыми и неуправляемыми массивами требуется больше информации, чем для других преобразуемых типов.
Управляемые массивы
Управляемые массивы могут иметь разные типы, однако базовым для всех этих типов является класс System.Array. Класс System.Array имеет свойства, определяющие ранг, длину, нижнюю и верхнюю границы массива, а также методы доступа, сортировки, поиска, копирования и создания массивов.
Эти типы массивов являются динамическими и не имеют соответствующих статических типов в библиотеке базовых классов. В качестве уникального типа массива удобно рассматривать сочетание типа элементов и ранга. Соответственно, тип одномерного массива целых чисел отличается от типа одномерного массива чисел типа double. Аналогичным образом, его тип будет отличаться от типа двухмерного массива целых чисел. При сравнении типов не учитываются границы массивов.
Как показано в следующей таблице, любой экземпляр управляемого массива должен иметь заданные тип элементов, ранг и нижнюю границу.
Тип управляемого массива | Тип элемента | Rank | Нижняя граница | Нотация сигнатуры |
---|---|---|---|---|
ELEMENT_TYPE_ARRAY | Задается по типу. | Задается по рангу. | При необходимости задается границами. | тип [ n,m ] |
ELEMENT_TYPE_CLASS | Неизвестно | Неизвестно | Неизвестно | System.Array |
ELEMENT_TYPE_SZARRAY | Задается по типу. | 1 | 0 | тип [ n ] |
Неуправляемые массивы
Неуправляемыми могут быть безопасные массивы в стиле COM или массивы в стиле C фиксированной или переменной длины. Безопасный массив — это описывающий сам себя массив, передающий тип, ранг и границы соответствующего массива данных. Массивы в стиле C — это одномерные типизированные массивы с фиксированной нижней границей, равной 0. Служба маршаллинга имеет ограниченную поддержку обоих типов массивов.
Передача параметров массива в код .NET
Массивы в стиле языка C и безопасные массивы могут передаваться в код .NET из неуправляемого кода в виде безопасных массивов или массивов в стиле C. В следующей таблице показаны значения неуправляемого типа и соответствующих импортируемых типов.
Неуправляемый тип | Импортируемый тип |
---|---|
SafeArray( Тип ) | ELEMENT_TYPE_SZARRAY < Преобразованный_тип > Ранг = 1, нижняя граница = 0. Размер известен только в том случае, если он предоставлен в управляемой сигнатуре. Безопасные массивы, которые не являются рангом = 1 или нижней границы = 0, нельзя маршалировать как SZARRAY. |
Type [] | ELEMENT_TYPE_SZARRAY < Преобразованный_тип > Ранг = 1, нижняя граница = 0. Размер известен только в том случае, если он предоставлен в управляемой сигнатуре. |
Безопасные массивы
При импорте безопасного массива из библиотеки типов в сборку .NET он преобразуется в одномерный массив известного типа (например, int). К элементам массива применяются те же правила преобразования типов, что и к параметрам. Например, безопасный массив типов BSTR преобразуется в управляемый массив строк, а безопасный массив вариантов — в управляемый массив объектов. Тип элементов 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. В случаях, когда передаваемый массив, как известно, является многомерным, можно изменить код общего промежуточного языка (CIL), созданный Tlbimp.exe, а затем перекомпилировать его. Дополнительные сведения об изменении кода CIL см. в разделе "Настройка вызываемых оболочки среды выполнения".
Массивы в стиле 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);
Несмотря на то, что с помощью атрибутов size_is или length_is массива в исходном коде на языке IDL можно передать сведения о размере клиенту, компилятор 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);
Маршаллировщик можно предоставить размеру массива, изменив код общего промежуточного языка (CIL), созданный Tlbimp.exe, а затем перекомпилируя его. Дополнительные сведения об изменении кода CIL см. в разделе "Настройка вызываемых оболочки среды выполнения". Чтобы указать число элементов в массиве, примените тип MarshalAsAttribute к параметру массива в определении управляемого метода одним из следующих способов:
Определите еще один параметр, который содержит число элементов в массиве. Параметры определяются по позиции, начиная с первого, который получает номер 0.
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 );
При маршалинге массивов из неуправляемого кода в управляемый код маршалер проверяет маршалAsAttribute , связанный с параметром, чтобы определить размер массива. Если размер массива не указан, то маршалируется только один элемент.
Примечание.
МаршалAsAttribute не влияет на маршалинг управляемых массивов в неуправляемый код. В этом направлении размер массива определяется с помощью проверки. Маршалинг подмножества управляемого массива невозможен.
Маршаллизатор взаимодействия использует методы CoTaskMemAlloc и CoTaskMemFree в Windows или malloc и бесплатные методы в других операционных системах для выделения и извлечения памяти. Эти методы также необходимо использовать при выделении памяти в неуправляемом коде.
Передача массивов в COM
Все типы управляемых массивов можно передавать в неуправляемый код из управляемого кода. В зависимости от управляемого типа и примененных к нему атрибутов, доступ к массиву осуществляется как к безопасному массиву или как к массиву в стиле C, как показано в следующей таблице.
Тип управляемого массива | Экспортируется как |
---|---|
ELEMENT_TYPE_SZARRAY < тип > | UnmanagedType .SafeArray( тип ) UnmanagedType.LPArray Тип предоставляется в сигнатуре. Ранг всегда равен 1, а нижняя граница всегда — 0. Размер всегда известен во время выполнения. |
ELEMENT_TYPE_ARRAY < тип > < ранг >[< границы >] | UnmanagedType.SafeArray( тип ) UnmanagedType.LPArray Тип, ранг и границы предоставляются в сигнатуре. Размер всегда известен во время выполнения. |
ELEMENT_TYPE_CLASS <System.Array> | UT_Interface UnmanagedType.SafeArray( тип ) Тип, ранг, границы и размер всегда известны во время выполнения. |
В OLE-автоматизации существует ограничение в отношении массивов структур, которые содержат LPSTR или LPWSTR. Поэтому строковые поля должны быть маршалированы как 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. Размер определяется во время выполнения на основе размера передаваемого управляемого массива.
Массив также можно маршалировать как массив стилей C с помощью атрибута MarshalAsAttribute . Например:
Управляемая сигнатура
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);
Ранг, размер и границы безопасного массива определяются во время выполнения на основе характеристик управляемого массива.
Массив также можно маршалировать как массив стилей C, применяя MarshalAsAttribute атрибут. Например:
Управляемая сигнатура
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 . При маршалле в качестве безопасного массива элементы массива маршалируются как варианты. Например:
Управляемая сигнатура
Sub New1( ar As System.Array )
Sub New2( <MarshalAs(UnmanagedType.SafeArray)> ar As System.Array )
void New1( System.Array ar );
void New2( [MarshalAs(UnmanagedType.SafeArray)] System.Array ar );
Неуправляемая сигнатура
HRESULT New([in] _Array *ar);
HRESULT New([in] SAFEARRAY(VARIANT) ar);
Массивы внутри структур
Неуправляемые структуры могут содержать вложенные массивы. По умолчанию эти внедренные поля массива маршалируются как SAFEARRAY. В следующем примере s1
— это вложенный массив, который выделяется непосредственно в структуре.
Неуправляемое представление
struct MyStruct {
short s1[128];
}
Массивы можно маршалировать как UnmanagedType, что требует задания MarshalAsAttribute поля. Размер может задаваться только в виде константы. В коде ниже показаны соответствующие управляемые определения 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;
}