封送處理變更

下列章節提供一組選取的變更,您可以在 Interop 組件中進行這些變更,解決某些特定的匯入處理輸出問題:

  • 相符的 C-Style 陣列

  • In/Out C-Style 陣列

  • 多維 C-Style 陣列

  • 非零界限的 SAFEARRAY

  • 保留簽章

  • 傳遞 Null 而非實值型別的參考

這些章節並未代表編輯 Interop 組件的每一種狀況。 例如,您也可以編輯 Interop 組件來增強其簡易操作。 決定哪些自訂是必要的唯一方式是使用 Interop 組件來實際撰寫程式碼。 如需編輯 Interop 組件的指示,請參閱 HOW TO:編輯 Interop 組件

當用戶端和伺服器都在不相容的 Apartment 中時,會影響封送處理。 在下列範例中,大部分的封送處理參數都不是 Automation 相容的,並且需要進行以下其中一個動作:

  • 確認用戶端和伺服器都是在相容的 Apartment 中 (所以和 COM 封送處理無關)。

  • 註冊 Proxy 和從介面定義語言 (IDL) 產生的 stub。 註冊型別程式庫並不會對這些狀況有所幫助,因為許多封送處理所需要的資訊並未由 IDL 傳播到型別程式庫。

相符的 C-Style 陣列

下列 IDL 宣告顯示 C-Style 陣列。

HRESULT ConformantArray([in] int cElems, [in, size_is(cElems)] int 
aConf[]);

因為這個型別不是 Automation 相容的,所以有關陣列大小的資訊 (例如,第一個參數和第二個參數之間的連結) 無法在型別程式庫中表示。 [型別程式庫匯入工具] (Tlbimp.exe) 會將第二個參數當做參考匯入為整數,而非當做 Managed 陣列。 您可以編輯 MSIL 來調整參數。

搜尋 MSIL

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32& aConf) runtime managed internalcall

取代為

method public hidebysig newslot virtual 
instance void  ConformantArray([in] int32 cElems,
[in] int32[] marshal([]) aConf) runtime managed internalcall

從 Managed 程式碼呼叫

int[] param1 = { 11, 22, 33 };
tstArrays.ConformantArray( 3, param1 );

In/Out C-Style 陣列

下列 IDL 宣告顯示 In/Out C-Style 陣列。

HRESULT InOutArray([in, out] int* pcElems, [in, out, size_is(,*pcElems)] 
int** ppInOut);

在這種狀況下,可以調整陣列的大小,並且傳回新的大小。 因為這個型別不是 Automation 相容的,所以有關陣列大小的資訊 (例如,第一個參數和第二個參數之間的連結) 無法在型別程式庫中表示。 Tlbimp.exe 會將第二個參數當成 IntPtr 匯入。 雖然您仍然可以從 Managed 程式碼中呼叫這個方法,但是要調整陣列,您必須編輯 MSIL 並使用 Marshal 類別中的方法,來手動處理記憶體的配置與解除配置。

搜尋 MSIL

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int ppInOut) runtime managed internalcall

取代成

.method public hidebysig newslot virtual 
instance void  InOutArray([in][out] int32& pcElems,
[in][out] native int& ppInOut) runtime managed internalcall

從 Managed 程式碼呼叫

int[] inArray = { 11, 22, 33 };
int arraySize = inArray.Length;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( typeof( int )) * inArray.Length );
Marshal.Copy( inArray, 0, buffer, inArray.Length );
tstArrays.InOutArray( ref arraySize, ref buffer );
if( arraySize > 0 )
{
int[] arrayRes = new int[ arraySize ];
Marshal.Copy( buffer, arrayRes, 0, arraySize );
Marshal.FreeCoTaskMem( buffer );
}

多維 C-Style 陣列

下列 IDL 宣告顯示二維 C-Style 陣列。

HRESULT TwoDimArray([in] int cDim, [in, size_is(cDim)] int aMatrix[][3]);

因為這個型別不是 Automation 相容的,所以有關陣列大小和維數的資訊 (例如,第一個參數和第二個參數之間的連結) 無法在型別程式庫中表示。 Tlbimp.exe 會將第二個參數當做 IntPtr 型別匯入,而非當成 Managed 多維陣列。 您可以編輯 MSIL 來調整參數。

搜尋 MSIL

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] native int aMatrix) runtime managed internalcall

取代為

.method public hidebysig newslot virtual 
instance void  TwoDimArray([in] int32 cDim,
[in] int32[,] marshal([]) aMatrix) runtime managed internalcall

