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


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

В приложении, целиком состоящем из управляемого кода, среда 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, не может быть выполнен.

Type []

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. [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;
}

См. также

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

Преобразуемые и непреобразуемые типы

Атрибуты направления

Копирование и закрепление

Другие ресурсы

Поведение маршалинга по умолчанию