System.Runtime.InteropServices.ICustomMarshaler 介面

本文提供此 API 參考文件的補充備註。

介面 ICustomMarshaler 提供用於處理方法呼叫的自定義包裝函式。

封送器提供新舊介面功能之間的橋樑。 自訂封送處理提供下列優點:

  • 它可讓設計成使用舊介面的用戶端應用程式,也能使用實作新介面的伺服器。
  • 它可讓建置的用戶端應用程式使用新的介面,以使用實作舊介面的伺服器。

如果您有介面引進不同的封送處理行為,或以不同的方式公開給元件物件模型 (COM),您可以設計自定義封送處理器,而不是使用 Interop 封送處理器。 藉由使用自定義封送器,您可以將新的 .NET Framework 元件與現有 COM 元件之間的差異降到最低。

例如,假設您正在開發稱為 INew的Managed介面。 當這個介面透過標準 COM 可呼叫包裝函式 (CCW) 公開給 COM 時,其方法與 Managed 介面相同,並使用 Interop 封送處理器內建的封送處理規則。 現在假設名為 IOld 的已知 COM 介面已經提供與 INew 介面相同的功能。 藉由設計自定義封送器,您可以提供的 Unmanaged 實 IOld 作,只委派對 介面之 Managed 實作的 INew 呼叫。 因此,自定義封送處理器會作為Managed與Unmanaged介面之間的橋樑。

注意

從 Managed 程式代碼呼叫僅限分派介面上的 Unmanaged 程式代碼時,不會叫用自定義封送器。

定義封送處理類型

您必須先定義要封送處理的Managed和 Unmanaged 介面,才能建置自定義封送處理器。 這些介面通常會執行相同的函式,但會以不同的方式公開給 Managed 和 Unmanaged 物件。

Managed 編譯程式會從元數據產生 Managed 介面,產生的介面看起來像任何其他 Managed 介面。 下列範例顯示一般介面。

public interface INew
{
    void NewMethod();
}
Public Interface INew
    Sub NewMethod()
End Interface

您可以在介面定義語言 (IDL) 中定義 Unmanaged 類型,並使用 Microsoft Interface Definition Language (MIDL) 編譯程式加以編譯。 您可以在連結庫語句中定義 介面,併為其指派具有通用唯一標識碼 (UUID) 屬性的介面標識碼,如下列範例所示。

 [uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
     [uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
     interface IOld : IUnknown
         HRESULT OldMethod();
}

MIDL 編譯程式會產生數個輸出檔案。 如果介面是在 Old.idl 中定義,輸出檔 Old_i.c 會 const 定義具有 介面識別碼 (IID) 的變數,如下列範例所示。

const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};

Old.h 檔案也由 MIDL 產生。 它包含可包含在 C++ 原始程式碼中的介面 C++ 定義。

實作 ICustomMarshaler 介面

您的自定義封送處理器必須實作 ICustomMarshaler 介面,以提供適當的包裝函式給運行時間。

下列 C# 程式代碼會顯示必須由所有自定義封送處理器實作的基底介面。

public interface ICustomMarshaler
{
    Object MarshalNativeToManaged(IntPtr pNativeData);
    IntPtr MarshalManagedToNative(Object ManagedObj);
    void CleanUpNativeData(IntPtr pNativeData);
    void CleanUpManagedData(Object ManagedObj);
    int GetNativeDataSize();
}
Public Interface ICustomMarshaler
     Function MarshalNativeToManaged( pNativeData As IntPtr ) As Object
     Function MarshalManagedToNative( ManagedObj As Object ) As IntPtr
     Sub CleanUpNativeData( pNativeData As IntPtr )
     Sub CleanUpManagedData( ManagedObj As Object )
     Function GetNativeDataSize() As Integer
End Interface

介面 ICustomMarshaler 包含提供轉換支援、清除支援,以及要封送處理之數據的相關信息的方法。

作業類型 ICustomMarshaler 方法 描述
轉換 (從原生程式代碼轉換為 Managed 程式代碼) MarshalNativeToManaged 將原生數據的指標封送處理至 Managed 物件。 這個方法會傳回自定義運行時間可呼叫包裝函式 (RCW),可封送處理傳遞為自變數的 Unmanaged 介面。 封送處理器應該傳回該類型的自定義 RCW 實例。
轉換 (從 Managed 到機器碼) MarshalManagedToNative 將Managed物件封送處理為原生數據的指標。 這個方法會傳回自定義 COM 可呼叫包裝函式 (CCW),可封送處理傳遞為自變數的 Managed 介面。 封送處理器應該傳回該類型的自定義CCW實例。
清除 (原生程式代碼) CleanUpNativeData 可讓封送處理器清除 方法所 MarshalManagedToNative 傳回的原生數據 (CCW)。
清除 (Managed 程式代碼) CleanUpManagedData 可讓封送處理器清除 方法所 MarshalNativeToManaged 傳回的Managed資料 (RCW)。
資訊 (關於機器碼) GetNativeDataSize 傳回要封送處理之 Unmanaged 數據的大小。

轉換

ICustomMarshaler.MarshalNativeToManaged

將原生數據的指標封送處理至 Managed 物件。 這個方法會傳回自定義運行時間可呼叫包裝函式 (RCW),可封送處理傳遞為自變數的 Unmanaged 介面。 封送處理器應該傳回該類型的自定義 RCW 實例。

ICustomMarshaler.MarshalManagedToNative

將Managed物件封送處理為原生數據的指標。 這個方法會傳回自定義 COM 可呼叫包裝函式 (CCW),可封送處理傳遞為自變數的 Managed 介面。 封送處理器應該傳回該類型的自定義CCW實例。

