如何找出「運行時類」實現的接口?
Windows元數據文件由關係數據庫組成:它基本上是一組相互關聯的表。您可以在ECMA 335的分區II中找到邏輯模式的規範。
每個運行時類和接口都由元數據庫的TypeDef表中的行表示。 「類型X實現接口I和J」關係由InterfaceImpl表中的行表示,該表將類型定義映射到它們實現的接口。
計算由類型實現的一組接口是很困難的,原因很多。假設我們要計算由XAML控件實現的一組接口。以下所有步驟都需要:
查找和加載定義Button
元數據文件。您可以撥打RoGetMetaDataFile
來完成此操作。
查找Button
類型的元數據令牌。這實際上是該類型的唯一標識符。你可以致電FindTypeDefByName
。
我們需要計算所有由Button
類型直接實現的接口。這可以通過撥打EnumInterfaceImpls
來完成。
Button
類型源自ButtonBase
類型。這由TypeDef表的擴展字段指定,您可以通過調用GetTypeDefProps
獲取該字段。我們需要計算它實現的所有接口。然後我們需要在類層次結構中一直這樣做 - 對於Button
,這有很多類型:ContentControl
,Control
,FrameworkElement
,UIElement
和DependencyObject
。
請注意,其中一些類型可能在另一個元數據文件中定義。在這種情況下,InterfaceImpl將通過TypeRef標記引用接口,而不是TypeDef標記。 TypeRef表包含對類型的引用。您需要解析該引用,加載正確的元數據文件,並在該元數據文件中查找目標類型。這可以使用我們已經爲Button
執行的步驟完成。
每個接口也可以實現其他接口,因此對於我們迄今爲止在我們的列表中已經累積的每個接口,我們需要獲得接口組需要的一組接口。這是通過遞歸獲取每個接口的InterfaceImpls來完成的。
請注意,正如基類的情況一樣,實現的接口也可以在另一個元數據文件中定義。所以,你需要解決這些問題,使用與基類相同的過程。
對於額外的挑戰,Windows運行時支持通用接口。如果一個接口實現了一個實例化的通用接口,那麼這個實例化的接口將由TypeSpec表中的一行表示,並且將伴隨blob流中的簽名。您需要解析簽名。這樣做的過程是相當複雜的,但數據格式在ECMA 335
完全確定在這一點上,你就會有一組完整的類型實現的接口。可以通過檢查與Button
類型直接關聯的InterfaceImpls找到默認接口:默認接口將應用DefaultAttribute
(注意,自定義屬性應用於InterfaceImpl,而不是接口類型)。您可以使用EnumCustomAttributes
來枚舉每個InterfaceImpl的自定義屬性。
所以,這是一項非常多的工作,而且它需要大量的代碼。我發現IMetaDataImport
界面和它的朋友處理起來不太友好,所以我寫了我自己的升級版許可CxxReflect library。 CxxReflect元數據庫提供比IMetaDataImport
更好的類型檢查,幷包含解析簽名所需的邏輯。它的速度不及IMetaDataImport
,但速度很快。
CxxReflect反射庫包含了大部分用於跨元數據文件邊界解析類型和用於與Windows運行時集成的邏輯。它仍處於Alpha質量階段,但對於常見任務(它目前正在進行一些重構,以使得類型解析邏輯可反覆使用而非反射)使用起來效果很好。
你當然可以自己實現邏輯來做到這一點,但它非常乏味。在CxxReflect的開發過程中,我發現了另外一個我沒有考慮過的並且不得不做大量返工的角落案例。規範是完整的,但事情應該如何運作並不總是顯而易見的。
順便說一下,與DefaultAttribute接口將是RuntimeClass用作API參數時使用的接口。 –
感謝:EnumInterfaceImpls實際上是我以後的。在我的情況下,所有其他的繼承情況都會自然而然地消失,因爲它會映射到多重繼承(在Python中)。 –
@Martinv.Löwis:你在建立一個Python語言投影嗎? –