Бөлісу құралы:


Маршалинг по умолчанию для массивов

Если приложение полностью состоит из управляемого кода, общеязыковая среда выполнения (CLR) передает типы массивов в качестве параметров ввода-вывода. В отличие от этого, маршализатор взаимодействия по умолчанию передает массив в качестве входных параметров.

В рамках оптимизации закрепления непреобразуемый массив может выступать в качестве параметра ввода-вывода при взаимодействии с объектами в том же подразделении. Тем не менее, если позже вы экспортируете код в библиотеку типов, используемую для генерации прокси между машинами, и эта библиотека используется для маршалинга вызовов между апартаментами, вызовы могут вернуться к истинному поведению параметра типа In.

Массивы имеют сложную структуру, поэтому для проведения различий между управляемыми и неуправляемыми массивами требуется больше информации, чем для других преобразуемых типов.

Управляемые массивы

Управляемые массивы могут иметь разные типы, однако базовым для всех этих типов является класс System.Array. Класс System.Array имеет свойства, определяющие ранг, длину, нижнюю и верхнюю границы массива, а также методы доступа, сортировки, поиска, копирования и создания массивов.

Эти типы массивов являются динамическими и не имеют соответствующих статических типов в библиотеке базовых классов. В качестве уникального типа массива удобно рассматривать сочетание типа элементов и ранга. Соответственно, тип одномерного массива целых чисел отличается от типа одномерного массива чисел типа double. Аналогичным образом, двумерный массив целых чисел отличается от одномерного массива целых чисел. При сравнении типов не учитываются границы массивов.

Как показано в следующей таблице, любой экземпляр управляемого массива должен иметь заданные тип элементов, ранг и нижнюю границу.

Тип управляемого массива Тип элемента Ранг Нижняя граница Нотация сигнатуры
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.
Тип[] 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 );
    

При маршаллировании массивов из неуправляемого кода в управляемый код маршаллировщик проверяет MarshalAsAttribute, связанный с параметром, чтобы определить размер массива. Если размер массива не указан, то маршалируется только один элемент.

Примечание.

MarshalAsAttribute не оказывает влияния на передачу данных управляемых массивов в неуправляемый код. В этом направлении размер массива определяется путем анализа. Маршалинг подмножества управляемого массива невозможен.

Маршализатор взаимодействия использует методы CoTaskMemAlloc и CoTaskMemFree в Windows или методы malloc и free в других операционных системах для выделения и извлечения памяти. Эти методы также необходимо использовать при выделении памяти в неуправляемом коде.

Передача массивов в 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. Таким образом, 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. Размер определяется во время выполнения по размеру передаваемого управляемого массива.

Массив также можно маршалировать как массив в стиле 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;
}

См. также