2011-03-03 55 views
1

我讀過關於COM Programmer's Cookbook中詳述的各種COM設計模式以及一些相關的SO線程,特別是the thread discussing composition vs. multiple inheritance。可能是因爲我太新C++和COM,我可能會丟失在各種來源所以這裏提出的觀點是在一個句子表達了我的問題:擴展MIDL接口和COM對象設計

我可以延長由MIDL爲DLL內部使用生成的接口和如果是這樣,在給定MIDL/COM限制的情況下,如何正確處理鑽石問題/並行層次?

骯髒的細節...

希望幫助其他找準我的困惑可能是,這裏有我的假設:

1)COM不支持虛擬繼承,並且只允許多重繼承僅通過接口。 2)即使COM不能看到它,只要我不期望它被COM直接暴露出來,使用不受支持的C++繼承應該不是非法的。 3)因爲MIDL只允許接口的單一繼承,所以如果我有一個並行層次結構,我需要將它們聚合爲coclass。

4)MIDL不會出現申報的coclass本身,所以我需要寫一個.h文件中聲明的實際類有,根據需要與理解COM消費者不能使用它,我可以擴展(這是確定) 。

我想要做的是有一個基礎對象(我還沒有決定它是抽象的還是不是,雖然我認爲它現在),它處理大多數實現細節並委託一些特定的功能子類。客戶端通常會使用子類。所以,

project.idl

import "oaidl.idl" 
import "ocidl.idl" 

[ 
    object, 
    uuid(...), 
    dual, 
    oleautomation 
] 
interface IBase : IDispatch { 
    //stuff I want to show to COM 
}; 

[ 
    object, 
    uuid(...), 
    dual, 
    oleautomation 
] 
interface IChild1 : IBase { 
    //stuff (in addition to base) I want to show to COM 
}; 

[ 
    object, 
    uuid(...), 
    dual, 
    oleautomation 
] 
interface IChild2 : IBase { 
    //stuff (in addition to base) I want to show to COM 
}; 

[ 
    uuid(...), 
    version(...), 
] 
library myproject { 
    importlib("stdole32.tlb"); 
    interface IBase; 
    interface IChild1; 
    interface IChild2; 
    [ 
     uuid(...), 
    ] 
    coclass Base { 
     [default]interface IBase; 
     interface IOle*; //include other IOle* interfaces required for the functionality 
    }; 
    [ 
     uuid(...), 
    ] 
    coclass Child1 { 
     [default]interface IChild1; 
     interface IOle*; //those are delegated to the base members 
    }; 
    [ 
     uuid(...), 
    ] 
    coclass Child2 { 
     [default]interface IChild2; 
     interface IOle*; //those are delegated to the base members 
    }; 
}; 

base.h

#include base_h.h //interfaces generated by MIDL 

// I assume I need to re-include IOle* because IBase has no relationship 
// and C++ wouldn't know that I want the object base to also have those 
// interfaces... 
class base : public IBase, 
      public IOle* { 
    //handle all IUnknown, IDispatch and other IOle* stuff here 
    //as well as the common implementations as appropriate 
}; 

child1.h

#include base.h 

//I'm not sure if I need to re-include the IOle* interfaces... 
//I also assume that by inheriting base, child1 also inherits its interface 
class Child1 : public Base, 
       public IChild1 { 
    //specific details only, let base handle everything else. 
}; 

child2.h

#include base.h 

//I'm not sure if I need to re-include the IOle* interfaces... 
class Child2 : public Base, 
       public IChild2 { 
    //specific details only, let base handle everything else. 
}; 

概念,創建一個新的子對象*總是意味着一個創作基地對象,因爲基地將需要處理的實施細則,所以我想這也是適當的有基拿關注QueryInterface &引用計數,但我對以下幾點感到困惑:

1)編譯器抱怨成員由於並行層次而不明確; IUnknown從我的自定義接口和其他IOle *接口重新實現了好幾次。文檔表明,預計每個對象實際上只需要一個實現,但我不清楚我將如何解決編譯器的問題,我覺得做一個演職人員是不對的?我也想知道是否應該讓所有接口實際上繼承哪個似乎對於C++是有效的,儘管COM不會有這樣的理解,但它不應該在意(?)。 2)然而,如果我在.h文件中聲明所有繼承接口爲虛擬的,編譯器會抱怨當我嘗試在Base class.cpp中實現QueryInterface時,不允許繼承成員。我一直在搜索這個錯誤,但我不清楚它在這裏試圖告訴我什麼。