從 Managed 程式碼呼叫

int[,] param = {{ 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 }};
tstArrays.TwoDimArray( 3, param );

非零界限的 SAFEARRAY

下列 IDL 宣告顯示 SAFEARRAY 參數。

HRESULT InSArray([in] SAFEARRAY(int) *ppsa);

將這個 SAFEARRAY 視為非零界限。 在 Managed 程式碼中,這類陣列是由 System.Array 型別所表示的。 不過,根據預設,匯入工具會將所有的 SAFEARRAY 參數轉換為 Managed 陣列的參考。 您有兩個變更預設行為的選項:

  • 使用 Tlbimp.exe 與 /sysarray 參數,將型別程式庫中的所有陣列當成 System.Array 型別匯入。

  • 以手動方式編輯 MSIL,將少數幾個參數當成 System.Array 型別匯入,如下列範例所示。

    搜尋 MSIL

    .method public hidebysig newslot virtual 
    instance void  InSArray([in] int32[]&  marshal( safearray int) ppsa) runtime managed internalcall
    

    取代為

    .method public hidebysig newslot virtual 
    instance void  InSArray(class [mscorlib]System.Array& marshal( safearray) 
    ppsa) runtime managed internalcall
    

    從 Managed 程式碼呼叫

    int[] lengthsArray = new int[1] { 3 };   
    int[] boundsArray = new int[1] { -1 };
    Array param2 = Array.CreateInstance( typeof(int), lengthsArray, boundsArray );
    for( int i = param2.GetLowerBound( 0 ); i <= param2.GetUpperBound( 0 ); i++ )
    param2.SetValue( i * 10, i ); 
    sum = tstArrays.InSArray( ref param2 );
    

保留簽章

下列 IDL 宣告顯示 COM 方法簽章。

HRESULT TestPreserveSig2([in] int inParam, [out,retval] int* outParam);

Tlbimp.exe 會變更 COM 方法的簽章。 在 IDL 中標示有 [out, retval] 的參數會變成 Managed 方法的傳回值。 所有表示失敗的 HRESULT 值都會轉換為 Managed 例外狀況。 有時候需要保留原始的 COM 方法簽章,例如,方法傳回成功 HRESULT 以外的資訊時。 下列 Managed 表示顯示您可以修改的簽章範例。

MSIL 中的 Managed 表示

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam) runtime managed internalcall
{

取代成

.method public hidebysig newslot virtual 
instance int32 TestPreserveSig2([in] int32 inParam, [out] int32& outParam) runtime managed internalcall preservesig

查看傳回哪一個 HRESULT

int hr = tst.TestPreserveSig2( -3, out retValue );
Console.WriteLine( "Return value is {0}", retValue );
if( hr == 0 )
Console.WriteLine( "HRESULT = S_OK" );
else if ( hr == 1 )
Console.WriteLine( "HRESULT = S_FALSE" );
else
Console.WriteLine( "HRESULT = {0}", hr );

傳遞 Null 代替實值型別的參考

下列 IDL 宣告顯示結構的 IDL 指標。

HRESULT TestPassingNull([in, unique] Point* refParam);

Tlbimp.exe 會將參數當成實值型別 Point 的參考匯出。 在 C# 和 Visual Basic 2005 中,如果必須是實值型別的參考,就無法將 null 參考 (在 Visual Basic 中為 Nothing) 當做參數來傳遞。 如果 COM 函式需要 null (Nothing) 參數,您可以編輯 MSIL 修改簽章。

搜尋 MSIL

.method public hidebysig newslot virtual 
instance void  TestPassingNull(
[in] valuetype MiscSrv.tagPoint& refParam) 
runtime managed internalcall

取代成

.method public hidebysig newslot virtual 
instance void  TestPassingNull([in] native int) runtime managed internalcall

更改過的簽章可以讓您傳遞 Null 值。 不過,當您需要傳遞一些實際值時,就必須使用 Marshal 類別的方法,如下列範例所示。

tagPoint p = new tagPoint();
p.x = 3;
p.y = 9;

IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( p ));
Marshal.StructureToPtr( p, buffer, false );
tst.TestPassingNull( buffer );
Marshal.FreeCoTaskMem( buffer );
tst.TestPassingNull( IntPtr.Zero );

請參閱

工作

HOW TO:編輯 Interop 組件

HOW TO:手動建立包裝函式

參考

Tlbimp.exe (型別程式庫匯入工具)

概念

自訂執行階段可呼叫包裝函式

COM 資料型別

自訂 COM 可呼叫包裝函式