`

实例深入学习COM技术

    博客分类:
  • COM
阅读更多

COM技术介绍

一、COM介绍

1、  定义

(Component Object Model)

COM是微软公司的最高级的,包罗万象的二进制通讯规范(也就是说是大家都要遵守的合同)。用于软件组件间跨进程,跨机器,和操作系统进行交互操作。COM是透明位置的。它可以在EXE,DLL或者远程机器上使用。

OLE是一个主要与用户界面相关的高级功能的集合。COM和OLE的概念界限原本就不清晰,总是容易混淆。

2、  历史

OLE(Object Linking & Embedding )是1991年首次出现的(是WINDOWS3.1自带的)。OLE最初的含义是对象链接和嵌入。当时用DDE(动态数据交换)作为底层通讯协议。

1993,COM首次出现。微软推出OLE2.0,开始用COM代替DDE作为底层通讯协议。这也是COM第一个重要的用途。

1996年,大多数开发人员开始编写32位的WIN95应用程序。他们发现,OLE使用COM的方式是一种非常好的设计软件的方法。开发人员开始使用类似的方法编写自己的对象和界面。另外,操作系统也开始要求使用COM技术编程,如编写WIN95用户界面。这些即不是OLE,也不是AUTOMATION,那么他到底是什么呢?这个属于大多数人倾向于使用COM。

3、  发展

1996年,微软推出NT4.0,DCOM首次出现,作为NT的一部分。它实现了将COM在分布式系统中的应用。

1997年开始流行ATL。COM作为一种技术规范,最早是由C语言来实现的,但是实现起来比较复杂。出现VC以后,又对COM进行了预制和封装,大大简化COM应用的开发。这就是ATL(Active Template Library)。

4、  现状

我们经常见到的用途:

使用外来控件。特别是在网页上使用ACTIVEX控件。ADO

WORD/EXCEL的应用。(两者交叉使用,在应用程序中调用)

二、概念

接口:可以理解为一个抽象的类。

OBJCET(component),相当与组件。与VC和VB中理解的OBJECT是两个不同的概念。千万不要混淆。一个纯粹封装的OBJCET


它是一个封装好的黑匣子。是一个不能看到内部数据结构的东西。是一个抽象的东西。

最基本的OBJCET,给这个OBJECT 加上接口,用于访问这个黑匣子。那么这个OBJECT就是一个COM OBJECT了。

Func1()

IFOO

Func2()


相当于一个手机充电器。用插销作为标准接口,但是内部实现被隐藏了起来。

在C++中,接口被表示为一个抽象的类。

例如: 程序清单:

class IFoo {

   virtual void Func1();

   virtual void Func2();

};

特点:

内部有多个纯虚函数。并且没有函数的实现。而且也不能包含任何内部数据成员。

把接口与实现隔离开来的目的:要把对象内部的工作细节隐藏起来,而这些实现都在类中实现。当实现类的数据成员发生变化时,客户程序是与已经编译好的二进制的接口通讯,所以它也无须重新编译。

例子:

手机是一个COM。它的芯片是接口。无论信息如何发生变化。我们都能接受。它的实现是个不不相同。而不同的实现就是不同的国产手机厂家。但是国内所有的厂家生产的手机全部都是使用的国外同一家的芯片产品,也就是说用同一个接口。

常见(有两个接口)

对象支持多接口。如果老版本的接口不方便,可以继承一个新的接口。但不能修改老的接口。接口一旦发布,就不能修改。

如果增加一个接口IFOO2,该接口仍然包含原接口IFOO的两个函数,而且新增加一个函数

IFOO    

IFOO2

程序清单:

class IFoo2 : public IFoo {

   // Inherited Func1, Func2

   virtual void Func2Ex(double nCount) = 0;

};

如何在C++中使用该接口的方法?

实现接口的来首先要从接口继承下来。

实现该接口:

class CFoo : public IFoo {

   void Func1() { /* ... */ }

   void Func2(int nCount) { /* ... */ }

};

调用:

首先要引入接口的定义。

创建一个指向对象的指针!!这只是为了方便而说的。实际上是错误的。COM中没有对象的指针。只有指向接口的指针。由于VC++调用内部虚拟函数是用了VTBL技术。即建立一个指针表,表中的指针指向成员函数。所以,指向接口的指针实质就是一个指向指针的指针。

将指针指向对象,那么意思实际上就是说,将指针指向对象的接口的简称。

所以,我们可以将指针指向对象的任意一个接口。最后都达到了将指针指向一个对象的目的。

用DELPHI语法写,更清晰易懂。

var pFoo:IFoo;

pFoo:=CFoo.Create;

//实现接口的函数

pFoo.Func1()

COM接口的实现。

COM定义了大量的接口及相对应的IID。

CLSID (class identifier GUID) (identifier:标识)

IID (interface identifier GUID)

GUID:是一个16个字节长的结构。能有3.4的10的38次方的组合。相当与宇宙中原子总数的平方根。所以,永远都用不完的。尽量不要拷贝它。

使用 GUIDGEN.EXE 来生成。

例如 IUnknown

IID of "00000000-0000-0000-c000-000000000046"

任何一个COM对象都包含一个IUNKNOWN接口。所有的接口都是从它这里派生的。

IUnknown 接口有三个函数:

HRESULT QueryInterface(REFIID riid, void **ppvObject);

ULONG AddRef();

ULONG Release();

简要功能介绍:

QueryInterface:用来查询riid对应的接口是否存在。结果在输出参数中。

AddRef:在对象的使用期限内增加引用记数。当对象第一次被创建时或者其他的用户将一个指针指向该对象时,调用该函数,内部记数将增加1。

Release:当用户不再使用的时候,调用它。最后一次调用时,记数变为0,对象将释放自己。

所以,前面的CLASS的定义

class IFoo {

   virtual void Func1(void) = 0;

   virtual void Func2(int nCount) = 0;

};

应该被改写为:COM对应的定义方式:

interface IFoo : IUnknown {

virtual HRESULT STDMETHODCALLTYPE Func1(void) = 0;

virtual HRESULT STDMETHODCALLTYPE Func2(int nCount) = 0;

};

用宏替换以后:

interface IFoo : IUnknown {

STDMETHOD Func1(void) PURE;

STDMETHOD Func2(int nCount) PURE;

};

创建OBJECT的实例,通过接口的指针引用对象(不能简单的说是:获取指向对象的指针)

IFoo *pFoo = NULL;

HRESULT hr = CoCreateInstance(CLSID_Foo, NULL, CLSCTX_ALL,

               IID_IFoo, (void **)&pFoo);

if (SUCCEEDED(hr)) {

   pFoo->Func1();   // Call methods.

   pFoo->Func2(5);

   pFoo->Release();   // MUST release interface when done.

}

一个实际例子:

     

IUnknown.  

IFoo

Ifoo2 

(IUNKNOWN接口有三个函数)

上边的接口是公共保留接口,左边的是自己定义的接口,都派生自IUNKNOWN。

QueryInterface的作用,获取同一个对象的另外一个接口。

IFoo *pFoo = NULL;

HRESULT hr = CoCreateInstance(CLSID_Foo2, NULL, CLSCTX_ALL,

               IID_IFoo, (void **)&pFoo);

if (SUCCEEDED(hr)) {

   pFoo->Func1();   // call IFoo::Func1

   IFoo2 *pFoo2 = NULL;

   hr = pFoo->QueryInterface(IID_IFoo2, (void **)&pFoo2);

   if (SUCCEEDED(hr)) {

      int inoutval = 5;

      pFoo2->Func3(&inoutval);   // IFoo2::Func3

      pFoo2->Release();

   }

   pFoo->Release();

}

IclassFactory的概念:

我们不能让COM对象自己创建自己的实例。要通过服务器。服务器则通过建立一个产生COM对象的工厂:类厂。用IclassFactory来产生很多新的OBJECT。但是,IclassFactory本身也是一个OBJECT。

 

IDL文件的目的:

达到跨语言实现。它以OSF(Open Software Foundation)的DCE RPC(Distributed Computing Environment Remote Procedure Call) IDL为基础,对它进行了扩展。

[object, uuid(3AB1D289-2145-4a33-9A98-9635C3518CD7)] //uuid和GUID是一个概念。

interface IFoo:Iunkonown

HRESULT Func2([in] long * p);

}

在VC中,实际上MFC也支持COM,但是,建议用ATL开发COM。应为它更简单,更纯净,更高级。ATL技术代表着COM技术的精华,提供了建立COM应用的核心构架,大大节省了手工代码量。所以我们从ATL实例开始了解COM技术。

三、COM应用

1、  OLE

2、  ATUOMATION

3、  ACTIVEX

4、 ATL

四、COM实际例子

一、建立一个服务器

1、  利用 MICROSOFT VISUALSTATIO 建立一个ATL PROJECT WIZARD 。选择默认的DLL。

2、  建立后,保存为mycomservice,REBUILD(编译)。

3、  右键调出 NEW ATL OBJECT 。

4、  选择默认的SIAMPLE,然后NEXT。

5、  输入名字为:First表示第一个接口。

6、  取默认值双接口。(DUAL)

7、  确定后,系统自动生成接口Ifirst和类Cfirst。

类实现,还没有代码。

8、  右键添加方法Methd1 

[in] long mPara ,[out,retval] long newPara

9、  右键添加属性 Ppty1

10、              在类定义中的PUBLIC部分增加成员变量定义: long m_x; 注意要将变量放到与方法和属性在同一个代码段。

11、              增加一些功能。比如增1,代码是:

       m_x=mPara;

*newPara=m_x+1;

12、              新定义一个自定义接口。(通过右键添加一个新的ATL OBJECT)

13、              在新的接口 Second上添加新的方法Methd2

14、              定义新的方法的内容:system("dir|more");

15、              服务器完成定义。

16、              所有的代码:

IDL文件:

// mycomservice.idl : IDL source for mycomservice.dll

//

// This file will be processed by the MIDL tool to

// produce the type library (mycomservice.tlb) and marshalling code.

import "oaidl.idl";

import "ocidl.idl";

       [

              object,

              uuid(913EFDEE-3FD8-4F66-852E-57D95490A196),

              dual,

              helpstring("IFirst Interface"),

              pointer_default(unique)

       ]

       interface IFirst : IDispatch

       {                                

              [propget, id(2), helpstring("property Ppty1")] HRESULT Ppty1([out, retval] long *pVal);

              [propput, id(2), helpstring("property Ppty1")] HRESULT Ppty1([in] long newVal);

              [id(3), helpstring("method Methd1")] HRESULT Methd1([in] long mPara,[out ,retval] long * newPara);

       };

       [

              object,

              uuid(ADD28210-06E1-4FF7-BC76-FF559ED7254A),

      

              helpstring("ISecond Interface"),

              pointer_default(unique)

       ]

       interface ISecond : IUnknown

       {

              [helpstring("method Methd2")] HRESULT Methd2();

       };

[

       uuid(7C025B97-09F8-4BB9-9951-2B8AFBB377EB),

       version(1.0),

       helpstring("mycomservice 1.0 Type Library")

]

library MYCOMSERVICELib

{

       importlib("stdole32.tlb");

       importlib("stdole2.tlb");

       [

              uuid(2BF62644-5257-414C-911C-8DBFACEFDBCC),

              helpstring("First Class")

       ]

       coclass First

       {

              [default] interface IFirst;

       };

       [

              uuid(7CBE299E-0965-4504-B4EB-06F841B7EF0F),

              helpstring("Second Class")

       ]

       coclass Second

       {

              [default] interface ISecond;

       };

};

FIRST类定义:

// First.h : Declaration of the CFirst

#ifndef __FIRST_H_

#define __FIRST_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////

// CFirst

class ATL_NO_VTABLE CFirst :

       public CComObjectRootEx<CComSingleThreadModel>,

       public CComCoClass<CFirst, &CLSID_First>,

       public IDispatchImpl<IFirst, &IID_IFirst, &LIBID_MYCOMSERVICELib>

{

public:

      

       CFirst()

       {

       }

DECLARE_REGISTRY_RESOURCEID(IDR_FIRST)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CFirst)

       COM_INTERFACE_ENTRY(IFirst)

       COM_INTERFACE_ENTRY(IDispatch)

END_COM_MAP()

// IFirst

public:

       long m_x;

       long m_p;

       STDMETHOD(Methd1)(/*[in]*/ long mPara,/*[out ,retval]*/ long * newPara);

       STDMETHOD(get_Ppty1)(/*[out, retval]*/ long *pVal);

       STDMETHOD(put_Ppty1)(/*[in]*/ long newVal);

      

};

#endif //__FIRST_H_

FIRST类的实现:

// First.cpp : Implementation of CFirst

#include "stdafx.h"

#include "Mycomservice.h"

#include "First.h"

/////////////////////////////////////////////////////////////////////////////

// CFirst

STDMETHODIMP CFirst::get_Ppty1(long *pVal)

{

       // TODO: Add your implementation code here

       *pVal =m_p;

       return S_OK;

}

STDMETHODIMP CFirst::put_Ppty1(long newVal)

{

       // TODO: Add your implementation code here

m_p=newVal;

       return S_OK;

}

STDMETHODIMP CFirst::Methd1(long mPara, long *newPara)

{

       // TODO: Add your implementation code here

       m_x=mPara;

       *newPara=m_x+1;

       return S_OK;

}

SECOND类定义:

// Second.h : Declaration of the CSecond

#ifndef __SECOND_H_

#define __SECOND_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////

// CSecond

class ATL_NO_VTABLE CSecond :

       public CComObjectRootEx<CComSingleThreadModel>,

       public CComCoClass<CSecond, &CLSID_Second>,

       public ISecond

{

public:

       CSecond()

       {

       }

DECLARE_REGISTRY_RESOURCEID(IDR_SECOND)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CSecond)

       COM_INTERFACE_ENTRY(ISecond)

END_COM_MAP()

// ISecond

public:

       STDMETHOD(Methd2)();

};

#endif //__SECOND_H_

Second类的实现

// Second.cpp : Implementation of CSecond

#include "stdafx.h"

#include "Mycomservice.h"

#include "Second.h"

/////////////////////////////////////////////////////////////////////////////

// CSecond

 

STDMETHODIMP CSecond::Methd2()

{

       // TODO: Add your implementation code here

       //system("dir|more");

       MessageBox(NULL,"???hello????","",0);

       return S_OK;

}

二、建立一个客户

1、  建立一个基于对话框的EXE文件,名字是:mycomclient。

2、  在StdAfx.h头文件中加入引入代码:

#import "..\mycomservice\mycomservice.tlb" no_namespace named_guids

编译,将自动增加新的文件(External Dependencies)。

3、  建立一个按钮和对应的函数。

4、  在函数中写代码。

5、  首先采用传统的方法。

6、  在对话框的公共部分:

IUnknown * pUnk;

 HRESULT hr;    

 IFirst    *pFirst;     

 ISecond   *pSecond;      

 CString   s;

7、  在对话框初始化时:

        HRESULT hr;    

       hr=CoInitialize(NULL);

        if FAILED(hr) // SUCCEEDED    

        AfxMessageBox("INITE Failed!!");

8、  第一个BUTTON的事件。

/*

       //方法1:直接将指针指到要使用的接口,创建COM对象

       hr=CoCreateInstance

       (CLSID_First,//或者clsid

       NULL,

       //CLSCTX_LOCAL_SERVER,

       CLSCTX_INPROC_SERVER,      //这是从MSDN中查到的唯一正确的用法

       IID_IFirst,

       (LPVOID* ) &pFirst //首先指向该接口,这是一个基本接口,可以代表该对象

       )                              ;     

       if (S_OK!=hr)

       AfxMessageBox("创建并初始化COM接口失败!!");

       else

       AfxMessageBox("创建并初始化COM接口成功!!");

       */                        

       /*

               //调通!!

               pFirst->put_Ppty1(7);

               long p                  ;

               pFirst->get_Ppty1(&p);

               s.Format("the car's fuel is %d ",p);

               AfxMessageBox(s);                  

       */

      

       //方法2:      接口间查询!

              hr=CoCreateInstance

                      (CLSID_Second,//或者clsid

                      NULL,         

                      CLSCTX_INPROC_SERVER,   

                      IID_IUnknown,

                      (LPVOID* ) &pFirst

//首先指向该接口,这是一个基本接口,可以代表该对象

                      )                            ;     

               if (S_OK!=hr)

                      AfxMessageBox("获取IKNOWN接口失败!!!");

               else

                      AfxMessageBox("获取IKNOWN接口成功!!");

               

               hr=pFirst->QueryInterface(IID_ISecond, (LPVOID *) &pSecond);              

               

               if SUCCEEDED(hr)

                      AfxMessageBox("query seond interface succefully!!");

               else

                      AfxMessageBox("query seond interface Failed!!");

                

               pSecond->Methd2();

                            

               //CoUninitialize();                     

9、  第二个BUTTON准备

//用到了灵敏指针

    IFirstPtr pFirst;

       pFirst.CreateInstance(CLSID_First);

      

       pFirst->put_Ppty1(15);

       long p;

       pFirst->get_Ppty1(&p);

       s.Format("the car fuel level is %d",p);

       MessageBox(s);       

通过DELPHI实现这些在VC下编写的接口

首先引入库。

Project/import library

implementation

 uses MYCOMSERVICELib_TLB,comobj;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

var x:ISecond;

begin

 x:=CoSecond.Create;

 x.Methd2;

end;

procedure TForm1.Button2Click(Sender: TObject);

var

y:variant;

z:IFirst;

L:Longint;

S:shortint;

begin

y:=createoleobject('MYCOMSERVICE.First');

//y:=createoleobject('TSecond.Second');   //自动化不支持这个接口

y.ppty1:=5;

L:=y.ppty1;

S:=shortint(L);

showmessage(inttostr(S));

(*

z:=CoFirst.Create;

//z.Ppty1:=5;

z.Set_Ppty1(5);

showmessage(inttostr(z.Get_Ppty1));

*)

end;

分享到:
评论

相关推荐

    精通Verilog HDL:IC设计核心技术实例详解part3(total4)

    本书从实际应用的角度详细地向读者介绍了Verilog HDL语言的使用,并利用实例深入剖析了Verilog HDL语法在实际应用中的要点,结构清晰,内容丰富。  全书共分为9章。前7章分别介绍了设计方法概论,Verilog HDL的语法...

    ASP.NET 3.5 开发大全(全方位学习+实例)

    不仅如此,本书还详细的讲解了如何使用现有的互联网上的优秀的开源项目进行应用程序开发以提高开发效率,同时,读者还能够通过了解简单易懂的开源项目深入学习ASP.NET应用程序开发。 3.分类讲解,理解深刻 本书通过...

    精通 Verilog HDL:IC 设计核心技术实例详解 part2(total4)

    本书从实际应用的角度详细地向读者介绍了Verilog HDL语言的使用,并利用实例深入剖析了Verilog HDL语法在实际应用中的要点,结构清晰,内容丰富。  全书共分为9章。前7章分别介绍了设计方法概论,Verilog HDL的语法...

    ASP.NET 3.5 开发大全(全方位学习+实例)_002

    不仅如此,本书还详细的讲解了如何使用现有的互联网上的优秀的开源项目进行应用程序开发以提高开发效率,同时,读者还能够通过了解简单易懂的开源项目深入学习ASP.NET应用程序开发。 3.分类讲解,理解深刻 本书通过...

    ASP.NET 3.5 开发大全(全方位学习+实例)_003

    不仅如此,本书还详细的讲解了如何使用现有的互联网上的优秀的开源项目进行应用程序开发以提高开发效率,同时,读者还能够通过了解简单易懂的开源项目深入学习ASP.NET应用程序开发。 3.分类讲解,理解深刻 本书通过...

    Visual C++项目开发实例导航

    . 本书的项目实例涉及网络应用、知识管理以及插件系统的构建等多个方面,对ActiveX编写、ASP组件实现、ADSI调用、VBA编程、连接点客户端的实现、ISAPI的编写、线程机制的建立、XML数据的解析、Windows扩展编程(区...

    vc++ 开发实例源码包

    如题,此实例非常适合学习,重载并自绘了Wnd类,效果是上下文字、图片、文字由大到小和星星闪烁等滚动效果。实例使用了加载类似xml文件读取信息,然后显示。 COM_ATL_Tutorial 简单的atl控件演示 COM接口挂钩及其...

    080_《Delphi技术方案宝典》(2/2)

    本书从delphi软件开发时必须掌握的核心技术入手,深入介绍各种核心技术在实际开发中的应用。全书分为9章,分别是框架设计方案、数据处理方案、用户登录权限及密码验证方案、数据查询方案、决策分析方案、报表打印...

    JSP.2.0技术手册.part3

    对这两项技术www. exvv.com的深入了解,将有助于您未来对于JavaServer Faces(JSF)技术以及Java Web Services技术www. exvv.com的学习。 本书分为三大部分,前三章为基本概念部分,帮助读者奠定相关www. exvv.com的...

    JSP.2.0技术手册.part2

    对这两项技术www. exvv.com的深入了解,将有助于您未来对于JavaServer Faces(JSF)技术以及Java Web Services技术www. exvv.com的学习。 本书分为三大部分,前三章为基本概念部分,帮助读者奠定相关www. exvv.com的...

    JSP.2.0技术手册.part1

    对这两项技术www. exvv.com的深入了解,将有助于您未来对于JavaServer Faces(JSF)技术以及Java Web Services技术www. exvv.com的学习。 本书分为三大部分,前三章为基本概念部分,帮助读者奠定相关www. exvv.com的...

    深入浅出Oracle: DBA入门、进阶与诊断案例(eygle)原生PDF+源代码--eygle(盖国强)

     为了让更多进入Oracle领域的朋友能够快速了解和掌握Oracle技术,让具备一定经验和积累的Oracle从业人员继续深入学习,作者倾力撰写了本书。  本书作者活跃于国内著名Oracle技术论坛ITPUB(www.itpub.net),并...

    114_《Delphi开发技术大全》(2/3)

    书中各部分介绍的技术既相互独立又相互联系,可以逐步引导读者深入学习并掌握Delphi的编程知识、方法和编程技巧。本书附有配套光盘。光盘提供了书中示例和典型应用实例的全部源代码,所有源代码都经过精心调试,在...

    114_《Delphi开发技术大全》(1/3)

    书中各部分介绍的技术既相互独立又相互联系,可以逐步引导读者深入学习并掌握Delphi的编程知识、方法和编程技巧。本书附有配套光盘。光盘提供了书中示例和典型应用实例的全部源代码,所有源代码都经过精心调试,在...

    114_《Delphi开发技术大全》(3/3)

    书中各部分介绍的技术既相互独立又相互联系,可以逐步引导读者深入学习并掌握Delphi的编程知识、方法和编程技巧。本书附有配套光盘。光盘提供了书中示例和典型应用实例的全部源代码,所有源代码都经过精心调试,在...

    xml本质论

    XML已经取代Java、设计模式和对象技术,成为软件行业期盼已久的问题解决方案。本书从编程角度深入分析了如何将XML用作类似COM、CORBA的组件集成技术并通过实例说明了... 本书适合XML编程人员和想深入学习XML的人阅读

    XML本质论

    XML已经取代Java、设计模式和对象技术,成为软件行业期盼已久的问题解决方案。本书从编程角度深入分析了如何将XML用作类似COM、CORBA的组件集成技术并通过实例说明... 本书适合XML编程人员和想深入学习XML的人阅读。

    深入浅出Oracle:DBA入门,进阶与诊断案例(盖国强编)

    从业人员继续深入学习,作者倾力撰写了本书。  本书作者活跃于国内著名Oracle技术论坛ITPUB(www.itpub.net),并全力打造国内极具影响力的个 人Oracle技术站点Eygle.com(www.eygle.com )。本书从基础出发,...

    067_《Delphi7组件与分布式应用开发》

    全书以组件与分布式应用开发为主题,贯穿实例深入浅出地介绍了Delphi 7支持的组件技术、VCL库的扩充的一般方法、可视组件的开发与发布、ActiveX控件技术、COM/DCOM/COM+的基本概念与原理、在Delphi 7中使用...

    深入浅出玩转51单片机

    之后带领读者深入浅出学习51单片机内部资源(如定时器、中断、串口)和经典外围电路(如LED、数码管、按键、液晶、点阵、EEPROM、温度传感器、时钟、红外线解码),同时穿插了一些C语言和基础电路;其后又扩展了一些...

Global site tag (gtag.js) - Google Analytics