zoukankan      html  css  js  c++  java
  • C#与COM组件

    什么是COM组件?COM组件只是一种规范。它是从面向对象思想改进来的。面向对象思想很好,但是现实中的局限性在于重用性。原来没有动态链接库的时候,每一次子类编译都要重新编译父类,十分麻烦。而且,如果类中的代码没有公开,那么想要增加方法就是痴心妄想,因为你编写的子类要重新编译一遍父类,而父类代码是不公开的。那么如何重用呢?只能改变一下思路,比如将class的代码编译生成一个一个的dll,那么当其他程序员想要重用class C时,就只需要在自己的程序中加载这个dll而不需要重新编译父类class C的代码了(这也就是组件必须要能动态链接的原因,动态链接比静态链接库)。正是这种思路引出了面向组件的编程思想。[子类编译成一个dll,父类编译成一个dll,之间通过动态调用关联,例如,写上这么一句:loadlibrary "xxx.dll"(或者是include #.h),https://zhidao.baidu.com/question/693573131261999484.html如何调用DLL中的函数如何编写c++dll库dll库与lib库调用DLL例子中使用LoadLibrary方法//怎么还需要DLL的头文件???使用LoadLibrary动态加载DLL并使用其中的类动态加载DLL之后如何继承并重写类呢(避免重复编译)?]

    下面,我就简单介绍一下面向组件的思想。在以前,应用程序总是被编写成一个单独的模块,就是说一个应用程序就是一个单独的二进制文件。后来在引入了面向组件的编程思想后,原本单个的应用程序文件被分隔成多个模块来分别编写,每个模块具有一定的独立性,也应具有一定的与本应用程序的无关性。一般来说,这种模块的划分是以功能作为标准的。比如,一个 网上办公管理系统,从功能上说它需要包含网络通信、数据库操作等部分,我们就可以将网络通信和数据库操作的部分分别提出来做成两个独立的模块。那么,原本单个的应用程序就分隔成了三个模块:主控模块、通信模块和数据库模块。而这里的通信模块和数据库模块还可以做得使其具有一定的通用性,那么其他的应用程序也就可以利用这些模块了。这样做的好处有很多,比如当对软件进行升级的时候,只要对需要改动的模块进行升级,然后用重新生成的一个新模块来替换掉原来的旧模块(但必须保持接口不变),而其他的模块可以完全保持不变。这样,软件升级就变得更加方便,工作量也更小。

    说了这么多,总结一下:面向组件编程思想,归结起来就是四个字:模块分隔。这里的“分隔”有两层含义,第一就是要“分”,也就是要将应用程序(尤其是大型软件)按功能划分成多个模块;第二就是要“隔”,也就是每一个模块要有相当程度的独立性,要尽量与其他模块“隔”开。这四个字是面向组件编程思想的精华所在,也是COM的精华所在!理解了这四个字,也就真正理解了面向组件编程的思想。(这里说一点题外话,COM其实是一套规范或者说一套标准,但是在我看来,COM的核心还在于它的思想,也就是面向组件编程思想。标准谁都能定,但是思想只有一个!)

    这样,一个一个的class就变成了一个一个的dll。这样难道就实现组件化编程了吗?当然,还有一些规范,来保证class之间的沟通与重用。

    规范:

    1. 首先是封装性,组件必须向外部隐藏其内部的实现细节,使从外部所能看到的只是接口。然后是组件必须能动态链接到一起,而不必像面向对象中的class一样必须重新编译。

    2. 由于组件向外部隐藏了其内部的细节,因此客户要使用组件时就必须通过一定的机制,也就是说要通过一定的方法来实现客户与组件之间的通信,这就需要接口。所谓接口就是组件对外暴露的、向外部客户提供服务的“ 连接点”。外部的客户见不到组件内部的细节,它所能看到的只是接口,客户也是通过接口来获取组件提供的服务。这有点像 OSI网络协议分层模型,每一层就像一个组件,它内部的实现细节对于其他层是不可见的;而每一层通过“服务接入点”向其上层提供服务,这就像这里所说的接口。一般来说,接口总是固定的,也是公开的。组件的开发人员要实现这些接口,而客户则通过接口获得服务。正是接口的这种固定和公开,才使得组件和客户能够在不了解对方的情况下达成一致。

    [这里的客户是指要使用某一个组件的程序或模块]

    那么,客户和组件之间又是通过接口来通信的。通过接口,COM是怎样将客户与组件分隔开来,又是怎样利用接口来实现客户与组件间的通信的呢?

    首先我要讲讲接口。COM中的接口实际上是一个函数地址表,当组件实现了这个接口后,这个函数地址表中就填满了组件所实现的那些接口函数的地址。而客户也就是通过这个函数地址表获得组件中那些接口函数的指针,从而获得组件所提供的服务的。从某种意义上说,我们可以把接口理解为c++中的虚拟基类;或者说,在c++中可以用虚拟基类来实现接口!这是因为COM中规定的接口的存储结构,和c++中的虚拟基类在内存中的结构是一致的。其存储结构如下图:

    接口有了,那么组件又是怎样实现接口的呢?实际上,如果用虚拟基类来实现接口,那么组件就是对这个虚拟基类的继承。大家知道,当某个类继承于一个虚拟基类的时候,它就要实现这个虚拟基类里声明的虚函数,这就正好与组件实现接口这一点相吻合。

    而客户只需要得到一个指向ComponentB实体的InterfaceA指针就可以获得ComponentB组件的服务了。

    //使用了组件ComponentB的客户: 
    …… 
    ComponentB  CB; 
    InterfaceA *pIA=&CB;  //获得指向ComponentB实体的InterfaceA指针,以下客户就可以只通过接口来获取组件的服务 
    pIA->Fun1(); 
    pIA->Fun2(); 
    ……

    但是我们注意到,这样做组件ComponentB和客户还是没有被完全分隔开。因为在客户代码里需要创建ComponentB实体,这对于只能看到接口而对组件一无所知的客户来说,是不可以接受的(比如客户不会知道组件的类名叫ComponentB,因为它只知道接口是InterfaceA)。解决这个问题的方法是在实现组件的动态链接文件(比如 dll文件)里创建组件的实体,而不是在客户代码里创建组件实体。通常组件都是以dll的形式出现的,而在实现组件的dll里都会实现一个叫CreateInstance的函数,这个函数可以被外部的客户调用。它返回一个接口的指针,当客户调用这个函数后就能够获得指向组件实体的接口指针了。它的实现也很简单:

    //在实现组件ComponentB的dll里: 
    InterfaceA *CreateInstance() 
    { 
       ComponentB CB; 
       InterfaceA *pIA=&CB; 
       return pIA; 
    }

    当然,真正的CreateInstance函数没有这么简单,我上面的代码只是一个简单的模拟。有个CreateInstance函数之后,客户代码就变成了:

    //使用了组件ComponentB的客户: 
    …… 
    InterfaceA *pIA=CreateInstance();  //获得指向ComponentB实体的InterfaceA指针,以下客户就可以只通过接口来获取组件的服务 
    pIA->Fun1(); 
    pIA->Fun2(); 
    ……

    这样,组件和客户就完全被分隔开了,而连接它们的只有接口以及一个CreateInstance的函数。 

    用C#实现:https://blog.csdn.net/soudog/article/details/1593346

    C# COM组件的规范:

    1、每个COM组件所有对外公布的方法都必须通过接口形式实现;
    2、由于.Net下编译的COM组件并等同于C编译的COM组件,所以存在必须在运行目标机器注册的情况,对此.Net下编译的COM组件必须为程序进行签名,即强命名密钥;

    维基百科:Component Object Model(COM):“组件对象模型(缩写COM)是微软的一套软件组件的二进制接口标准。这使得跨编程语言的进程间通信、动态对象创建成为可能。”COM到底是什么1COM到底是什么2

    1. 新建类库

    2.记着规范1,新建一个接口ToString

    Namespace A{
      public interface a{
        intTestFunction(intnum,stringstr1,stringstr2,doubledb);
      }
    }

    3.在类中实现接口:

    4.找到节点“Properties”打开AssemblyInfo.cs文件,将[assembly:ComVisible(false)]属性改成[assembly:ComVisible(true)]。

    5.项目右键—属性、或者双击“Properties”节点,进入到项目属性设置界面。选择“应用程序”选项卡,点击“程序集信息”按钮,勾选“使程序集COM可见”,确定。选择“生成”选项卡,在底部“输出”栏目下,勾选“为COM互操作注册”。

    选择“签名”选项卡。
    、勾选“为程序集签名”;
    、在“选择强名称密钥文件”的下拉框选择“<新建…>”
    、在“密钥文件名称”输入:HK.NetPBDemo.snk(任意命名,snk后缀即可)
    、去掉“使用密码保护密钥文件”,确定。

    6.编译项目。

    7.打开vs指令窗,运行指令:

    gacutil/i DLL路径HK.NetPBDemo.dll将这个DLL加入的全局缓存里
    regasm DLL路径HK.NetPBDemo.dll注册这个dll

    附:以上11个步骤即可完成COM组件在本机的环境下注册并提供调用。
    1、若要在其他机器使用必须把编译的“DLL”、“TLB”文件一并复制在目标机器(已安装.NetFramework环境)并放在需调用的程序根目录;
    2、在目标机器“c:WINDOWSMicrosoft.NetFrameworkv4.0.3.319”下找到“RegAsm.exe”,该路径是我的安装路径,如果没有就搜索下“RegAsm.exe”
    这个东东吧。【我的环境:虚拟机、系统WindowsXP、.NetFramework4.0】
    3、运行CMD打开指令窗,讲路径定位到“c:WINDOWSMicrosoft.NetFrameworkv4.0.3.319”,
    运行指令:regasm.exeDLL路径HK.NetPBDemo.dll。如下图【注册成功】

    如何在项目中引用COM组件呢?:

    1. 右键项目->添加引用。

    2.对象查看器。

    3.

    参考:

    https://blog.csdn.net/Repeaterbin/article/details/4318395?utm_medium=distribute.pc_relevant.
    none-task-blog-OPENSEARCH-2.channel_param&depth_1-utm_source=distribute.pc_relevant.no
    ne-task-blog-OPENSEARCH-2.channel_param

  • 相关阅读:
    预处理命令
    函数
    结构体
    字符数组
    数组
    文件
    用 typedef 定义类型
    枚举类型
    联合
    位运算
  • 原文地址:https://www.cnblogs.com/2008nmj/p/13901679.html
Copyright © 2011-2022 走看看