Domyślne marshalling dla tablic

W aplikacji składającej się całkowicie z kodu zarządzanego środowisko uruchomieniowe języka wspólnego przekazuje typy tablic jako parametry in/out. Z kolei marshaller międzyoperajowy przekazuje tablicę jako Domyślnie w parametrach.

W przypadku optymalizacji przypinania tablica blittable może wydawać się działać jako parametr in/out podczas interakcji z obiektami w tym samym mieszkaniu. Jeśli jednak później wyeksportujesz kod do biblioteki typów używanej do generowania serwera proxy między maszynami i ta biblioteka jest używana do marshalingu wywołań między apartamentami, wywołania mogą przywrócić wartość true W zachowaniu parametru.

Tablice są złożone z natury, a różnice między zarządzanymi i niezarządzanych tablicami uzasadniają więcej informacji niż inne typy nienależące do blittable.

Tablice zarządzane

Typy tablic zarządzanych mogą się różnić; System.Array jednak klasa jest klasą bazową wszystkich typów tablic. Klasa System.Array ma właściwości służące do określania rangi, długości i dolnej i górnej granicy tablicy, a także metod uzyskiwania dostępu do tablic, sortowania, wyszukiwania, kopiowania i tworzenia tablic.

Te typy tablic są dynamiczne i nie mają odpowiedniego typu statycznego zdefiniowanego w bibliotece klas bazowych. Wygodne jest myślenie o każdej kombinacji typu elementu i klasyfikacji jako odrębnego typu tablicy. W związku z tym jednowymiarowa tablica liczb całkowitych jest innego typu niż jednowymiarowa tablica podwójnych typów. Podobnie dwuwymiarowa tablica liczb całkowitych różni się od jednowymiarowej tablicy liczb całkowitych. Granice tablicy nie są brane pod uwagę podczas porównywania typów.

Jak pokazano w poniższej tabeli, każde wystąpienie tablicy zarządzanej musi mieć określony typ elementu, rangę i dolną granicę.

Typ tablicy zarządzanej Typ elementu Ranga Dolna granica Notacja podpisu
ELEMENT_TYPE_ARRAY Określony przez typ. Określony przez rangę. Opcjonalnie określone przez granice. type[n,m]
ELEMENT_TYPE_CLASS Nieznane Nieznane Nieznane System.array
ELEMENT_TYPE_SZARRAY Określony przez typ. 1 0 type[n]

Tablice niezarządzane

Tablice niezarządzane to bezpieczne tablice typu COM lub tablice w stylu C ze stałą lub zmienną długością. Sejf tablice to samoopisujące się tablice, które mają typ, rangę i granice skojarzonej tablicy. Tablice w stylu C to tablice jednowymiarowe z stałą dolną granicą 0. Usługa marshalling ma ograniczoną obsługę obu typów tablic.

Przekazywanie parametrów tablicy do kodu platformy .NET

Zarówno tablice w stylu C, jak i bezpieczne tablice mogą być przekazywane do kodu platformy .NET z niezarządzanego kodu jako bezpiecznej tablicy lub tablicy w stylu C. W poniższej tabeli przedstawiono niezarządzaną wartość typu i zaimportowany typ.

Typ niezarządzany Zaimportowany typ
Sejf Array(typ) <ELEMENT_TYPE_SZARRAY ConvertedType>

Ranga = 1, dolna granica = 0. Rozmiar jest znany tylko w przypadku podania w podpisie zarządzanym. Sejf tablice, które nie są rangą = 1 lub dolna granica = 0, nie mogą być ułożone jako SZARRAY.
Typ[] <ELEMENT_TYPE_SZARRAY ConvertedType>

Ranga = 1, dolna granica = 0. Rozmiar jest znany tylko w przypadku podania w podpisie zarządzanym.

tablice Sejf

Gdy bezpieczna tablica jest importowana z biblioteki typów do zestawu .NET, tablica jest konwertowana na jednowymiarową tablicę znanego typu (na przykład int). Te same reguły konwersji typów, które mają zastosowanie do parametrów, mają również zastosowanie do elementów tablicy. Na przykład bezpieczna tablica typów BSTR staje się zarządzaną tablicą ciągów, a bezpieczna tablica wariantów staje się zarządzaną tablicą obiektów. Typ elementu SAFEARRAY jest przechwytywany z biblioteki typów i zapisywany w wartości UnmanagedType SAFEARRAY wyliczenia.

Ponieważ nie można określić rangi i granic bezpiecznej tablicy z biblioteki typów, przyjmuje się, że ranga jest równa 1, a dolna granica jest równa 0. Ranga i granice muszą być zdefiniowane w podpisie zarządzanym utworzonym przez importera biblioteki typów (Tlbimp.exe). Jeśli ranga przekazana do metody w czasie wykonywania różni się, SafeArrayRankMismatchException zwracana jest wartość . Jeśli typ tablicy przekazanej w czasie wykonywania różni się, SafeArrayTypeMismatchException zwracany jest parametr . W poniższym przykładzie przedstawiono bezpieczne tablice w kodzie zarządzanym i niezarządzanych.

