大四的課還是蠻有意思的,其中有一個COM組件的課,感覺挺有收穫,做了一個小Demo,是關於COM彙總的,
彙總和包容是COM的基本特徵,老師上課沒把這倆的關係講清楚,也不大知道為啥放著簡單的包容不用,用彙總寫模組。
通過做這個Demo,俺算是理解了。因為包容只是單向調用介面,也就是從外到內,不能從內到外。而彙總則是雙向調用的,
非常的靈活。下面通過具體的例子說說。
這個小Demo是控制台調用三個動態連結程式庫的,每個連結庫中都有一個方法,最外面的庫CompB.dll裡面是減法運算,
彙總CompC.dll裡面的乘法,CompC.dll彙總CompA.dll裡面的加法。CompB.dll調用介面是OtherInterface,CompC.dll
調用介面是AnyInterface,CompA.dll調用介面是SomeInterface。 CompB內部指標Inner指向CompC,CompC內部指標
Inner指向CompA,Outer指標指向CompB,CompA內部指標Outer指標指向CompC,通過這四個指標,可以使介面查詢
雙向傳遞。也就是說我可以通過OtherInterface拿到CompA的Add方法,也可以通過SomeInterface拿到CompB的Minus
方法,調用方式非常靈活。三個介面無論調用哪一個都可以訪問到三個動態連結程式庫中的所有方法。每一個方法都可以通過不同
介面調用,原理也很清晰和簡單。多層彙總調用的中介層dll就需要一對指標指向上層dll和下層dll,就像CompC一樣。實現也不
複雜,每個動態連結程式庫都有一個查詢介面方法,CompC和CompA通過非委託查詢介面,進行調用傳遞。初始化過程是這樣的,
CompB初始化CompC執行個體,CompC初始化CompA執行個體。CompB如果希望調用CompA就必須先初始化CompC,初始化過
程也很清晰。為了模組的靈活性,多寫些代碼還是值得滴。
基本原理
我本地的執行輸出:
多層彙總嵌套核心方法是CompC的兩個查詢方法:
QueryInterface:
1 HRESULT CC::QueryInterface(const IID& iid, void **ppv)
2 {
3 printf("---------------------excute CompC QueryInterface!!!\n");
4 if ( iid == IID_OtherInterface )
5 {
6 if(m_pUnknownOuter != NULL){
7 printf("CompC m_pUnknownOuter is not null!\n");
8 return m_pUnknownOuter->QueryInterface(iid, ppv);
9 }else{
10 return NondelegationQueryInterface(iid, ppv);
11 }
12 }
13 if ( iid == IID_SomeInterface ) {
14 if ( m_pUnknownInner != NULL ){
15 printf("CompC m_pUnknownInner is not null!\n");
16 return m_pUnknownInner->QueryInterface(iid, ppv);
17 } else{
18 return NondelegationQueryInterface(iid, ppv);
19 }
20 } else {
21 return NondelegationQueryInterface(iid, ppv);
22 }
23 }
NondelegationQueryInterface:
1 HRESULT CC::NondelegationQueryInterface(const IID& iid, void **ppv)
2 {
3 printf("---------------------excute CompC NondelegationQueryInterface!!!\n");
4 if ( iid == IID_IUnknown )
5 {
6 *ppv = (INondelegatingUnknown *) this ;
7 ((IUnknown *)(*ppv))->AddRef() ;
8 } else if ( iid == IID_AnyInterface )
9 {
10 *ppv = (IAnyInterface *) this ;
11 ((IAnyInterface *)(*ppv))->AddRef() ;
12 } else if ( iid == IID_SomeInterface )
13 {
14 return m_pUnknownInner->QueryInterface(iid, ppv) ;
15 } else if ( iid == IID_OtherInterface )
16 {
17 return m_pUnknownOuter->QueryInterface(iid, ppv) ;
18 } else
19 {
20 *ppv = NULL;
21 return E_NOINTERFACE ;
22 }
23 return S_OK;
24 }
網上這方面東西不多,發篇文章填補一下。 為這方面不太清楚的同學提供下協助。