共用方式為


陣列的預設封送處理

在包含整個 Managed 程式碼的應用程式中,Common Language Runtime 會將陣列類型傳遞為 In/Out 參數。 相較之下,Interop 封送處理器預設會將陣列傳遞為 In 參數。

使用關聯最佳化,與相同 Apartment 中的物件互動時,Blittable 陣列可以操作為 In/Out 參數。 不過,如果您稍後將程式碼匯出至用來產生跨電腦 Proxy 的型別程式庫,並且使用該程式庫跨 Apartment 封送處理呼叫,則呼叫可以回復為實際 In 參數行為。

陣列本質上相當複雜,而且 Managed 與 Unmanaged 陣列之間的區別保證資訊比其他非 Blittable 類型還要多。

Managed 陣列

Managed 陣列類別可能會不同;不過,System.Array 類別是所有陣列類型的基底類別。 System.Array 類別的屬性可以判斷陣列的順位、長度以及下限和上限,以及用來存取、排序、搜尋、複製和建立陣列的方法。

這些陣列類型是動態的,而且沒有基底類別庫中所定義的對應靜態類型。 很容易就會將每個項目類型和順位組合視為不同類型的陣列。 因此,一維整數陣列的類型與一維 Double 類型陣列不同。 同樣地,二維整數陣列與一維整數陣列不同。 比較類型時,不會考慮陣列界限。

如下表所示,任何 Managed 陣列執行個體都必須是特定項目類型、順位和下限。

Managed 陣列類型 項目類型 順位 下限 簽章標記法
ELEMENT_TYPE_ARRAY 按類型指定 依軍階規定 可選擇性地以界限指定 type[n,m]
ELEMENT_TYPE_CLASS 未知 未知 未知 System.Array
ELEMENT_TYPE_SZARRAY 按類型指定 1 0 type[n]

Unmanaged 陣列

Unmanaged 陣列是具有固定或變動長度的 COM 樣式安全陣列或 C 樣式陣列。 安全陣列是自我描述陣列,具有關聯陣列資料的類型、順位和界限。 C 樣式陣列是固定下限為 0 的一維類型陣列。 封送處理服務具有這兩種類型之陣列的有限支援。

將陣列參數傳遞給 .NET 程式碼

從 Unmanaged 程式碼,可以將 C 樣式陣列和安全陣列以安全陣列或 C 樣式陣列形式傳遞給 .NET 程式碼。 下表顯示 Unmanaged 類型值和匯入的類型。

