1. 程式人生 > >COM編程_第一講_深入COM框架以及實現簡單的COM

COM編程_第一講_深入COM框架以及實現簡單的COM

dir 微軟 高級 原理 padding 不知道 簡單的 out interface

一丶我們要理解COM是什麽(為什麽理解)

現在很多人會用com(也就是ALT)但是不知道原理,如果改一點東西,那麽整體的框架重來,因為你不懂改哪裏,如果懂了,那麽遇到問題,那麽就會知道我要怎麽做,是什麽問題了

二丶什麽是COM

COM是微軟公司為了計算機工業的軟件生產更加符合人類的行為方式開發的一種新的軟件開發技術。在COM構架下,人們可以開發出各種各樣的功能專一的組件,然後將它們按照需要組合起來,構成復雜的應用系統。由此帶來的好處是多方面的:可以將系統中的組件用新的替換掉,以便隨時進行系統的升級和定制;可以在多個應用系統中重復利用同一個組件;可以方便的將應用系統擴展到網絡環境下;COM與語言,平臺無關的特性使所有的程序員均可充分發揮自己的才智與專長編寫組件模塊;等等。 COM是開發軟件組件的一種方法。組件實際上是一些小的二進制可執行程序,它們可以給應用程序,操作系統以及其他組件提供服務。開發自定義的COM組件就如同開發動態的,面向對象的API。多個COM對象可以連接起來形成應用程序或組件系統。並且組件可以在運行時刻,在不被重新鏈接或編譯應用程序的情況下被卸下或替換掉。Microsoft的許多技術,如ActiveX, DirectX以及OLE等都是基於COM而建立起來的。並且Microsoft的開發人員也大量使用COM組件來定制他們的應用程序及操作系統。

好,這是百度的答案,很多人看了懵逼,簡單來說

總結:

  1.COM是一種框架,我們可以利用這個框架,實現跨平臺開發,比如你開發了一個COM,那麽別的程序一樣使用

  2.COM其實是二進制下的可執行的程序,可以給其他的程序使用

實現簡單的COM從接口設計模式開始

什麽是接口模式

1.接口模式就是我們不知道,但是當用戶用的時候,才知道是什麽類型,所以可以是已知的,規範一下接口即可.

簡單來說:

  接口模式就是類似於U盤 插入到電腦上,中間的USB的那個接口,只要是支持這個接口的,都可以插入到電腦上

比如硬盤等等.

2.插件模式: 插件模式是未知的,比如用戶怎麽寫你都是不知道的,所以定義好規範,讓用戶一一的實現你的插件的接口即可.

簡單來說:

  簡單來說就是為你的程序提供的擴展,如果用戶實現了你自定義的接口,那麽你的應用程序就可以支持這個功能了.所以插件和接口不要搞混

實現簡單的COM以及思路

1.按照上面所說的,我們要實現COM那麽就要有一個接口,這裏我用C++來寫了,

2.在寫的過程中,我會依次的把為什麽這樣寫,不能怎麽寫都會說清楚,最後開發一個跨語言使用的ATL(也就是COM)組件

3.下面的內容可能有點多,最後我會寫總結,可以看下.雖然函數不多,就一個類,但是從底層講起,為什麽這麽做所以比較多.

1.定義接口類

1 2 3 4 5 6 7 class IUnKnow { public:
virtual HRESULT QueryInterFace(const GUID& riid,void **ppObject) = 0; virtual ULONG AddRef() = 0; virtual ULONG Release() = 0; }

首先將第一個接口中的函數

QueryInterFace,這個函數是查找我們的接口,根據查找的接口通過第二個OUT參數接受查詢接口的實現類的對象

什麽意思?(GUID下面講解)

  其實就是我定義了一個新的接口類,繼承了IUnKnow,這個新的接口類中有自己新添加的功能,而有一個類是實現了這個接口類,通過這個函數,可以找到實現類的對象,進而可以調用裏面的方法(下面講解)

為什麽要這樣寫返回值,以及參數要這樣寫:

  想一下,如果我們返回值是void *的話是不是不需要第二個參數了,是不需要第二個參數了,但你保證所有的語言

  都會這樣返回嗎,顯然是不會的,所以要統一接口,統一返回值(HRESULT)參數由第二個傳出

AddRef() 引用計數 這個必須加,因為你想,如果我們每次查詢是否存在就new一個對象,那樣是不是太浪費了

所以搞個引用計數

Release();大家可能不同了,為什麽釋放資源要單獨寫一個Release()釋放,這裏請看下面講解

接口的設計原則

1.接口一旦是定義好的,你的函數的順序不能改變 為什麽?

  因為接口的設計都是用的指針,都是虛表去查(虛表是什麽,可以補一下C++的基礎,簡單來說就是通過虛函數來調用的)如果一單你的接口的順序改變了,那麽對應的虛表就會改變,

舉個例子:

  比如你的插件(也就是咱們現在寫的這個)有一個功能是Add(int n1,int n2,long *Result) (兩個數相加)

