一丶我们要理解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.定义接口类

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(...) = ;
}
class InterFaceMathNew : pulic InterFaceOld
{
virtual Sub(....) = ;
}
class CMath : public interfaceMathNew
{
Query....(){}
AddRef(){}
Release(){}
Add(...){}
Sub(...){}
}

老版本的插件是这样写

class InterFaceMathOld : IUnKnow
{
virtual Add(...) = ;
} class CMath : public InterFaceOld
{
Query....(){}
AddRef(){}
Release(){}
Add(...){}
}

这些代码我都会写好,发到云盘中下载研究,如果连接过时了,请评论告知,或者QQ2510908331 这地方一定要细看,

我会一步步的吧Com从基本到高级的源码写出来,这样能熟悉一下COM的框架

接口设计的细节问题 (解决为什么要用Release)

1名称粉碎

.我们想一下,C语言的函数,看一下二进制或者DLL,是不是都是原本什么样子就是什么样子

C++的函数有一个语法支持重载了,内部怎么支持的重载,这就是一个问题

C++为了支持重载,会对你定义的函数做一个名称粉碎,也就是加了一些额外的符号,比如我们调试中都会遇到一种错误叫做,找不到外部符号  例如test@aHINTAdd....当然我写的不准确,下面有一个帖子专门介绍,可以看看

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的更多相关文章

  1. ArcGIS for Desktop入门教程_第一章_引言 - ArcGIS知乎-新一代ArcGIS问答社区

    原文:ArcGIS for Desktop入门教程_第一章_引言 - ArcGIS知乎-新一代ArcGIS问答社区 1 引言 1.1 读者定位 我们假设用户在阅读本指南前应已具备以下知识: · 熟悉W ...

  2. 少儿编程Scratch第一讲:Scratch完美的初体验

    素材及视频下载 链接:https://pan.baidu.com/s/1qX0T2B_zczcLaCCpiRrsnA提取码:xfp8 都说未来是人工智能.计算机程式控制的时代,如何让青少年接触计算机编 ...

  3. C语言_第一讲_C语言入门

    一.C语言的简介 1.C语言是一个标准,而执行标准的时候产生的自动化程序则是编译器2.了解:1983年美国国家标准化歇会(ANSI)制定了C语言标准.C语言的特点:3.代码的可移植性(理想状态是代码可 ...

  4. Python笔记_第一篇_面向过程_第一部分_2.内存详解

    Python的很多教材中并没有讲内存方面的知识,但是内存的知识非常重要,对于计算机工作原理和方便理解编程语言是非常重要的,尤其是小白,因此需要把这一方面加上,能够更加深入的理解编程语言.这里引用了C语 ...

  5. C语言_第二讲_规范以及常用数据类型

    一丶编码规范基本数据类型 编码规范 任何程序员,都应该有良好的的编码习惯,便于以后的代码可读性和维护 常见了编码规范有 匈牙利命名法 驼峰式大小写 匈牙利命名法: 是电脑程序设计中的一种变量命名规则, ...

  6. Python笔记_第一篇_面向过程_第一部分_4.格式化输入和输出

    开始Python编程首先要学习两类最常用(经常出现和使用)输入和输出.学习编程最重要的状态就是“人机交互”,所以这两类函数显得尤其重要. 第一部分 格式化输入 1.1   函:input 语:inpu ...

  7. Python笔记_第一篇_面向过程_第一部分_3.进制、位运算、编码

    通过对内存这一个部分的讲解,对编程会有一个相对深入的认识.数据结构是整个内存的一个重要内容,那么关于数据结构这方面的问题还需要对进制.位运算.编码这三个方面再进行阐述一下.前面说将的数据结构是从逻辑上 ...

  8. Python笔记_第一篇_面向过程_第一部分_0.开场白

    *什么是Python? Python是一种面向对象的解释型计算机程序设计语言,由荷兰人Guido(吉多) van Rossum于1989年发明,第一个公开版本发行于1991年.在国外应用非常的广泛,国 ...

  9. Python笔记_第一篇_面向过程_第一部分_7.文件的操作(.txt)

    在平时,我们不光要对程序内的代码进行输入和输出的操作,还要对程序外的文件进行和语言之间的交换.操作和运算.在基础部分,先讲解对于外部的.txt文件的操作. 第一部分 基本内容讲解 1.   什么是文件 ...

随机推荐

  1. Visual Studio Debugger中七个鲜为人知的小功能

    Visual Studio debugger是一个很棒的调试工具,可以帮助程序猿们快速地发现和解决问题.这里给大家简单介绍一下VS调试工具中的七个鲜为人知的小功能. 1.    一键跳转到指定语句 调 ...

  2. [leetcode-530-Minimum Absolute Difference in BST]

    Given a binary search tree with non-negative values, find the minimum absolute difference between va ...

  3. (cljs/run-at (JSVM. :browser) "简单类型可不简单啊~")

    前言  每逢学习一个新的语言时总要先了解这门语言支持的数据类型,因为数据类型决定这门语言所针对的问题域,像Bash那样内置只支持字符串的脚步明显就是用于文本处理啦.而数据类型又分为标量类型(Scala ...

  4. 深入理解Java虚拟机-----------虚拟机类加载机制

    虚拟机类加载机制 类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期包括:加载,验证,准备,解析,初始化,使用,卸载等7个阶段.其中,验证,准备,解析3个部分称为连接. 以上7个阶段中,加载, ...

  5. 修改ElementUI源码实践

    提要 Vue2.0+Vuex+ElementUI是现在很多项目都在使用的BS软件的开发组合. Vue相较于Angular具有学习成本低,上手快以及组件轻量化的特点:相较于React,其官方提供的很多指 ...

  6. jmeter-命令行执行脚本

    日常测试过程中发现,在大数量并发时,jmeterGUI界面时长宕机.卡死,在这种情况下我们就需要使用命令行来执行脚本了(非GUI), 命令行执行首先就必须要配置环境变量,如同JAVA-HOME一样,这 ...

  7. 没写完。。51nod_1630: B君的竞技场(期望 概率)

    题目链接 根据 你可以认为B君的水平是在所有人中的等概率随机 ,设 每场中B君获胜的概率为p~U(0,1),在给定的x,y下至游戏结束B君的获胜场数为f(p) (这是一个关于p的函数), 由此

  8. 配置ssh免密码登录——集群学习日记

    度过了难熬的考试月时期之后,最近和小伙伴一起参加的的比赛进入了紧张的准备时期.在进行工作的时候,发现有很多基础的知识点,自己不是很清楚以及了解,所以在想,要不就边学习的时候边写下学习日记,以供自己后来 ...

  9. Python面向对象编程(三)

    封装 1.为什么要封装? 封装就是要把数据属性和方法的具体实现细节隐藏起来,只提供一个接口.封装可以不用关心对象是如何构建的 2.封装包括数据的封装和函数的封装,数据的封装是为了保护隐私,函数的封装是 ...

  10. [C++ Calculator 项目] 文件读入与可视化实现

    Calculator V1.1 注:这是C++计算器项目第三.四部分-文件读写与可视化 [基于原代码重构而得] 源文件已上传至Github 文件读写 可视化 文件读写: Ⅰ.在原基础代码上增加了-f参 ...