在包含整個 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 上使用 CoTaskMemAlloc 和 CoTaskMemFree 方法,或在其他作業系統上使用 malloc 和 free 方法來分配與檢索記憶體。 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;
}