工做中寫WinForm程序常常會引用第三方的組件,包括引用Com組件,作了一個桌面程序須要展現PDF,看了些其它的開源組件對PDF的兼容性都不是很好,有些看着PDF是正常的可是複製出來的字有不少亂碼。而後就直接引用了adboe pdf reader來顯示,測試了不一樣pdf兼容性算是不錯的。那如何引用呢?程序員
在工具欄選擇項
編程
添加Com組件
找到Adobe PDF Reader勾選,而後點擊肯定以後組件就被添加到工具箱裏面了。
windows
使用Com組件
新建一個窗體或者用戶控件,將剛纔添加的Adobe PDF Reader 組件拖入到窗體中就能夠像winform控件同樣操做該控件了。
安全
在該窗體類中生成了一個AxAcroPDFLib.AxAcroPDF的控件,進入該控件類能夠看到控件類對外提供的方法,包括用於加載顯示pdf的 LoadFile 方法,gotoFirstPage 等翻頁的方法。
服務器
而該控件有一個父類AxHost類,進入Axhost類有一個摘要:網絡
包裝 ActiveX 控件,並將它們做爲功能完整的 Windows 窗體控件公開架構
對此我陷入了沉思,ActiveX控件究竟是什麼,com組件如何被使用,AxAxAcroPDFLib.AxAcroPDF類是如何生成的,Winform和Com如何互操做?因而我進行了一番資料查找和學習。編程語言
ActiveX控件技術基於由COM,可鏈接對象,複合文檔,屬性頁,OLE自動化,對象持久性以及系統提供的字體和圖片對象組成的基礎。
控件本質上是一個COM對象,它公開IUnknown接口,客戶端能夠經過該對象獲取指向其其餘接口的指針。控件能夠經過IClassFactory2和自我註冊來支持許可。
也就是說ActiveX控件是基於COM對象的,使用COM技術讓不一樣語言編寫的控件能夠進行互相調用,而如何編寫ActiveX控件呢,可使用ATL 和 MFC,可是兩個我都沒使用過!而且沒編寫過,因此我就略過,只先了解其概念。既然它是基於COM,那接下來看看COM是什麼東東。函數
Microsoft組件對象模型(COM)定義了一個二進制互操做性標準,用於建立在運行時進行交互的可重用軟件庫。您可使用COM庫,而無需將其編譯到應用程序中。COM是許多Microsoft產品和技術(例如Windows Media Player和Windows Server)的基礎。
COM定義了適用於許多操做系統和硬件平臺的二進制標準。對於網絡計算,COM爲在不一樣硬件平臺上運行的對象之間的交互定義了標準的有線格式和協議。COM獨立於實現語言,這意味着您可使用其餘編程語言(例如C ++和.NET Framework中的編程語言)建立COM庫。
COM規範提供了支持跨平臺軟件重用的全部基本概念:
組件之間的函數調用的二進制標準。
將功能強類型分組到接口中的規定。
提供多態性,功能發現和對象生存期跟蹤的基本接口。
惟一標識組件及其接口的機制。
組件加載器,可從部署中建立組件實例。
COM具備多個部分,這些部分能夠一塊兒工做以建立由可重用組件構建的應用程序:
一個主機系統提供了一個運行時環境符合的COM規範。
定義要素合同的接口和實現接口的組件。
爲系統提供組件的服務器,以及使用組件提供的功能的客戶端。
一個註冊表,用於跟蹤組件在本地和遠程主機上的部署位置。
一個服務控制管理器,能夠在本地和遠程主機上找到組件,並將服務器鏈接到客戶端。
一種結構化的存儲協議,它定義瞭如何導航主機文件系統上文件的內容。
跨主機和平臺啓用代碼重用對於COM相當重要。可重用的接口實現被稱爲組件,組件對象或COM對象。組件實現一個或多個COM接口。
您能夠經過設計庫實現的接口來定義自定義COM庫。圖書館的使用者能夠發現和使用其功能,而無需瞭解圖書館的部署和實施細節。工具
這是官方的定義,固然還有不少細節說明能夠看看https://docs.microsoft.com/zh-cn/windows/win32/com/com-technical-overview 其中包括實現的定義和方式,對象和接口、接口實現、IUnknown接口等等。
那是如何實現如何調用呢,引用一段有趣的歸納性的描述:
COM主要是一套給C/C++用的接口,固然爲了微軟的野心,它也被推廣到了VB、Delphi以及其餘一大堆奇奇怪怪的平臺上。它主要爲了使用dll發佈基於interface的接口。咱們知道dll的接口是爲了C設計的,它導出的基本都是C的函數,從原理上來講,將dll加載到內存以後,會告訴你一組函數的地址,你本身call進去就能夠調用相應的函數。
可是對於C++來講這個事情就頭疼了,如今假設你有一個類,咱們知道使用一個類的第一步是建立這個類:new MyClass()。這裏直接就出問題了,new方法經過編譯器計算MyClass的大小來分配相應的內存空間,可是若是庫升級了,相應的類可能會增長新的成員,大小就變了,那麼使用舊的定義分配出來的空間就不能在新的庫當中使用。
要解決這問題,咱們必須在dll當中導出一個CreateObject的方法,用來代替構造函數,而後返回一個接口。然而,接口的定義在不一樣版本當中也是有可能會變化的,爲了兼容之前的版本同時也提供新功能,還須要讓這個對象能夠返回不一樣版本的接口。接口實際上是一個只有純虛函數的C++類,不過對它進行了一些改造來兼容C和其餘一些編程語言。
在這樣改造以後,出問題的還有析構過程~MyClass()或者說delete myClass,由於同一個對象可能返回了不少個接口,有些接口還在被使用,若是其中一個被人delete了,其餘接口都會出錯,因此又引入了引用計數,來讓許多人能夠共享同一個對象。
其實到此爲止也並不算是很奇怪的技術,咱們用C++有的時候也會使用Factory方法來代替構造函數實現某些特殊的多態,也會用引用計數等等。COM技術的奇怪地方在於微軟實在是腦洞太大了,它們構造了一個操做系統級別的Factory,規定全部人的Interface都統一用UUID來標識,之後想要哪一個Interface只要報出UUID來就好了。這樣甚至連連接到特定的dll都省了。
這就比如一個COM程序員,只要他在Windows平臺上,調用別的庫就只要首先翻一下魔導書,查到了一個用奇怪文字寫的「Excel = {xxx-xxx-xxxx...}」的記號,而後它只要對着空中喊一聲:「召喚,Excel!CoCreateInstance, {xxx-xxx-xxxx...}」
而後呼的從魔法陣裏面竄出來了一個怪物,它長什麼樣咱們徹底看不清,由於這時候它的類型是IUnknow,這是腦洞奇大無比的微軟爲全部接口設計的一個基類。咱們須要進一步要求它變成咱們能控制的接口形態,因而咱們再喊下一條指令:
「變身,Excel 2003形態!QueryInterface, {xxx-xxx-xxxx...}」
QueryInterface使用的是另外一個UUID,用來表示不一樣版本的接口。因而怪物就變成了咱們須要的Excel 2003接口,雖然咱們不知道它其實是2003仍是2007仍是更高版本。
等咱們使喚完這隻召喚獸,咱們就會對它說「回去吧,召喚獸!Release!」可是它不必定聽話,由於以前給它的命令也許尚未執行完,它會忠誠地等到執行完再回去,固然咱們並不關心這些細節。(引用地址:https://www.zhihu.com/question/49433640)
從這個歸納理解,全部的COM類其實都繼承了IUnknown,當咱們拿到IUnknown接口後還須要轉成咱們須要使用的類型,而這個類型若是用強轉可能會出錯,可是微軟認爲,直接由用戶來轉型是不安全的須要惟一的一個標識符來肯定一個類,那麼這個標識符就是GUID。類ID就叫做CLSID,接口ID就叫做IID,還須要一個轉型的函數叫QueryInterface。QueryInterface做爲IUnknown中的一個純虛函數,作的事情其實很簡單,判斷本身能不能轉成某個GUID所指向的類而已。若是不能夠,則返回E_NOTIMPL,能夠的話返回S_OK,並將轉換後的指針做爲參數返回。
COM組件並不須要名字,或者說不須要UUID,由於咱們老是使用他裏面的接口,而不是直接使用COM組件,因此接口也要UUID。說了這麼多,COM架構這麼複雜,確定須要一箇中間層,或者說擺渡人,這就是COM Library(一堆dll) + 註冊表。A應用通知COM Library,並輸入接口的UUID,由COM Library裝入B應用的該組件對應的dll,並把接口指針返回給A應用,指針裏指示的是一堆函數指針,由這些指針,能夠調用到B應用裏的函數功能。
注:上面有時說的UUID,有時說的GUDI,UUID便是GUID值。
有了上面的ActiveX控件和Com組件的介紹,咱們再回到開始咱們如何導入的ActiveX控件。
ActiveX 控件導入程序將 ActiveX 控件的 COM 類型庫中的類型定義轉換爲 Windows 窗體控件。
Windows 窗體只能承載 Windows 窗體控件,即從 Control 派生的類。 Aximp.exe 生成可承載於 Windows 窗體上的 ActiveX 控件的包裝器類。 這使你得以使用適用於其餘 Windows 窗體控件的同一設計時支持和編程方法。
若要承載 ActiveX 控件,必須生成從 AxHost 派生的包裝器控件。 此包裝器控件包含基礎 ActiveX 控件的一個實例。 它知道如何與 ActiveX 控件通訊,但它顯示爲 Windows 窗體控件。 這個生成的控件承載 ActiveX 控件並將其屬性、方法和事件做爲生成的控件的屬性、方法和事件公開。
因而可知當咱們再工具箱裏面選擇添加com組件後實際隱含執行了該導入程序,爲咱們生成了對應的AxAcroPDFLib.AxAcroPDF包裝器控件。而AxAcroPDFLib則如同第三點中講的那樣就是COM Library。
既然AxAcroPDFLib 是擺渡人(互操做程序集) 那麼咱們能夠看到這個COM Library的引用
有了互操做程序那麼這個互操做程序必然是去調用COM組件,調用COM組件那麼UUID呢?將這個程序集放到Dnspy反編譯能夠看到在ClsidAttribute標記有{ca8a9780-280d-11cf-a24d-444553540000},構造函數裏面有UUID。
而後咱們打開註冊表查詢下對應的值和註冊表的狀況。
因此經過上面的概念瞭解和猜測驗證,基本清楚了com的設計和想法,以及ActiveX控件的調用過程。