1、WinForm引用Adobe PDF Reader

工作中写WinForm程序经常会引用第三方的组件,包括引用Com组件,做了一个桌面程序需要展示PDF,看了些其它的开源组件对PDF的兼容性都不是很好,有些看着PDF是正常的但是复制出来的字有很多乱码。然后就直接引用了adboe pdf reader来显示,测试了不同pdf兼容性算是不错的。那如何引用呢?

  • 在工具栏选择项

  • 添加Com组件

    找到Adobe PDF Reader勾选,然后点击确定之后组件就被添加到工具箱里面了。

  • 使用Com组件

    新建一个窗体或者用户控件,将刚才添加的Adobe PDF Reader 组件拖入到窗体中就可以像winform控件一样操作该控件了。

在该窗体类中生成了一个AxAcroPDFLib.AxAcroPDF的控件,进入该控件类可以看到控件类对外提供的方法,包括用于加载显示pdf的 LoadFile 方法,gotoFirstPage 等翻页的方法。



而该控件有一个父类AxHost类,进入Axhost类有一个摘要:

包装 ActiveX 控件,并将它们作为功能完整的 Windows 窗体控件公开

对此我陷入了沉思,ActiveX控件到底是什么,com组件如何被使用,AxAxAcroPDFLib.AxAcroPDF类是如何生成的,Winform和Com如何互操作?于是我进行了一番资料查找和学习。

2、ActiveX控件

ActiveX控件技术基于由COM,可连接对象,复合文档,属性页,OLE自动化,对象持久性以及系统提供的字体和图片对象组成的基础。

控件本质上是一个COM对象,它公开IUnknown接口,客户端可以通过该对象获取指向其其他接口的指针。控件可以通过IClassFactory2和自我注册来支持许可。

也就是说ActiveX控件是基于COM对象的,使用COM技术让不同语言编写的控件可以进行互相调用,而如何编写ActiveX控件呢,可以使用ATL 和 MFC,但是两个我都没使用过!并且没编写过,所以我就略过,只先了解其概念。既然它是基于COM,那接下来看看COM是什么东东。

3、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值。

4、Aximp.exe(Windows 窗体 ActiveX 控件导入程序)

有了上面的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。

5、验证

既然AxAcroPDFLib 是摆渡人(互操作程序集) 那么我们可以看到这个COM Library的引用

有了互操作程序那么这个互操作程序必然是去调用COM组件,调用COM组件那么UUID呢?将这个程序集放到Dnspy反编译可以看到在ClsidAttribute标记有{ca8a9780-280d-11cf-a24d-444553540000},构造函数里面有UUID。

然后我们打开注册表查询下对应的值和注册表的情况。



6、总结

所以通过上面的概念了解和猜想验证,基本清楚了com的设计和想法,以及ActiveX控件的调用过程。

  1. Activex控件时COM实现的一种方式。
  2. Activex控件通过VS工具引用时调用了Aximp.exe 。
  3. Aximp.exe程序生成了互操作程序集AxAcroPDFLib,同时生成可承载于 Windows 窗体上的 ActiveX 控件的从 AxHost 派生的包装器控件。
  4. 调用AxAcroPDF方法时通过com组件调用引用控件的功能。

