匯出的成員轉換
這個主題將描述匯出處理序如何轉換下列成員:
方法
屬性
事件
方法
COM 用戶端預期會呼叫方法、傳遞熟悉的 COM 資料型別 (例如參數),並且接收傳回的 HRESULT。但是,在 .NET 的領域中,類別並沒有傳回型別 (而且,事實上,根本不使用 HRESULT) 這類限制。
為了要滿足這兩種模型,Managed 型別的每一個方法都有 .NET 簽章和隱含的 COM 簽章。這兩種簽章通常會有很大差異。.NET 用戶端是使用 .NET 簽章與伺服器互動,而 (可能在同一時間) COM 用戶端則是使用 COM 簽章與伺服器互動。伺服器會使用 .NET 簽章實作方法,而執行階段封送處理服務則負責使用將呼叫委派給 Managed 方法的 COM 簽章提供 Stub。
HRESULT 轉譯
將 Managed 傳回值變更為 [out, retval] 參數,並且將 Unmanaged 傳回值的型別變更為 HRESULT,即可將 Managed 簽章轉換為 Unmanaged 簽章。例如,DoSomething
方法可能會具有下列簽章:
Managed 簽章
short DoSomething(short i);
Unmanaged 簽章
HRESULT DoSomething([in] short i, [out, retval] short *rv);
請注意,COM 簽章會傳回 HRESULT 並且對傳回值有額外的 out 參數。Managed 實作的傳回值永遠是以 [out, retval] 參數的方式傳回 (附加在 Unmanaged 簽章後面),而 Unmanaged 簽章則永遠會傳回 HRESULT。如果 Managed 方法傳回虛值 (Void),Runtime 便會省略 [out, retval] 參數。例如:
Managed 簽章
void DoSomething(short i);
Unmanaged 簽章
HRESULT DoSomething([in] short i);
在某些情況下,最好是讓 Managed 簽章保持不變。您可以使用 PreserveSigAttribute 來進行這項動作。例如:
Managed 簽章
[PreserveSig] short DoSomething(short i);
Unmanaged 簽章
short DoSomething ([in] short i);
擁有兩種不同的方法簽章,可以更容易地從 COM 和 .NET 兩者的用戶端不著痕跡地使用類別。此外,COM 和 .NET 兩者的用戶端也可以同時使用一個 .NET 類別。身為類別作者的您只需要實作 Managed 簽章,使用 Tlbexp.exe (或等效的 API) 即可自動將簽章匯出至針對該類別產生的型別程式庫。
多載方法
雖然 .NET 支援多載方法,不過 IDispatch 介面只依賴方法名稱來繫結,而不是完整的方法簽章。因此,它並不支援多載方法。不過,為了提供對型別之多載方法的存取,Tlbexp.exe 會使用序數來裝飾多載方法的名稱,讓每一個方法名稱都是唯一的。
下列 Managed 和 Unmanaged 簽章所示就是序數的含入:
Managed 簽章
interface INew {
public:
void DoSomething();
void DoSomething(short s);
void DoSomething(short l);
void DoSomething(float f);
void DoSomething(double d);
}
Unmanaged 簽章
interface INew {
void DoSomething();
void DoSomething_2(short s);
void DoSomething_3(short l);
void DoSomething_4(float f);
void DoSomething_5(double d);
}
方法的 COM 簽章會顯示為單一 DoSomething
方法,後面接著一系列裝飾的 DoSomething_
x 方法,其中 x 是從 2 開始,並且對每一個多載形式的方法遞增。請注意,有些多載方法可以從基底型別繼承。但是,並不保證多載方法會在型別版本演進時保持相同的數字。
雖然 .NET 用戶端可以使用多載形式的方法,但是 COM 用戶端卻必須存取裝飾的方法。物件瀏覽器會顯示具有方法簽章的所有形式的裝飾方法,讓您能夠選取正確的方法。晚期繫結的用戶端也可以呼叫 IDispatch::GetIdsOfNames,傳入裝飾名稱 (Decorated Name) 來取得任何多載方法的 DispID。
屬性
Managed 類別和介面可以具有屬性。Managed 屬性是可能擁有關聯 Get 方法和 Set 方法的特定資料型別。這些方法都是個別定義的,就如同任何其他方法一樣。以下程式碼範例所示為含有 Height
屬性的介面。實作這個介面的類別必須提供屬性的 Get 和 Set 方法。
interface IMammal {
IMammal Mother{get;set;}
IMammal Father{get;set;}
int Height{get;set;}
int Weight{get;set;}
}
class Human : IMammal
{
int weight;
int height;
IMammal father;
IMammal mother;
public IMammal Mother { get { return mother; } set { mother = value; } }
public IMammal Father { get { return father; } set { father = value; } }
public int Height { get { return height; } set { height = value; } }
public int Weight { get { return weight; } set { weight = value; } }
}
在匯出期間,Tlbexp.exe 會將屬性 Set 方法轉換為 [propput],並將 Get 方法轉換為 [propget]。在 COM 裡面的屬性名稱會保持與 Managed 屬性名稱相同。這項規則有下列例外:
如果這個屬性型別 (除了實值型別) 是類別或介面,那麼屬性 Set 方法會成為 [propputref],給參數加入一層間接取值。
如果這個屬性沒有 Get 或 Set 方法,Tlbexp.exe 會從型別程式庫省略這個屬性。
就像屬性一樣,Managed 欄位也會被匯出至型別程式庫。Runtime 封送處理服務會對所有公用欄位自動產生 Get 和 Set 方法。在轉換處理期間,Tlbexp.exe 會對每一欄位產生一個 [propput] (或 [propputref]) 函式和一個 [propget] 函式,如以下型別程式庫表示所示。
型別程式庫表示
interface IMammal : IDispatch {
[propget] HRESULT Mother([out, retval] IMammal** pRetVal);
[propputref] HRESULT Mother([in] IMammal* pRetVal);
[propget] HRESULT Father([out, retval] IMammal** pRetVal);
[propputref] HRESULT Father([in] IMammal* pRetVal);
[propget] HRESULT Height([out, retval] long* pRetVal);
[propput] HRESULT Height([in] long pRetVal);
[propget] HRESULT Weight([out, retval] long* pRetVal);
[propput] HRESULT Weight([in] long pRetVal);
[propget] HRESULT Age([out, retval] long* pRetVal);
[propput] HRESULT Age([in] long pRetVal);
};
事件
如果您不太熟悉 COM Interop 中的事件模型,請參閱 Managed 和 Unmanaged 事件。Managed 型別是使用委派架構事件模型實作事件。例如,下列程式碼範例中的 Class1Events
介面會引發 Click
事件。
Public Delegate Sub ClickDelegate()
<GuidAttribute("1A585C4D-3371-48dc-AF8A-AFFECC1B0967"), _
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface Class1Event
Sub Click ()
End Interface
<ComSourceInterfaces("Class1Event, EventSrc")> _
Public Class Class1
Public Event Click As ClickDelegate
End Class
public delegate void Click();
public interface Class1Event
{
void Click();
}
[ComSourceInterfaces("Class1Event, EventSrc")]
public class Class1
{
public event ClickDelegate Click;
}
在匯出過程中,Tlbexp.exe 會將事件介面標記為 Coclass 中的來源。如同下列型別程式庫表示所示,匯出的 ComClass1Events
介面已標記為來源介面。
型別程式庫表示
disinterface Class1Event {
properties:
methods:
[id(0x60020000)]
HRESULT Click();
};
coclass Class1
{
…
[default, source] Class1Event;
};
請參閱
概念
匯出的組件轉換
匯出的模組轉換
匯出的型別轉換
匯出的參數轉換