Unmanaged 類型 匯入的類型
SafeArray(類型 < ELEMENT_TYPE_SZARRAYConvertedType>

順位 = 1,下限 = 0。 只有在 Managed 簽章中提供時,才會知道大小。 不是順位 = 1 或下限 = 0 的安全陣列無法封送處理為 SZARRAY
Type[] < ELEMENT_TYPE_SZARRAYConvertedType>

順位 = 1,下限 = 0。 只有在 Managed 簽章中提供時,才會知道大小。

安全陣列

將安全陣列從型別程式庫匯入至 .NET 組件時,會將陣列轉換成一維已知類型陣列 (例如 int)。 套用至參數的相同類型轉換規則也會套用至陣列項目。 例如,一個安全的類型陣列 BSTR 會變成一個可管理的字串陣列,而一個安全的變體陣列則會變成物件的受管理陣列。 SAFEARRAY元素型別會從型態庫擷取並儲存在SAFEARRAY列舉值UnmanagedType中。

因為無法從型別程式庫判斷安全陣列的順位和界限,所以順位會假設等於 1,而下限假設等於 0。 順位和界限必須定義於型別程式庫匯入工具 (Tlbimp.exe) 所產生的 Managed 簽章中。 若執行時傳遞給方法的秩不同,則拋出 a SafeArrayRankMismatchException 。 若執行時傳遞的陣列類型不同,則拋出 a SafeArrayTypeMismatchException 。 下列範例示範 Managed 和 Unmanaged 程式碼中的安全陣列。

Unmanaged 簽章

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

Managed 簽章

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 物件。 如果所傳遞的陣列已知為多維度,您可以編輯 Tlbimp.exe 所產生的中繼語言 (CIL) 程式碼,然後重新進行編譯。 如需如何修改 CIL 程式碼的詳細資料,請參閱自訂執行階段可呼叫包裝函式

C 樣式陣列

將 C 樣式陣列從型別程式庫匯入至 .NET 組件時,會將陣列轉換成 ELEMENT_TYPE_SZARRAY

陣列項目類型取決於型別程式庫,並且在匯入期間保留。 套用至參數的相同轉換規則也會套用至陣列項目。 例如,一個型別陣 LPStr 列會變成型別陣列 String 。 Tlbimp.exe 會擷取陣列項目類型,並將 MarshalAsAttribute 屬性套用至參數。

陣列順位會假設等於 1。 如果順位大於 1,則會以資料行主要順序將陣列封送處理為一維陣列。 下限一律會等於 0。

型別程式庫可以包含固定或可變長度的陣列。 Tlbimp.exe 只能從型別程式庫中匯入固定長度陣列,因為型別程式庫缺少封送處理可變長度陣列所需的資訊。 對於固定長度陣列,大小會從類型函式庫匯入,並捕捉在應用於參數的MarshalAsAttribute中。

您必須手動定義包含可變長度陣列的型別程式庫,如下列範例所示。

Unmanaged 簽章

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

Managed 簽章

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 or length_is 屬性來傳達大小給用戶端,但 Microsoft 介面定義語言(MIDL)編譯器不會將該資訊傳播到型別函式庫。 如果不知道大小,則 Interop 封送處理服務無法封送處理陣列項目。 因此,會將可變長度的陣列匯入為參考引數。 例如:

Unmanaged 簽章

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

Managed 簽章

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

您可以編輯 Tlbimp.exe 所產生的通用中間語言 (CIL) 程式碼,然後重新進行編譯,以提供具有陣列大小的封送處理器。 如需如何修改 CIL 程式碼的詳細資料,請參閱自訂執行階段可呼叫包裝函式。 若要指出陣列中的項目數,請使用下列其中一種方式,將 MarshalAsAttribute 類型套用至 Managed 方法定義的陣列參數:

  • 識別包含陣列中項目數的另一個參數。 參數是以位置進行識別,而第一個參數開始於數字 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 對將受管理陣列編入非受管理程式碼沒有影響。 在該方向,陣列大小取決於檢查。 沒有任何方法可以封送處理 Managed 陣列的子集。

zh-TW: 互操作 marshaller 在 Windows 上使用 CoTaskMemAllocCoTaskMemFree 方法,或在其他作業系統上使用 mallocfree 方法來分配與檢索記憶體。 Unmanaged 程式碼所執行的記憶體配置也必須使用這些方法。

將陣列傳遞給 COM

所有 Managed 陣列類型都可以從 Managed 程式碼傳遞至 Unmanaged 程式碼。 根據 Managed 類型和其套用的屬性,陣列可以存取為安全陣列或 C 樣式陣列,如下表所示。

Managed 陣列類型 匯出為
ELEMENT_TYPE_SZARRAY<類型> UnmanagedType 。SafeArray(類型

UnmanagedType.LPArray

類型是在簽章中提供。 順位一律為 1,下限一律為 0。 大小在執行時總是已知。
ELEMENT_TYPE_ARRAY<類型><等級>[<界限>] UnmanagedType.SafeArray(type

UnmanagedType.LPArray

在簽章中,提供類型、順位和界限。 大小在執行時總是已知。
ELEMENT_TYPE_CLASS<System.Array> UT_Interface

UnmanagedType.SafeArray(type

類型、等級、界限與大小在執行時始終已知。

與包含 LPSTR 或 LPWSTR 之結構陣列有關的 OLE Automation 限制。 因此,欄位String必須編組為 UnmanagedType.BSTR。 否則,系統將擲回 例外狀況。

ELEMENT_TYPE_SZARRAY

當包含 ELEMENT_TYPE_SZARRAY 參數(一維陣列)的方法從 .NET 組合語言匯出到型態庫時,該陣列參數會被轉換成特定型別的 a SAFEARRAY 。 相同的轉換規則會套用至陣列項目類型。 Managed 陣列的內容會自動從 Managed 記憶體複製至 SAFEARRAY。 例如:

Managed 簽章

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

Unmanaged 簽章

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

安全陣列的順位一律為 1,而下限一律為 0。 大小由執行時傳遞的受管理陣列大小決定。

使用 MarshalAsAttribute 屬性,也可以將陣列封送處理為 C 樣式陣列。 例如:

Managed 簽章

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

Unmanaged 簽章

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

雖然封送處理器具有封送處理陣列所需的長度資訊,但是陣列長度通常會傳遞為個別引數,以將長度傳遞給被呼叫者。

ELEMENT_TYPE_ARRAY

當包含 ELEMENT_TYPE_ARRAY 參數的方法從 .NET 組合語言匯出到型別函式庫時,陣列參數會被轉換成特定型態的 a SAFEARRAY 。 Managed 陣列的內容會自動從 Managed 記憶體複製至 SAFEARRAY。 例如:

Managed 簽章

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

Unmanaged 簽章

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

安全陣列的排名、大小與界限,在執行時由受管理陣列的特性決定。

套用 MarshalAsAttribute 屬性,也可以將陣列封送處理為 C 樣式陣列。 例如:

Managed 簽章

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

Unmanaged 簽章

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

無法封送處理巢狀陣列。 例如,下列簽章會在使用型別程式庫匯出工具 (Tlbexp.exe) 匯出時產生錯誤。

Managed 簽章

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

<ELEMENT_TYPE_CLASS System.Array>

當包含 System.Array 參數的方法從 .NET 組合語言匯出到型別函式庫時,陣列參數會轉換成介面 _Array 。 受管理陣列的內容僅能透過介面的方法 _Array 與屬性存取。 System.Array 也可以使用SAFEARRAY 屬性來將其當作MarshalAsAttribute 進行封送處理。 封送處理為安全陣列時,會將陣列項目封送處理為變異值。 例如:

Managed 簽章

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

Unmanaged 簽章

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

結構內的陣列

Unmanaged 結構可以包含內嵌的陣列。 根據預設,會將這些內嵌的陣列欄位封送處理為 SAFEARRAY。 在下列範例中,s1 是直接配置在結構本身內的內嵌陣列。

Unmanaged 呈現

struct MyStruct {
    short s1[128];
}

陣列可以封送處理為 UnmanagedType,這需要您設定 MarshalAsAttribute 欄位。 大小只能設定為常數。 下列程式碼示範 MyStruct 的對應 Managed 定義。

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

另請參閱