WinForm引用ActiveX组件,对Com组件的学习的更多相关文章

  1. Winform开发全套31个UI组件开源共享

    一.前言 这套UI库是上一个公司(好几年前了)完成的.当时主要为开发公司内部ERP系统,重新设计实现了所有用到的Winform组建,包括Form窗体组建6个(支持换肤),基础控件25个.其中有很多参考 ...

  2. 二、Vue组件(component):组件的相互引用、通过props实现父子组件互传值

    一.组件各部分说明及互相引用 1.一个vue组件由三个部分组成 Template 只能存在一个根元素 2.Script 3.Style scoped:样式只在当前组件内生效 1.1 组件的基本引用代码 ...

  3. {VS2010C#}{WinForm}{ActiveX}VS2010C#开发基于WinForm的ActiveX控件

    在VS2010中使用C#开发基于WinForm的ActiveX控件 常见的一些ActiveX大部分是使用VB.Delphi.C++开发,使用C#开发ActiveX要解决下面三个问题: 使.NET组件可 ...

  4. 谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo

    前言 前端已经过了单兵作战的时代了,现在一个稍微复杂一点的项目都需要几个人协同开发,一个战略级别的APP的话分工会更细,比如携程: 携程app = 机票频道 + 酒店频道 + 旅游频道 + ..... ...

  5. JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐(二)

    前言:上篇 JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐 分享了几个项目中比较常用的组件,引起了许多园友的关注.这篇还是继续,因为博主觉得还有几个非常简单.实用的组件,实在不愿自己 ...

  6. JS组件系列——表格组件神器:bootstrap table

    前言:之前一直在忙着各种什么效果,殊不知最基础的Bootstrap Table用法都没有涉及,罪过,罪过.今天补起来吧.上午博主由零开始自己从头到尾使用了一遍Bootstrap Table ,遇到不少 ...

  7. JS组件系列——表格组件神器:bootstrap table(二:父子表和行列调序)

    前言:上篇 JS组件系列——表格组件神器:bootstrap table 简单介绍了下Bootstrap Table的基础用法,没想到讨论还挺热烈的.有园友在评论中提到了父子表的用法,今天就结合Boo ...

  8. JS组件系列——表格组件神器:bootstrap table(三:终结篇,最后的干货福利)

    前言:前面介绍了两篇关于bootstrap table的基础用法,这章我们继续来看看它比较常用的一些功能,来个终结篇吧,毛爷爷告诉我们做事要有始有终~~bootstrap table这东西要想所有功能 ...

  9. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)

    原文 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习) Mono.Cecil是一个强大的MSIL的注入工具,利用它可以实现动态创建程序集,也可以实现拦截器横向切入动态方法,甚至还 ...

随机推荐

  1. Vegas实战——如何导入导出视频

    Vegas作为一款专业的视频非编软件,在国内受到了很多用户的喜爱.小编认为,对于很多用户来说,他们选择sony vegas的一个原因是vegas在不论是从产品性能,还是使用效果上,都很容易被用户接受. ...

  2. 使用ABBYY FineReader将文档保存为电子书形式

    运用ABBYY FineReader 15的OCR识别技术,不仅能将PDF文档.图像.扫描页面等保存为可编辑的格式,方便用户的进一步编辑使用:而且还能直接转换为电子书的格式,方便用户使用更为便携的电子 ...

  3. 如何用EasyRecovery恢复U盘乱码的文件

    大多数用户在遇到U盘文件乱码的情况时,都显得有点手足无措,甚至会删除乱码文件,这样就导致了乱码文件数据的丢失.针对这种情况,我们应该怎么正确处理呢? 图1:U盘乱码 U盘文件乱码原因: 想要解决问题, ...

  4. H5系列之常用的语义元素

    H5添加了几个新标签,带有语义化的标签,像我们的div 和 span 标签,你说他两能干嘛呢, 好像他两什么事都能干.举个例子,你家里的房子,有几个房间,如果不分房间的话,是不是你 今天睡这里,明天睡 ...

  5. windows安装redis扩展

    Thread Safety enabled 打开phpinfo() 看php版本是ts还是nts,  如上是ts版本的,所以需要安装redis的ts版本, redis的扩展下载地址 https://p ...

  6. 【CF620E】New Year Tree

    (题面来自luogu) 题意翻译 你有一棵以1为根的有根树,有n个点,每个节点初始有一个颜色c[i]. 有两种操作: 1 v c 将以v为根的子树中所有点颜色更改为c 2 v 查询以v为根的子树中的节 ...

  7. 红黑树、TreeMap、TreeSet

    事先声明以下代码基于JDK1.8版本 参考资料 大部分图片引自https://www.jianshu.com/p/e136ec79235c侵删 https://www.cnblogs.com/skyw ...

  8. LeetCode 020 Valid Parentheses

    题目描述:Valid Parentheses Given a string containing just the characters '(', ')', '{', '}', '[' and ']' ...

  9. 「考试」CSP-S 2020

    乱扯 爆炸的过程是这样的 写了\(2.5h\)的\(T1\)过不去大样例,自闭了 决定调\(T2\)然后过了样例但事实上写的完全是假的 这个时候突然\(T1\)灵光一闪就没再看\(T2\)了 然后就一 ...

  10. Beta冲刺——用户测试报告

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 Beta 冲刺 这个作业的目标 团队进行Alpha冲刺 作业正文 正文 其他参考文献 无 用户测试报告 用户 ...