2010-07-06 47 views
5

假設我有一個類實現兩個或多個COM接口(完全一樣here):使用隱式轉換爲上傳而不是QueryInterface()合法與多繼承?

class CMyClass : public IInterface1, public IInterface2 { 
}; 

QueryInterface()必須返回相同的指針相同的接口的每個請求(它需要一個顯式上溯造型適當指針調整) :

if(iid == __uuidof(IUnknown)) { 
    *ppv = static_cast<IInterface1*>(this); 
    //call Addref(), return S_OK 
} else if(iid == __uuidof(IInterface1)) { 
    *ppv = static_cast<IInterface1*>(this); 
    //call Addref(), return S_OK 
} else if(iid == __uuidof(IInterface2)) { 
    *ppv = static_cast<IInterface2*>(this); 
    //call Addref(), return S_OK 
} else { 
    *ppv = 0; 
    return E_NOINTERFACE; 
} 

現在的目標是2 IUnknown秒 - 一個是IInterface1的基礎,另一個是IInterface2基礎。和。

讓我們假設我打電話QueryInterface()IInterface2 - 當我打電話QueryInterface()IUnknown返回將從指針不同的指針返回。到現在爲止還挺好。然後,我可以將檢索到的IInterface2*轉換爲接受IUnknown*的任何函數,並且感謝C++隱式轉換,指針將被接受,但將檢索QueryInterface()不同於指針。實際上,如果該函數在調用後立即調用QueryInterface(),IUnknown它將檢索不同的指針。

這是合法的COM?當我有一個指向多重繼承對象的指針並且允許隱式預測時,我該如何處理情況?

+0

實際上,我從來沒有見過任何代碼使用COM標識規則。也就是說,另一個IUnknown *不合法 - 您必須選擇一個從QueryInterface返回。 就你自己的,內部的C++使用COM對象實現對象而言 - 如果你投你不做COM,那麼你所做的任何事情都是合法的C++,而不是Legitimage COM。 – 2010-07-06 11:26:53

+0

@Chris Becke:我猜身份是實現一些類似緩存的功能所必需的。 – sharptooth 2010-07-07 07:27:38

回答

3

COM沒有關於接口標識的規則,只有對象標識。 QI的第一條規則是如果兩個接口指針上的IID_Unknown上的QI必須返回相同的指針(如果它們由相同的對象實現)。您的QI實施可以正確執行此操作。

如果沒有接口標識的保證,COM方法不能認爲它獲得了相同的IUnknown指針傳遞,它會在該指針上調用QI時檢索它。所以如果需要證明對象身份,那麼需要單獨的QI。

2

看來還有一個小小的誤區。接口IInterface1IInterface2是抽象的。 IInterface1IInterface2沒有單獨的QueryInterface()。只有一個聲明,類CMyClass將實施IInterface1IInterface2CMyClass的方法集合方法IInterface1IInterface1在一起)的所有方法。

所以,你在班裏CMyClass一個QueryInterface()實施,一個AddRef()一個Release()方法。在QueryInterface()中,您將指向CMyClass的實例的指針投射到static_cast<IUnknown*>

已更新:嗨!寫完我的回答後,我不得不馬上開車。只有現在我可以閱讀所有其他答案,並可以添加一些東西給我的答案。

好的。你說如果你投了IInterface1IUnknown,如果你投IInterface2IUnknown你會收到兩個不同的指針。你是對的!但不過沒關係。如果您比較包含指針(在兩種情況下地址都有QueryInterface(),AddRef()Release()),您將看到兩個指針都具有相同的包含。所以我也是對的!

有很多很好的例子,說明如何在純C中實現COM。在這種情況下,您需要定義一個靜態結構,指向虛擬函數的指針,如QueryInterface(),AddRef()Release(),並將結構的指針返回爲QueryInterface()的結果。它再次表明,只有你給出的包含對於COM而言是重要的,而不是一個指針(它是不重要的)。

還有一點小話。在一些評論中,你寫了關於方法QueryInterface(),AddRef()Release()的許多實現的可能性。我在這裏不同意。原因是接口是純抽象類,如果您定義了實現某些接口的類,則不會有典型的類繼承。您只有一個類,從所有接口執行所有功能。如果你在C++中這樣做,那麼編譯器會創建並填充靜態vtables,其中包含相應的指針,指向函數QueryInterface(),AddRef(), Release()等的唯一實現,但是所有vtables都指向相同的函數。

爲了減少微軟推出的vtables的數量__declspec(novtable)ATL_NO_VTABLE,但這不是你問題的一部分。

+0

是的,我知道。但是有一個正式的問題。當我隱式從'IInterface2'上傳到'IUnknown'時,我得到的指針與我從'QI()'得到的指針不同。整個問題是關於一件事情:就COM而言,「IUnknown」是否「合法」? – sharptooth 2010-07-06 07:20:41

+0

這些方法有許多實現,即使除一個方法外,其他方法都會將調用轉發給實現它的單個版本。 – 2010-07-06 07:38:35

2

由於Hans指出,你的執行QueryInterface是正確的。

COM對象的用戶的責任是始終使用QueryInterface。原因正是爲了防止您指出的情況:將IInterface1 *或IInterface2 *指針投射到IUnknown *指針將產生不同的物理值。

在C++中,實現者不可能阻止用戶做錯。

是否會導致應用程序失敗取決於應用是否在乎有關身份比較COM對象。

MSDN: The Rules of the Component Object Model

對象的身份。這是要求 到任何調用QueryInterface任何 接口上給定對象實例 特定接口的IUnknown 必須始終返回相同的物理 指針值。這使得調用 的QueryInterface(IID_IUnknown,...)上 任何兩個接口,比較 結果以確定它們是否 點到 對象(同COM對象的身份)的同一個實例。

由於Oleg指出,對象身份的失敗將有一個相當有限的影響,因爲COM接口部件的調用是基本上不受影響 - 如果函數簽名相匹配的每個虛擬表項將指向相同的函數地址。

所有COM智能指針實現使用QueryInterface時轉換到不同的接口,或噹噹前接口是可疑的。他們的比較運算符在每個輸入指針上自動使用QueryInterface(IID_IUnknown, ...),以獲得可以直接比較的物理IUnknown *指針。如果您選擇在應用程序中使用原始指針,對象身份的失敗將開始影響您的應用程序。

失敗不會顯示的一個特例是類沒有任何鑽石繼承。但是,無論是否使應用程序崩潰,隱式轉換在COM中都是非法的。

+0

我讀過規則,但是......他們說'QueryInterface()',而不是別的,必須遵循標識要求。這真的意味着一個隱式的預報是非法的嗎? 'QI()'是'QI()',C++轉換是C++轉換。 – sharptooth 2010-07-06 07:31:00

+0

我不同意。引用關於*對象標識*,與接口標識不同。 – 2010-07-06 07:39:18

+1

@sharptooth:在COM中思考時,儘量遠離IInterface1從IUnknown繼承的思想。更準確的說法是:IInterface1包含成員AddRef,Release和QueryInterface以及它自己的函數。 – rwong 2010-07-06 07:39:36

0

如果你有interface IInterface1 : IDispatchinterface IInterface2 : IDispatch然後QIIUnknownIInterface1IInterface2必須返回每個對象的身份規則相同的指針

但是......

QIIDispatchIInterface1可以返回比較不同的實現QI爲。

所以答案是(再次)它取決於。對於上傳到IUnknown的負面影響,可能會對上傳到其他任何內容產生積極影響。