Podpis niezarządzany

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

Podpis zarządzany

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

Wielowymiarowe lub niezerowo powiązane bezpieczne tablice mogą być przekształcane w kod zarządzany, jeśli sygnatura metody utworzona przez Tlbimp.exe jest modyfikowana w celu wskazania typu elementu ELEMENT_TYPE_ARRAY zamiast ELEMENT_TYPE_SZARRAY. Alternatywnie możesz użyć przełącznika /sysarray z Tlbimp.exe, aby zaimportować wszystkie tablice jako System.Array obiekty. W przypadkach, gdy przekazywana tablica jest znana jako wielowymiarowa, można edytować kod wspólnego języka pośredniego (CIL) utworzony przez Tlbimp.exe, a następnie ponownie go skompilować. Aby uzyskać szczegółowe informacje na temat modyfikowania kodu CIL, zobacz Dostosowywanie otoek wywoływanych w czasie wykonywania.

Tablice w stylu C

Gdy tablica w stylu C jest importowana z biblioteki typów do zestawu platformy .NET, tablica jest konwertowana na ELEMENT_TYPE_SZARRAY.

Typ elementu tablicy jest określany z biblioteki typów i zachowywany podczas importowania. Te same reguły konwersji, które mają zastosowanie do parametrów, mają również zastosowanie do elementów tablicy. Na przykład tablica typów LPStr staje się tablicą typów Ciąg. Tlbimp.exe przechwytuje typ elementu tablicy i stosuje MarshalAsAttribute atrybut do parametru.

Przyjmuje się, że ranga tablicy jest równa 1. Jeśli ranga jest większa niż 1, tablica jest określana jako tablica jednowymiarowa w kolejności głównej kolumny. Dolna granica zawsze jest równa 0.

Biblioteki typów mogą zawierać tablice o stałej lub zmiennej długości. Tlbimp.exe można importować tylko tablice o stałej długości z bibliotek typów, ponieważ biblioteki typów nie zawierają informacji potrzebnych do marshalingu tablic o zmiennej długości. W przypadku tablic o stałej długości rozmiar jest importowany z biblioteki typów i przechwycony w elemecie MarshalAsAttribute zastosowanym do parametru.

Należy ręcznie zdefiniować biblioteki typów zawierające tablice o zmiennej długości, jak pokazano w poniższym przykładzie.

Podpis niezarządzany

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

Podpis zarządzany

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

Mimo że można zastosować atrybuty size_is lub length_is do tablicy w źródle języka IDL (Interface Definition Language) w celu przekazania rozmiaru klientowi, kompilator języka MICROSOFT Interface Definition Language (MIDL) nie propaguje tych informacji do biblioteki typów. Bez znajomości rozmiaru usługa międzyoperacyjnej nie może marshalingu elementów tablicy. W związku z tym tablice o zmiennej długości są importowane jako argumenty odwołania. Na przykład:

Podpis niezarządzany

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

Podpis zarządzany

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

Możesz podać marshaller z rozmiarem tablicy, edytując kod wspólnego języka pośredniego (CIL) utworzony przez Tlbimp.exe, a następnie ponownie je skompilując. Aby uzyskać szczegółowe informacje na temat modyfikowania kodu CIL, zobacz Dostosowywanie otoek wywoływanych w czasie wykonywania. Aby wskazać liczbę elementów w tablicy, zastosuj MarshalAsAttribute typ do parametru tablicy definicji metody zarządzanej w jeden z następujących sposobów:

  • Zidentyfikuj inny parametr zawierający liczbę elementów w tablicy. Parametry są identyfikowane według pozycji, zaczynając od pierwszego parametru jako liczby 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 );
    
  • Zdefiniuj rozmiar tablicy jako stałą. Na przykład:

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

Podczas określania rozmiaru tablic z niezarządzanego kodu do kodu zarządzanego marshaller sprawdza atrybut MarshalAsAttribute skojarzony z parametrem w celu określenia rozmiaru tablicy. Jeśli rozmiar tablicy nie jest określony, tylko jeden element jest ukierunkowany.

Uwaga

Funkcja MarshalAsAttribute nie ma wpływu na kierowanie zarządzanych tablic do niezarządzanych kodów. W tym kierunku rozmiar tablicy jest określany przez badanie. Nie ma możliwości marshalingu podzbioru tablicy zarządzanej.

Międzyoperacyjny marshaller używa metod CoTaskMemAlloc i CoTaskMemFree w systemie Windows lub malloc i bezpłatnych metod w innych systemach operacyjnych, aby przydzielić i pobrać pamięć. Alokacja pamięci wykonywana przez niezarządzany kod musi również używać tych metod.

Przekazywanie tablic do modelu COM

