匯入的型別轉換
更新:2007 年 11 月
這個主題將說明匯入處理序如何轉換下列型別:
介面
類別
結構
列舉型別
常數
Typedef
一般而言,Tlbimp.exe 會以型別在原來型別程式庫中的同樣名稱匯入它們。型別程式庫內的名稱必須是唯一的,這樣才可以消除轉換處理期間的命名衝突。所有有效的型別程式庫名稱都是有效的組件名稱。
匯入的型別是以它所屬的命名空間 (與原來的型別程式庫相同) 為範圍。這些型別是由它們的完整命名空間和型別名稱來個別地識別。
您可以使用型別程式庫中的型別程式庫屬性,來明確地控制匯入型別的 Managed 名稱。這個使用者定義屬性識別項為 0F21F359-AB84-41e8-9A78-36D110E6D2F9。以下型別程式庫展示了使用者定義屬性的加入。
型別程式庫表示
[ uuid(…),
version(1.0)
]
library AcmeLib {
interface Widget {};
[custom(0F21F359-AB84-41e8-9A78-36D110E6D2F9,
"Acme.WidgetLib.Slingshot")]
coclass Slingshot {};
};
雖然 Tlbimp.exe 是將型別程式庫匯入至 AcmeLib 命名空間,但是 Slingshot 類別卻會成為 Acme.WidgetLib.Slingshot。
介面
當匯入處理轉換介面時,會去除所有 IUnknown 和 IDispatch 方法。除非介面為雙重介面 (衍生自 IDispatch 的介面),否則這項轉換會將 GuidAttribute 套用至介面,以保留在型別程式庫中指派的介面識別項 (IID) 和 InterfaceTypeAttribute。
型別程式庫表示
[uuid(…), ]
interface IWidget : IUnknown {
HRESULT New()
HRESULT Start()
};
[uuid(…), ]
interface IGadget : IWidget {
HRESULT Baz()
};
在轉換期間,匯入處理序會將基底介面的方法加入到衍生介面。在下列範例中,會將 New 和 Start 加入至 IGadget 介面:
<Guid(…), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Interface IWidget
Sub [New]()
Sub Start()
End Interface
<Guid(…), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Interface IGadget
Inherits IWidget
Shadows Sub [New]()
Shadows Sub Start()
Sub Baz()
End Interface
[Guid(…), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IWidget {
void New();
void Start();
};
[Guid(…), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IGadget : IWidget {
new void New();
new void Start();
void Baz();
};
類別
匯入處理序會建立 Managed 類別來代表每一個 COM Coclass,賦予 Managed 類別與附加在 Class 上之原始 Coclass 相同的名稱。例如,NewNewer Coclass 會變成 NewNewerClass。這項轉換會將 GuidAttribute 加入到類別,以擷取 Coclass 的類別識別項 (CLSID)。
除了 Managed 類別以外,匯入處理序會加入名稱與 Coclass 相同的介面,並套用 CoClassAttribute 來辨識原始 Coclass 的 CLSID。這個介面的 IID 與 Coclass 的預設介面相同。有了這個介面,用戶端便可永遠註冊為事件接收。
與 COM Coclass 的不同之處在於,Managed 類別可以包含類別成員。為了與 .NET Framework 方法一致,這項轉換會將 Coclass 實作之每一介面的關聯成員加入到每一個類別。Managed 類別的使用者可以叫用 Managed 型別的方法和屬性,而不用先轉型 (Casting) 成特定的介面。匯入處理序也會將預設的建構函式加入到每一個轉換的 Coclass。建構函式可以讓您從 Managed 程式碼建立類別 (無法建立沒有建構函式的類別)。這個預設建構函式沒有引數;它的實作會呼叫基底類別建構函式。如果將 noncreatable 型別程式庫屬性套用至 Coclass,匯入處理序將不建立這個類別的預設建構函式。
由於介面成員名稱不一定是唯一的,所以成員之間可能會發生名稱和 DispId 抵觸。TlbImp.exe 是解決名稱抵觸的方式,是在每個抵觸的類別成員名稱前面加上介面名稱和底線。當成員名稱抵觸時,Coclass 陳述式 (Statement) 中所列第一個介面會保持不變。
發生 DispId 抵觸時,匯入處理序會指派 DispId 給 Coclass 預設介面的成員,但不會指派給衝突的類別成員。不過,匯入處理序永遠會指派 DispId 給介面的成員。
型別程式庫表示
[uuid(…)]
interface INew : IDispatch {
[id(0x100)] HRESULT DoFirst();
[id(0x101)] HRESULT DoSecond();
}
[uuid(…)]
interface INewer : IDispatch {
[id(0x100)] HRESULT DoNow();
[id(0x101)] HRESULT DoSecond();
}
[uuid(…)]
coclass NewNewer {
[default] interface INew;
interface INewer;
}
轉換後的型別顯示如下:
<Guid(…)> Public Interface INew
…
End Interface
<Guid(…)> Public Interface INewer
…
End Interface
<Guid(…)> Public Interface NewNewer
Inherits INew
…
End Interface
<Guid(…)> Public Class NewNewerClass
Implements INew
Implements INewer
Implements NewNewer
' Method implementation
<DispId(100)> _
…
End Class
[Guid(…)]
public interface INew {…}
[Guid(…)]
public interface INewer {…}
[Guid(…)]
public interface NewNewer : INew {…}
[Guid(…)]
public class NewNewer : INew, INewer, NewNewer{
// Method implementation.
[DispId(100)]…
}
結構
在型別程式庫內定義的結構是匯入做為中繼資料。如果結構的某一個欄位是個參考型別,Tlbimp.exe 會將這個型別匯入為 IntPtr 並且套用 ComConversionLossAttribute。這個屬性表示資訊在匯入過程中已遺失。
列舉型別
型別程式庫匯入工具 (Tlbimp.exe)會匯入 Unmanaged 列舉型別,當做 Managed Enum 型別。
常數
在這個版本中,常數不會從型別程式庫匯入。
Typedef
型別程式庫中的型別定義 (Typedef) 不會匯入。而是將參數和欄位匯入做為基礎型別。例如,BUTTON_COLOR 型別的參數會匯入為整數型別,因為 BUTTON_COLOR 是某一個整數的別名 (Alias)。
匯入之後,這些參數和欄位會含有可將它們與 ComAliasNameAttribute 中它們原來型別關聯的資訊。轉換處理序會套用 ComAliasNameAttribute,將欄位、參數或傳回值與用作別名之程式庫中的型別程式庫名稱和型別關聯。
以下型別程式庫表示顯示 cl 參數是以 BUTTON_COLOR 做為型別,它是某一個整數的別名。
型別程式庫表示
library MyLib {
typedef [public] int BUTTON_COLOR;
interface ISee {
HResult SetColor([in] BUTTON_COLOR cl);
HResult GetColor([out, retval] BUTTON_COLOR *cl);
};
coclass See {
[default] interface ISee
};
};
轉換後的型別顯示如下:
public interface ISee {
void SetColor([ComAliasName("MyLib.BUTTON_COLOR")]] int cl);
[return: ComAliasName("MyLib.BUTTON_COLOR")] int GetColor();
};
public class See {
public void SetColor([ComAliasName("MyLib.BUTTON_COLOR")]] int cl);
[return: ComAliasName("MyLib.BUTTON_COLOR")] int GetColor();
};
請注意,BUTTON_COLOR 的實際型別在產生的中繼資料中未定義。反之,在型別程式庫中型別設定為 BUTTON_COLOR 的參數,現在型別設定為基礎型別 int,並賦予屬性 ComAliasNameAttribute。這個轉換處理序也會將該屬性套用至類別方法上的引數。