你編譯好了你的插件了,我的Client程序就可以使用了,使用的時候正常調用Add,返回的結果也就正確了,

如果有一天你有一個減法,正好放在的Add的前邊,那麽你的Client就會調用減法了,因為以前的那個位置是輸入Add的這樣就會出錯了.

2.參數不能改變

  參數也是不能改變的,接口一點定義了,就不要動了.

3.兼容性

  什麽是兼容性

  比如你的Client是老版本,用你編寫的新插件,你的Add函數沒有變,還是會依次調用你的函數,不影響使用.

但是你要反過來想,當你的Client是新版本的時候,調用你的舊的插件怎麽辦,比如你的舊的插件沒有Sub函數

而你調用了是不是就出錯了.

所以為了保證兼容,我們會新定義一個接口類,讓以前的實現類繼承新的接口類,而新的接口類繼承以前的接口類

偽代碼:

  新版本的插件要這樣寫

class InterFaceMathOld : IUnKnow
{
     virtual Add(...)     = 0;
}
class InterFaceMathNew : pulic InterFaceOld
{
    virtual Sub(....) = 0;    
}
class CMath : public interfaceMathNew
{
    Query....(){}
    AddRef(){}
    Release(){}
    Add(...){}
    Sub(...){}        
}

老版本的插件是這樣寫

class InterFaceMathOld : IUnKnow
{
     virtual Add(...)     = 0;
}

class CMath : public InterFaceOld
{
    Query....(){}
    AddRef(){}
    Release(){}
    Add(...){}  
}

這些代碼我都會寫好,發到雲盤中下載研究,如果連接過時了,請評論告知,或者QQ2510908331 這地方一定要細看,

我會一步步的吧Com從基本到高級的源碼寫出來,這樣能熟悉一下COM的框架

接口設計的細節問題 (解決為什麽要用Release)

1名稱粉碎

.我們想一下,C語言的函數,看一下二進制或者DLL,是不是都是原本什麽樣子就是什麽樣子

C++的函數有一個語法支持重載了,內部怎麽支持的重載,這就是一個問題

C++為了支持重載,會對你定義的函數做一個名稱粉碎,也就是加了一些額外的符號,比如我們調試中都會遇到一種錯誤叫做,找不到外部符號 例如[email protected]當然我寫的不準確,下面有一個帖子專門介紹,可以看看

http://www.cnblogs.com/zhugehq/p/5959360.html

不同的編譯器它的名稱粉碎是不一樣的,這個是沒有標準的,你說有重載,這個是標準可以,但是怎麽實現的,沒有標準

所以我們為什麽要定義為純虛函數,這樣你要調用函數就直接通過虛表,去查找了,而不是找你的實現了,所以我們的接口沒有重載,不能寫任何重載的函數

從逆向角度說一下,為什麽不支持重載,因為VC++6.0編譯器,在建立虛表的時候,會根據函數的類型排序,上面的接口原則說了,不能影響順序問題,所以不能寫

2.多重繼承問題

  a.多重繼承會影響虛表的,一旦影響了虛表,就改變的接口的不變原則,(虛繼承也是一樣).

  b.不能有虛析構,如果你調用虛析構,VC++6.0會傳入一個1,或者一個0(後臺傳入的,逆向角度)這個根據這個狀態值去釋放內存.

  而GCC是不一樣的,他可以用數組的方式,反正都可以實現,那麽這樣也違反了接口的設計原則了.

總結:

  1.不能用多重繼承(父類沒有虛函數可以多重繼承,不影響子類的虛表即可)

  2.不能用虛析構,如果釋放內存,則用純虛方法的Release()來釋放內存

  3.不能有重載,重載會影響虛表

GUID簡介

GUID是一種數據結構,在Windwos系統中可以通過guidgen命令來打開GUID,也可以通過API coCreateguid(查一下MSDN)我都是用windows自帶的

作用: 我們第一個查找接口的函數中定義了Guid,這是為了我們查詢接口準備的,每一個接口都會有一個GUID,guid是保證不重復的.

總結:

  說了怎麽多,我們發現其實定義一個COM的接口很容易,就是2個接口,(IUnknow(頂級的接口類),Ixxx(你自己的接口類))和一個實現的接口類,我們就要說怎麽多細節.當然這也是為了我們更加的理解COM的設計.

如有問題QQ:2510908331 論壇:www.w1x8.com 如果說的有錯誤,請指出,如果你有更好的建議,請評論說明,因為COM組件很老的,很多人會用,但是不懂,因為我們是逆向工程,所以需要熟悉COM框架,為了誰讓更多人熟悉COM框架,請歡迎指出

框架代碼連接: 鏈接:http://pan.baidu.com/s/1dFkvwJn 密碼:myie

為了讓大家學習方便,所以沒有寫引用計數和釋放內存,所以框架代碼釋放內存可能會出錯,請先掌握這個第一個版本的框架代碼,後續會慢慢優化,基於這個框架代碼.配合博客文章,能更深的理解COM框架

COM編程_第一講_深入COM框架以及實現簡單的COM