Wszystkie typy tablic zarządzanych można przekazać do niezarządzanego kodu z kodu zarządzanego. W zależności od typu zarządzanego i zastosowanych do niego atrybutów można uzyskać dostęp do tablicy jako bezpiecznej tablicy lub tablicy w stylu C, jak pokazano w poniższej tabeli.

Typ tablicy zarządzanej Wyeksportowane jako
<typ ELEMENT_TYPE_SZARRAY> UnmanagedType. Sejf Array(typ)

UnmanagedType.LPArray

Typ jest udostępniany w podpisie. Ranga jest zawsze 1, dolna granica zawsze wynosi 0. Rozmiar jest zawsze znany w czasie wykonywania.
<ELEMENT_TYPE_ARRAY ranga> typu><[<granice]> Unmanagedtype. Sejf Array(typ)

UnmanagedType.LPArray

Typ, ranga, granice są podane w podpisie. Rozmiar jest zawsze znany w czasie wykonywania.
ELEMENT_TYPE_CLASS<System.Array> UT_Interface

Unmanagedtype. Sejf Array(typ)

Typ, ranga, granice i rozmiar są zawsze znane w czasie wykonywania.

Istnieje ograniczenie w automatyzacji OLE odnoszące się do tablic struktur zawierających LPSTR lub LPWSTR. W związku z tym pola Ciąg muszą być rozdzielane jako UnmanagedType.BSTR. W przeciwnym razie zostanie zgłoszony wyjątek.

ELEMENT_TYPE_SZARRAY

Gdy metoda zawierająca parametr ELEMENT_TYPE_SZARRAY (tablica jednowymiarowa) jest eksportowana z zestawu .NET do biblioteki typów, parametr tablicy jest konwertowany na SAFEARRAY danego typu. Te same reguły konwersji dotyczą typów elementów tablicy. Zawartość tablicy zarządzanej jest automatycznie kopiowana z pamięci zarządzanej do pliku SAFEARRAY. Na przykład:

Podpis zarządzany

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

Podpis niezarządzany

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

Ranga bezpiecznych tablic jest zawsze 1, a dolna granica zawsze wynosi 0. Rozmiar jest określany w czasie wykonywania przez rozmiar przekazywanej tablicy zarządzanej.

Tablicę można również rozmieszać jako tablicę w stylu C przy użyciu atrybutu MarshalAsAttribute . Na przykład:

Podpis zarządzany

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

Podpis niezarządzany

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

Mimo że marshaller ma informacje o długości potrzebne do marshalingu tablicy, długość tablicy jest zwykle przekazywana jako oddzielny argument, aby przekazać długość do obiektu wywoływanego.

ELEMENT_TYPE_ARRAY

Gdy metoda zawierająca parametr ELEMENT_TYPE_ARRAY jest eksportowana z zestawu .NET do biblioteki typów, parametr tablicy jest konwertowany na SAFEARRAY danego typu. Zawartość tablicy zarządzanej jest automatycznie kopiowana z pamięci zarządzanej do pliku SAFEARRAY. Na przykład:

Podpis zarządzany

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

Podpis niezarządzany

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

Ranga, rozmiar i granice bezpiecznych tablic są określane w czasie wykonywania przez cechy zarządzanej tablicy.

Tablicę można również rozmieszać jako tablicę w stylu C, stosując MarshalAsAttribute atrybut . Na przykład:

Podpis zarządzany

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

Podpis niezarządzany

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

Zagnieżdżonych tablic nie można rozmieścić. Na przykład następujący podpis generuje błąd podczas eksportowania z eksporterem biblioteki typów (Tlbexp.exe).

Podpis zarządzany

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

<ELEMENT_TYPE_CLASS System.Array>

Gdy metoda zawierająca System.Array parametr jest eksportowana z zestawu .NET do biblioteki typów, parametr tablicy jest konwertowany na interfejs _Array . Zawartość tablicy zarządzanej jest dostępna tylko za pośrednictwem metod i właściwości interfejsu _Array . System.Array można również rozmieszczać jako SAFEARRAY przy użyciu atrybutu MarshalAsAttribute . W przypadku uśmierceniania jako bezpiecznej tablicy elementy tablicy są marshalled jako warianty. Na przykład:

Podpis zarządzany

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

Podpis niezarządzany

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

Tablice w strukturach

Struktury niezarządzane mogą zawierać tablice osadzone. Domyślnie te osadzone pola tablicy są marshalled jako SAFEARRAY. W poniższym przykładzie s1 jest osadzona tablica, która jest przydzielana bezpośrednio w samej strukturze.

Reprezentacja niezarządzana

struct MyStruct {
    short s1[128];
}

Tablice można rozmieścić jako UnmanagedType, co wymaga ustawienia MarshalAsAttribute pola. Rozmiar można ustawić tylko jako stałą. Poniższy kod przedstawia odpowiadającą mu definicję zarządzaną .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;
}

Zobacz też