工做中写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控件的调用过程。