清理

ICustomMarshaler.CleanUpNativeData

可讓封送處理器清除 方法所 MarshalManagedToNative 傳回的原生數據 (CCW)。

ICustomMarshaler.CleanUpManagedData

可讓封送處理器清除 方法所 MarshalNativeToManaged 傳回的Managed資料 (RCW)。

大小資訊

ICustomMarshaler.GetNativeDataSize

傳回要封送處理之 Unmanaged 數據的大小。

注意

如果自定義封送處理器會在從原生封送處理至 Managed 或清除時呼叫任何設定最後一個 P/Invoke 錯誤的方法,則 由 Marshal.GetLastWin32Error() 傳回的值,並將 Marshal.GetLastPInvokeError() 代表封送處理或清除呼叫中的呼叫。 當將自定義封送處理器搭配 P/Invokes DllImportAttribute.SetLastErrortrue設定為 時,這可能會造成錯誤。 若要保留最後一個 P/Invoke 錯誤,請使用 Marshal.GetLastPInvokeError() 實作中的 ICustomMarshalerMarshal.SetLastPInvokeError(Int32) 方法。

實作 GetInstance 方法

除了實作 ICustomMarshaler 介面之外,自定義封送處理器還必須實 static 作稱為 GetInstance 的方法,以接受 String 做為參數,並且具有的 ICustomMarshaler傳回型別。 這個 static 方法是由 Common Language Runtime 的 COM Interop 層呼叫,以具現化自定義封送器實例。 傳遞至 GetInstance 的字串是 Cookie,方法可用來自定義傳回的自定義封送處理器。 下列範例顯示最少但完整的 ICustomMarshaler 實作。

public class NewOldMarshaler : ICustomMarshaler
{
    public static ICustomMarshaler GetInstance(string pstrCookie)
        => new NewOldMarshaler();

    public Object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException();
    public IntPtr MarshalManagedToNative(Object ManagedObj) => throw new NotImplementedException();
    public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException();
    public void CleanUpManagedData(Object ManagedObj) => throw new NotImplementedException();
    public int GetNativeDataSize() => throw new NotImplementedException();
}

套用 MarshalAsAttribute

若要使用自定義封送處理器,您必須將 MarshalAsAttribute 屬性套用至要封送處理的參數或欄位。

您也必須將 UnmanagedType.CustomMarshaler 列舉值傳遞至建 MarshalAsAttribute 構函式。 此外,您必須使用下列其中一個具名參數來指定 MarshalType 欄位:

  • MarshalType (必要):自定義封送器之元件限定名稱。 名稱應該包含自定義封送器命名空間和類別。 如果自定義封送器未在所使用的元件中定義,您必須指定其定義所在的元件名稱。

    注意

    您可以使用 MarshalTypeRef 欄位, MarshalType 而不是欄位。 MarshalTypeRef 會採用更容易指定的類型。

  • MarshalCookie (選擇性):傳遞至自定義封送器的Cookie。 您可以使用 Cookie 將其他資訊提供給封送器。 例如,如果使用相同的封送處理器來提供一些包裝函式,Cookie 會識別特定的包裝函式。 Cookie 會傳遞至 GetInstance 封送器的方法。

屬性 MarshalAsAttribute 會識別自定義封送處理器,以便啟動適當的包裝函式。 Common Language Runtime 的 Interop 服務接著會檢查 屬性,並在第一次需要封送處理自變數 (參數或字段) 時建立自定義封送處理器。

運行時間接著會在自定義封送器上呼叫 MarshalNativeToManagedMarshalManagedToNative 方法,以啟動正確的包裝函式來處理呼叫。

使用自定義封送處理器

當自定義封送器完成時,您可以使用它做為特定類型的自定義包裝函式。 下列範例顯示 Managed 介面的定義 IUserData

interface IUserData
{
    void DoSomeStuff(INew pINew);
}
Public Interface IUserData
    Sub DoSomeStuff(pINew As INew)
End Interface

在下列範例中 IUserData ,介面會使用 NewOldMarshaler 自定義封送器,讓 Unmanaged 用戶端應用程式將介面傳遞 IOldDoSomeStuff 方法。 方法的 DoSomeStuff Managed 描述會採用 INew 介面,如上一個範例所示,而的 Unmanaged 版本 DoSomeStuff 則採用 IOld 介面指標,如下列範例所示。

[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
     [uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
     interface IUserData : IUnknown
         HRESULT DoSomeStuff(IUnknown* pIOld);
}

匯出 Managed 定義的 IUserData 類型連結庫會產生此範例中顯示的 Unmanaged 定義,而不是標準定義。 套 MarshalAsAttribute 用至 INew 方法 Managed 定義中自變數的屬性 DoSomeStuff 表示自變數使用自定義封送處理器,如下列範例所示。

using System.Runtime.InteropServices;
Imports System.Runtime.InteropServices
interface IUserData
{
    void DoSomeStuff(
        [MarshalAs(UnmanagedType.CustomMarshaler,
         MarshalType="NewOldMarshaler")]
    INew pINew
    );
}
Public Interface IUserData
    Sub DoSomeStuff( _
        <MarshalAs(UnmanagedType.CustomMarshaler, _
        MarshalType := "MyCompany.NewOldMarshaler")> pINew As INew)
End Interface

在上述範例中,提供給 MarshalAsAttribute 屬性的第一個參數是 UnmanagedType.CustomMarshaler 列舉值 UnmanagedType.CustomMarshaler

第二個參數是 MarshalType 字段,其提供自定義封送器之元件限定名稱。 此名稱是由自定義封送器 (MarshalType="MyCompany.NewOldMarshaler") 的命名空間和類別所組成。