編輯:我回答了我自己的問題。 Intel had documentation對於這個錯誤,我最初沒有點擊鏈接,假設它可能不適用於Visual Studio。我希望我無論如何做了,但現在我明白了爲什麼我得到這個錯誤,因爲我試圖在Base ::中執行所有的實現,而不是IUnknown ::或IDispatch ::。這現在引發一個新的問題,可能會澄清我的原始&主要問題 - 如何將實施從IUnknown(和其他人)推遲到基地,並只從基地工作,如果甚至可能?看起來,如果我只是使用IUnknown :: xxx,它不再可以訪問Base的私有成員,這似乎理智的事情期望,以便可能不是我想要的。我試圖聲明所有其他接口,除了基地自己的接口爲虛擬接口,但這並不真正需要。 (同樣,這可能是我的經驗沒有看到明顯的解決方案。)

3)Base的QueryInterface不能將基礎投射到一個孩子,這是一個合理的投訴,所以我假設我必須重新實現所有孩子的QI,但一旦我確定所請求的接口不是孩子的,我可以委託回基地的QI。奇怪的是,編譯器堅持認爲child * class是抽象的,因爲缺少IUnknown & IDispatch的成員,但是基礎沒有實現,因此孩子也應該擁有這些成員?

各種編譯器錯誤讓我擔心,我有一種或兩種語言的&框架是導致我做出我怎麼能設計出一個COM有缺陷的假設缺乏經驗對象&繼承層次結構和實現方式,我決然丟失這裏的東西。任何指針,即使頭上一巴掌,將不勝感激。

謝謝!

回答

5

在這裏你已經有了正確的想法,你所缺少的就是在派生最多的課堂上束縛一些鬆散的目標。作爲一名COM開發人員,您期望所有的AddRef/Release/QI在類對象上都是相同的;但是面向C++的編譯器並不知道,所以將它們視爲可能分離。你在這裏的兩個impls是Base中的那個,以及你添加的任何接口中的那個。在這裏直接設置編譯器非常簡單:在派生類最多的地方,重新定義所有的IUnknown方法,並將它們定向到合適的基類 - 例如。

class ChildX: public Base, 
       public IChildA 
       ... more COM interfaces if needed ... 
{ 
    ... 

    // Direct IUnknown methods to Base which does the refcounting for us... 
    STDMETHODIMP_(ULONG) AddRef() { return Base::AddRef(); } 
    STDMETHODIMP_(ULONG) Release() { return Base::Release(); } 
    ... suggest implementing QI explicitly here. 
} 

這基本上說,所有的方法叫的AddRef,無論他們如何在ChildX結束了,會得到具體實施。

它最簡單的在這裏實際實現QI,並且只委託AddRef/Release到Base。 (從技術上講,Base可以使用static_cast投射到Child,但是在完全定義Child之後,您需要將代碼放入函數中;但不建議這樣做,因爲基類很少有理由知道從它派生的類。)

其他需要注意的事項:確保Base有一個虛擬dtor聲明 - 即使只是空的,以便當Base執行'delete this'時,ref將變爲0,它將調用dt中的dtors派生類和它們分配的任何資源都會得到適當的清理。另外,一定要讓ref的計數正確,並且如果需要的話,線程安全;請檢查COM書籍的任何好介紹(例如「Inside Distributed COM」,儘管名稱是以普通COM開頭的),以查看其他人如何做到這一點。

這是一個非常常見的COM語言,許多框架使用#define宏或更多派生的模板類在派生類最多的AddRef/Release/QI中添加(如MFC一樣),然後把這些委託給一個處理大部分家務的知名基類。

+0

下決心是否願意解釋這裏發生了什麼問題,或者他們會如何做到這一點? – BrendanMcK 2011-08-26 08:31:42

+1

+1打擊巨魔/仇敵。對此迴應做出很大努力。如果不是100%完美,這是一個很好的開始。 – kevinarpe 2012-06-05 16:50:39