Partilhar via


Marshalling padrão para matrizes

Em um aplicativo que consiste inteiramente de código gerenciado, o common language runtime passa os tipos de matriz como parâmetros de entrada/saída. Em contraste, o marshaller de interoperabilidade passa uma matriz como In parâmetros por padrão.

Com a otimização de fixação, uma matriz blittable pode parecer operar como um parâmetro de entrada/saída ao interagir com objetos no mesmo apartamento. No entanto, se você exportar posteriormente o código para uma biblioteca de tipos usada para gerar o proxy entre máquinas e essa biblioteca for usada para organizar suas chamadas entre apartamentos, as chamadas poderão reverter para o comportamento verdadeiro do parâmetro In.

Os arrays são complexos por natureza, e as distinções entre arrays gerenciados e não gerenciados garantem mais informações do que outros tipos não blittable.

Arrays gerenciados

Os tipos de array gerenciado podem variar; no entanto, a System.Array classe é a classe base de todos os tipos de matriz. A classe System.Array tem propriedades para determinar a classificação, o comprimento e os limites inferior e superior de uma matriz, bem como métodos para acessar, classificar, pesquisar, copiar e criar matrizes.

Esses tipos de matriz são dinâmicos e não têm um tipo estático correspondente definido na biblioteca de classes base. É conveniente pensar em cada combinação de tipo de elemento e classificação como um tipo distinto de matriz. Portanto, uma matriz unidimensional de inteiros é de um tipo diferente de uma matriz unidimensional de tipos duplos. Da mesma forma, uma matriz bidimensional de inteiros é diferente de uma matriz unidimensional de inteiros. Os limites da matriz não são considerados ao comparar tipos.

Como mostra a tabela a seguir, qualquer instância de uma matriz gerenciada deve ser de um tipo de elemento, classificação e limite inferior específicos.

Tipo de array gerenciado Tipo de elemento Posição Limite inferior Notação de assinatura
ELEMENT_TYPE_ARRAY Especificado por tipo. Especificado por classificação. Opcionalmente especificado por limites. tipo [ n,m ]
ELEMENT_TYPE_CLASS Desconhecido Desconhecido Desconhecido System.Array
ELEMENT_TYPE_SZARRAY Especificado por tipo. 1 0 tipo [ n ]

Arrays não gerenciados

Matrizes não gerenciadas são matrizes seguras no estilo COM ou matrizes no estilo C com comprimento fixo ou variável. Matrizes seguras são matrizes autodesdescritas que carregam o tipo, a classificação e os limites dos dados de matriz associados. As matrizes de estilo C são matrizes unidimensionais tipadas com um limite inferior fixo de 0. O serviço de empacotamento tem suporte limitado para ambos os tipos de matrizes.

Passando parâmetros de matriz para o código .NET

Tanto as matrizes de estilo C quanto as matrizes seguras podem ser passadas para o código .NET a partir de código não gerenciado como uma matriz segura ou uma matriz de estilo C. A tabela a seguir mostra o valor do tipo não gerenciado e o tipo importado.

Tipo não gerenciado Tipo importado
SafeArray( Tipo ) < ELEMENT_TYPE_SZARRAY ConvertedType >

Rank = 1, limite inferior = 0. O tamanho só é conhecido se fornecido na assinatura gerenciada. Matrizes seguras que não são rank = 1 ou limite inferior = 0 não podem ser empacotadas como SZARRAY.
Tipo [] < ELEMENT_TYPE_SZARRAY ConvertedType >

Rank = 1, limite inferior = 0. O tamanho só é conhecido se fornecido na assinatura gerenciada.

Matrizes seguras

Quando uma matriz segura é importada de uma biblioteca de tipos para um assembly .NET, a matriz é convertida em uma matriz unidimensional de um tipo conhecido (como int). As mesmas regras de conversão de tipo que se aplicam a parâmetros também se aplicam a elementos de matriz. Por exemplo, uma matriz segura de tipos BSTR torna-se uma matriz gerenciada de cadeias de caracteres e uma matriz segura de variantes torna-se uma matriz gerenciada de objetos. O tipo de elemento SAFEARRAY é capturado da biblioteca de tipos e salvo no valor SAFEARRAY da UnmanagedType enumeração.

Como a classificação e os limites da matriz segura não podem ser determinados a partir da biblioteca de tipos, a classificação é assumida como igual a 1 e o limite inferior é assumido como igual a 0. A classificação e os limites devem ser definidos na assinatura gerenciada produzida pelo Importador de Biblioteca de Tipos (Tlbimp.exe). Se a classificação passada para o método em tempo de execução for diferente, um SafeArrayRankMismatchException é lançado. Se o tipo da matriz passada em tempo de execução for diferente, um SafeArrayTypeMismatchException será lançado. O exemplo a seguir mostra matrizes seguras em código gerenciado e não gerenciado.

Assinatura não gerenciada

HRESULT New1([in] SAFEARRAY( int ) ar);
HRESULT New2([in] SAFEARRAY( DATE ) ar);
HRESULT New3([in, out] SAFEARRAY( BSTR ) *ar);

Assinatura gerenciada

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);

Matrizes seguras multidimensionais ou não vinculadas a zero podem ser agrupadas em código gerenciado se a assinatura do método produzida por Tlbimp.exe for modificada para indicar um tipo de elemento de ELEMENT_TYPE_ARRAY em vez de ELEMENT_TYPE_SZARRAY. Como alternativa, você pode usar a opção /sysarray com Tlbimp.exe para importar todas as matrizes como System.Array objetos. Nos casos em que a matriz que está sendo passada é conhecida por ser multidimensional, você pode editar o código de linguagem intermediária comum (CIL) produzido por Tlbimp.exe e, em seguida, recompilá-lo. Para obter detalhes sobre como modificar o código CIL, consulte Customizing Runtime Callable Wrappers.

Matrizes estilo C

Quando uma matriz de estilo C é importada de uma biblioteca de tipos para um assembly .NET, a matriz é convertida em ELEMENT_TYPE_SZARRAY.

O tipo de elemento de matriz é determinado a partir da biblioteca de tipos e preservado durante a importação. As mesmas regras de conversão que se aplicam aos parâmetros também se aplicam aos elementos da matriz. Por exemplo, uma matriz de tipos LPStr torna-se uma matriz de tipos String. Tlbimp.exe captura o tipo de elemento de matriz e aplica o MarshalAsAttribute atributo ao parâmetro.

Presume-se que a classificação da matriz seja igual a 1. Se a classificação for maior que 1, a matriz é empacotada como uma matriz unidimensional em ordem coluna-maior. O limite inferior é sempre igual a 0.

As bibliotecas de tipos podem conter matrizes de comprimento fixo ou variável. Tlbimp.exe pode importar apenas matrizes de comprimento fixo de bibliotecas de tipos porque as bibliotecas de tipos não têm as informações necessárias para organizar matrizes de comprimento variável. Com matrizes de comprimento fixo, o tamanho é importado da biblioteca de tipos e capturado no MarshalAsAttribute que é aplicado ao parâmetro.

Você deve definir manualmente bibliotecas de tipos contendo matrizes de comprimento variável, conforme mostrado no exemplo a seguir.

Assinatura não gerenciada

HRESULT New1(int ar[10]);
HRESULT New2(double ar[10][20]);
HRESULT New3(LPWStr ar[10]);

Assinatura gerenciada

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);

Embora você possa aplicar os atributos size_is ou length_is a uma matriz na fonte IDL (Interface Definition Language) para transmitir o tamanho a um cliente, o compilador Microsoft Interface Definition Language (MIDL) não propaga essas informações para a biblioteca de tipos. Sem saber o tamanho, o serviço de interoperabilidade não pode organizar os elementos da matriz. Consequentemente, matrizes de comprimento variável são importadas como argumentos de referência. Por exemplo:

Assinatura não gerenciada

HRESULT New1(int ar[]);
HRESULT New2(int ArSize, [size_is(ArSize)] double ar[]);
HRESULT New3(int ElemCnt, [length_is(ElemCnt)] LPStr ar[]);

Assinatura gerenciada

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);

Você pode fornecer ao marshaller o tamanho da matriz editando o código de linguagem intermediária comum (CIL) produzido por Tlbimp.exe e, em seguida, recompilando-o. Para obter detalhes sobre como modificar o código CIL, consulte Customizing Runtime Callable Wrappers. Para indicar o número de elementos na matriz, aplique o MarshalAsAttribute tipo ao parâmetro array da definição de método gerenciado de uma das seguintes maneiras:

  • Identifique outro parâmetro que contenha o número de elementos na matriz. Os parâmetros são identificados por posição, começando com o primeiro parâmetro como número 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 );
    
  • Defina o tamanho da matriz como uma constante. Por exemplo:

    Sub [New](\<MarshalAs(UnmanagedType.LPArray, SizeConst:=128)> _
       ar() As Integer)
    
    void New(
       [MarshalAs(UnmanagedType.LPArray, SizeConst=128)] int[] ar );
    

Ao organizar matrizes de código não gerenciado para código gerenciado, o marshaller verifica o MarshalAsAttribute associado ao parâmetro para determinar o tamanho da matriz. Se o tamanho da matriz não for especificado, apenas um elemento será empacotado.

Nota

O MarshalAsAttribute não tem efeito sobre a empacotação de matrizes gerenciadas para código não gerenciado. Nessa direção, o tamanho da matriz é determinado pelo exame. Não há como organizar um subconjunto de uma matriz gerenciada.

O marshaller de interoperabilidade usa os métodos CoTaskMemAlloc e CoTaskMemFree no Windows ou malloc e métodos livres em outros sistemas operacionais para alocar e recuperar memória. A alocação de memória executada por código não gerenciado também deve usar esses métodos.

Passando matrizes para COM

Todos os tipos de array gerenciado podem ser passados para código não gerenciado a partir do código gerenciado. Dependendo do tipo gerenciado e dos atributos aplicados a ele, o array pode ser acessado como um array seguro ou um array estilo C, conforme mostrado na tabela a seguir.

Tipo de array gerenciado Exportado como
< ELEMENT_TYPE_SZARRAY tipo > UnmanagedType. SafeArray( tipo )

UnmanagedType.LPArray

O tipo é fornecido na assinatura. A classificação é sempre 1, o limite inferior é sempre 0. O tamanho é sempre conhecido em tempo de execução.
< ELEMENT_TYPE_ARRAY tipo > < rank >[ < limites ] > UnmanagedType.SafeArray( tipo )

UnmanagedType.LPArray

Tipo, classificação, limites são fornecidos na assinatura. O tamanho é sempre conhecido em tempo de execução.
ELEMENT_TYPE_CLASS <System.Array> UT_Interface

UnmanagedType.SafeArray( tipo )

Tipo, classificação, limites e tamanho são sempre conhecidos em tempo de execução.

Há uma limitação na automação OLE relacionada a matrizes de estruturas que contêm LPSTR ou LPWSTR. Portanto, os campos String devem ser empacotados como UnmanagedType.BSTR. Caso contrário, uma exceção será lançada.

ELEMENT_TYPE_SZARRAY

Quando um método que contém um parâmetro ELEMENT_TYPE_SZARRAY (matriz unidimensional) é exportado de um assembly .NET para uma biblioteca de tipos, o parâmetro array é convertido em um SAFEARRAY de um determinado tipo. As mesmas regras de conversão se aplicam aos tipos de elementos de matriz. O conteúdo da matriz gerenciada é copiado automaticamente da memória gerenciada para a SAFEARRAY. Por exemplo:

Assinatura gerenciada

Sub [New](ar() As Long)
Sub [New](ar() As String)
void New(long[] ar );
void New(String[] ar );

Assinatura não gerenciada

HRESULT New([in] SAFEARRAY( long ) ar);
HRESULT New([in] SAFEARRAY( BSTR ) ar);

A classificação das matrizes seguras é sempre 1 e o limite inferior é sempre 0. O tamanho é determinado em tempo de execução pelo tamanho da matriz gerenciada que está sendo passada.

A matriz também pode ser empacotada como uma matriz de estilo C usando o MarshalAsAttribute atributo. Por exemplo:

Assinatura gerenciada

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 );

Assinatura não gerenciada

HRESULT New(long ar[]);
HRESULT New(BSTR ar[]);
HRESULT New(LPStr ar[]);

Embora o marshaller tenha as informações de comprimento necessárias para organizar a matriz, o comprimento da matriz geralmente é passado como um argumento separado para transmitir o comprimento ao destinatário.

ELEMENT_TYPE_ARRAY

Quando um método que contém um parâmetro ELEMENT_TYPE_ARRAY é exportado de um assembly .NET para uma biblioteca de tipos, o parâmetro array é convertido em um SAFEARRAY de um determinado tipo. O conteúdo da matriz gerenciada é copiado automaticamente da memória gerenciada para a SAFEARRAY. Por exemplo:

Assinatura gerenciada

Sub [New](ar(,) As Long)
Sub [New](ar(,) As String)
void New( long [,] ar );
void New( String [,] ar );

Assinatura não gerenciada

HRESULT New([in] SAFEARRAY( long ) ar);
HRESULT New([in] SAFEARRAY( BSTR ) ar);

A classificação, o tamanho e os limites dos arrays seguros são determinados em tempo de execução pelas características do array gerenciado.

A matriz também pode ser empacotada como uma matriz de estilo C aplicando o MarshalAsAttribute atributo. Por exemplo:

Assinatura gerenciada

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 );

Assinatura não gerenciada

HRESULT New(long ar[]);
HRESULT New(LPStr ar[]);

As matrizes aninhadas não podem ser empacotadas. Por exemplo, a assinatura a seguir gera um erro quando exportada com o Exportador de Biblioteca de Tipos (Tlbexp.exe).

Assinatura gerenciada

Sub [New](ar()()() As Long)
void New(long [][][] ar );

<ELEMENT_TYPE_CLASS System.Array>

Quando um método que contém um System.Array parâmetro é exportado de um assembly .NET para uma biblioteca de tipos, o parâmetro array é convertido em uma interface _Array . O conteúdo da matriz gerenciada é acessível somente através dos métodos e propriedades da interface _Array. System.Array também pode ser empacotado como um SAFEARRAY usando o MarshalAsAttribute atributo . Quando empacotados como uma matriz segura, os elementos da matriz são agrupados como variantes. Por exemplo:

Assinatura gerenciada

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 );

Assinatura não gerenciada

HRESULT New([in] _Array *ar);
HRESULT New([in] SAFEARRAY(VARIANT) ar);

Matrizes dentro de estruturas

Estruturas não gerenciadas podem conter matrizes incorporadas. Por padrão, esses campos de matriz incorporados são empacotados como um SAFEARRAY. No exemplo a seguir, s1 é uma matriz incorporada que é alocada diretamente dentro da própria estrutura.

Representação não gerenciada

struct MyStruct {
    short s1[128];
}

As matrizes podem ser agrupadas como UnmanagedType, o que requer que você defina o MarshalAsAttribute campo. O tamanho pode ser definido apenas como uma constante. O código a seguir mostra a definição gerenciada correspondente de 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;
}

Consulte também