COM ActiveX C++ Builder

COM ActiveX C++ Builder

1.COM技术概述

COM表示Component Object Model(组件对象模型),它是Microsoft大力推广的软件开发技术。采用COM规范开发的应用软件具有强大的功能,主要有如下几点:

◆COM是二进制编程规范,可以编写被多种语言使用的代码。

◆用于创建ActiveX控件。

◆通过OLE Automation 控制其它的程序。

◆与其它机器上的对象或程序进行对话,构成分布式应用程序。

Microsoft推出Windows 98和Windows NT 5.0后,整个操作系统的核心都围绕着COM来建立。我们可以把Windows系统看作是一系列的COM接口,在需要是可以调用这些接口。如DirectX就是一系列的COM接口服务程序,通过它可以进行高性能的Windows图形程序设计。

用COM技术开发的应用程序从理论上说是客户/服务器模式的程序。程序员可以使用一系列的COM服务程序来构造他们自己的应用程序,这些服务程序可以根据需要随时嵌入到主程序中。在分布式系统中,可以通过网络来访问这些服务程序。将来,操作系统和整个网络可能会被看作是一套以COM对象形式提供的服务集。一部分程序员负责建立这些服务,而另一部分程序员只负责如何调用它们。其目的是实现软件的即插即用。

开发COM应用程序是比较复杂的,通常需采用ActiveX模板库(ATL)来编程。在这里我们推荐采用C++ Builder来开发COM程序,Inprise(Borland)公司的面向对象技术一直处于世界领先水平,C++ Builder采用可视化方法,隐藏了ATL的实现细节,自动生成COM接口所需的代码。

以下的程序举例采用C++ Builder 4.0 编制,在中文Windows98环境下运行。

2.建立COM服务程序

COM服务程序有三种形式,第一种是驻留在本地机器上以DLL形式提供,该服务程序被调用时,嵌入到调用程序的线程中运行;第二种是驻留在本地机器上以EXE形式提供,该服务程序被调用时将占用独立的线程运行;第三种驻留在远端机器上以EXE形式提供,服务程序通过网络被调用,它在远端机器上运行,结果通过网络返回调用者。

在此采用第一种形式建立COM服务程序,这也是最常用的形式,DirectX就是采用这种形式提供的。

C++ Builder建立COM服务程序的方法如下:

2.1创建支持COM接口对象的动态连接库文件:

◆打开File/New/ActiveX项目页,选择ActiveX Library;

◆选择Save All 将项目以PCOMServer文件名保存;此时C++ Builder 自动生成如下的文件:

PCOMServer.bpr:工程的项目文件;

PCOMServer.h,PCOMServer.cpp:支持COM对象的动态连接库源文件,其中有许多函数用于COM接口对象的自动装配,大家不用去编辑它们;

PCOMServer_ATL.h,PCOMServer_ATL.cpp:ATL形式的文件供C++ Builder编译器调用,大家也不要去编辑它们。

◆打开Project/Options/Linker 属性页不选中Use dynamic RTL选项,打开Project/Options/Packages属性页不选中Builder with runtime packages选项,这两步操作可以使开发的COM动态连接库不依赖C++ Builder的VCL动态连接库,有利于独立发行,但在一般情况下还是建议选中这两项。

2.2建立COM接口对象


打开File/New/ActiveX属性页,选择Automation Object表示向服务程序中插入一个自动类型的COM对象,我们选择这种类型的COM对象是为了可以自动注册,并且自动支持可以被其他语言调用。此时出现如下的对话框,输入COM类的名字MyCOM即可,对话框中的其它选项用于规定COM对象的性质,可查看帮助信息。

2.3通过类型库编辑器编辑COM对象中相应接口对象的属性和方法


此时自动进入类型库编辑器,类型库用于存储COM对象的说明,是一个可以被多种语言调用的头文件包。在类型库中,可以定义COM对象的接口,定义接口对象的属性和方法等。类型库编辑器如下所示:

可以看出此时自动产生了MyCOM类的一个接口类IMyCOM,在COM应用软件中我们实际上是与接口对象打交道,下面通过类型库编辑器为IMyCOM接口定义方法和属性。

◆单击编辑器顶部的Method按钮;

◆在Arributes页面的Name字段中输入方法的名称,本例中是AddInt用于整数加法;

◆在Parameters页面中,单击Add按钮编辑方法中的参数;

x和y是输入的两个整数,ret用于返回运算的结果,必须定义为指针型

◆切换到Flags页面,可以对接口的属性作调整;

◆在Text页面中可以检查生成的IDL代码:

[id(0x00000001)]


HRESULT _stdcall AddInt([in] int x, [in] int y, [out, retval] int * ret );

◆单击Refresh按钮,此时可以关闭类型库编辑器。当需要为接口添加新的属性和方法时,可以通过View/Type Library重新打开编辑器。选择Save All用C++ Builder提供的缺省文件名保存类型库的相关文件如下:

PCOMServer.TLB: 类型库文件;

PCOMServer_TLB.cpp:包含COM接口和对象的说明,其主要目的是方便访问,在客户程序中需将本文件包含到客户程序的工程中;

PCOMServer_TLB.h: PCOMServer_TLB.cpp的头文件,通过#include引入到客户程序中。

MyCOMImpl.cpp: 该文件是我们需要编写程序代码的地方,实现类型库定义的接口对象的方法和属性;

MyCOMImpl.h: MyCOMImpl.cpp的头文件。

2.4 实现COM接口中的方法

打开MyCOMImpl.cpp文件会发现我们在类型库编辑器中定义的方法,为该方法编写代码如下:

STDMETHODIMP TMyCOMImpl::AddInt(int x, int y, int* ret)

{

*ret=x+y;

return S_OK;

}

2.5 生成DLL文件并注册COM对象

◆选择Project/Builder PCOMServer 生成PCOMServer.DLL文件。

◆打开类型库编辑器,单击Register按钮完成对COM对象的注册。

通过Windows任务栏中的Run菜单运行REGEDIT程序,在Windows注册表的HKEY_CLASSES_ROOT键下查找到PCOMServer.MyCOM子键,PCOMServer为DLL文件的名字,MyCOM为COM对象的名字,在下面可以看到该COM对象的全局唯一描述符CLSID如下:

{59834F03-49F1-11D3-B85B-00E09804A418}

注意:不同的机器生成的描述符不同.

在HKEY_CLASSES_ROOT键下查找到CLSID子键,在它下面找{59834F03-49F1-11D3-B85B-00E09804A418}子键,下面有如下的条目:

InprocServer32:存储PCOMServer.DLL的路径目录;

ProgID:存储COM对象的注册名:PCOMServer.MyCOM;

Typelib:存储COM对象的CLSID值{59834F03-49F1-11D3-B85B-00E09804A418}。

COM对象就是通过在注册表中的纪录实现DLL与客户程序的自动连接。

3.建立COM客户程序

客户程序将访问PCOMServer.DLL服务程序中的MyCOM对象,这些对象的说明保存在前面所述的TLB文件中。我们可以直接将PCOMServer_TLB.cpp加入到客户程序的项目文件中,并在客户程序中引用PCOMServer_TLB.h文件;也可以通过Project/Import Type Library引用PCOMServer_TLB.TLB文件,重新生成.cpp和.h文件,自动完成上述过程。

客户程序的编程重点是实现对服务程序中COM对象的方法的调用,调用的方法有多种,都是通过所谓的代理接口来完成的,这些代理接口在PCOMServer_TLB.h中有详细的定义,从这些定义中可以看出这些代理接口调用对象方法的过程。

PCOMServer_TLB.h文件很重要,包含了调用MyCOM对象的各种接口信息,该文件主要内容如下:

// Type Lib: D:/CAI/com/PCOMServer.tlb

// IID/LCID: {5BD378E5-4B57-11D3-B85B-00E09804A418}/0

// Helpfile:

// DepndLst:

// (1) v2.0 stdole, (C:/WINDOWS/SYSTEM/STDOLE2.TLB)

// (2) v4.0 StdVCL, (C:/WINDOWS/SYSTEM/STDVCL40.DLL)

// ************************************************************************

#ifndef __PCOMServer_TLB_h__

#define __PCOMServer_TLB_h__

#pragma option push -b -w-inl

#include <vcl/utilcls.h>

#if !defined(__UTILCLS_H_VERSION) || (__UTILCLS_H_VERSION < 0x0101)

#error "This file requires an newer version of the header file UTILCLS.H"

#endif

#include <olectl.h>

#include <ocidl.h>

#if defined(USING_ATLVCL) || defined(USING_ATL)

#if !defined(__TLB_NO_EVENT_WRAPPERS)

#include <atl/atlmod.h>

#endif

#endif

namespace Stdvcl {class IStrings; class IStringsDisp;}

using namespace Stdvcl;

namespace Pcomserver_tlb

{

DEFINE_GUID(LIBID_PCOMServer, 0x5BD378E5, 0x4B57, 0x11D3, 0xB8, 0x5B, 0x00, 0xE0, 0x98, 0x04, 0xA4, 0x18);

DEFINE_GUID(IID_IMyCOM, 0x5BD378E6, 0x4B57, 0x11D3, 0xB8, 0x5B, 0x00, 0xE0, 0x98, 0x04, 0xA4, 0x18);

DEFINE_GUID(CLSID_MyCOM, 0x5BD378E8, 0x4B57, 0x11D3, 0xB8, 0x5B, 0x00, 0xE0, 0x98, 0x04, 0xA4, 0x18);

interface DECLSPEC_UUID("{5BD378E6-4B57-11D3-B85B-00E09804A418}") IMyCOM;

typedef IMyCOM MyCOM;

#define LIBID_OF_MyCOM (&LIBID_PCOMServer)

interface IMyCOM : public IDispatch

{

public:

virtual HRESULT STDMETHODCALLTYPE AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/) = 0; // [1]

#if !defined(__TLB_NO_INTERFACE_WRAPPERS)

int __fastcall AddInt(int x/*[in]*/, int y/*[in]*/)

{

int ret;

OLECHECK(this->AddInt(x, y, &ret));

return ret;

}

#endif // __TLB_NO_INTERFACE_WRAPPERS

};

#if !defined(__TLB_NO_INTERFACE_WRAPPERS)

template <class T /* IMyCOM */ >

class TCOMIMyCOMT : public TComInterface<IMyCOM>, public TComInterfaceBase<IUnknown>

{

public:

TCOMIMyCOMT() {}

TCOMIMyCOMT(IMyCOM *intf, bool addRef = false) : TComInterface<IMyCOM>(intf, addRef) {}

TCOMIMyCOMT(const TCOMIMyCOMT& src) : TComInterface<IMyCOM>(src) {}

TCOMIMyCOMT& operator=(const TCOMIMyCOMT& src) { Bind(src, true); return *this;}

HRESULT __fastcall AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/);

int __fastcall AddInt(int x/*[in]*/, int y/*[in]*/);

};

typedef TCOMIMyCOMT<IMyCOM> TCOMIMyCOM;

template<class T>

class IMyCOMDispT : public TAutoDriver<IMyCOM>

{

public:

IMyCOMDispT(){}

IMyCOMDispT(IMyCOM *pintf)

{

TAutoDriver<IMyCOM>::Bind(pintf);

}

IMyCOMDispT& operator=(IMyCOM *pintf)

{

TAutoDriver<IMyCOM>::Bind(pintf);

return *this;

}

HRESULT BindDefault(/*Binds to new instance of CoClass MyCOM*/)

{

return OLECHECK(Bind(CLSID_MyCOM));

}

HRESULT BindRunning(/*Binds to a running instance of CoClass MyCOM*/)

{

return BindToActive(CLSID_MyCOM);

}

HRESULT __fastcall AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/);

int __fastcall AddInt(int x/*[in]*/, int y/*[in]*/);

};

typedef IMyCOMDispT<IMyCOM> IMyCOMDisp;

template <class T> HRESULT __fastcall

TCOMIMyCOMT<T>::AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/)

{

return (*this)->AddInt(x, y, ret);

}

template <class T> int __fastcall

TCOMIMyCOMT<T>::AddInt(int x/*[in]*/, int y/*[in]*/)

{

int ret;

OLECHECK(this->AddInt(x, y, &ret));

return ret;

}

template <class T> HRESULT __fastcall

IMyCOMDispT<T>::AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/)

{

static _TDispID _dispid(*this, OLETEXT("AddInt"), DISPID(1));

TAutoArgs<2> _args;

_args[1] = x /*[VT_INT:0]*/;

_args[2] = y /*[VT_INT:0]*/;

return OutRetValSetterPtr(ret /*[VT_INT:1]*/, _args, OleFunction(_dispid, _args));

}

template <class T> int __fastcall

IMyCOMDispT<T>::AddInt(int x/*[in]*/, int y/*[in]*/)

{

int ret;

this->AddInt(x, y, &ret);

return ret;

}

typedef TCoClassCreatorT<TCOMIMyCOM, IMyCOM, &CLSID_MyCOM, &IID_IMyCOM> CoMyCOM;

#endif // __TLB_NO_INTERFACE_WRAPPERS

}; // namespace Pcomserver_tlb

#if !defined(NO_IMPLICIT_NAMESPACE_USE)

using namespace Pcomserver_tlb;

#endif

#pragma option pop

#endif // __PCOMServer_TLB_h__

下面是文件中说明的主要对象及其定义:

interface IMyCOM : public IDispatch

class TCOMIMyCOMT : public TComInterface<IMyCOM>

class IMyCOMDispT : public TAutoDriver<IMyCOM>

class CoMyCOM: public CoClassCreator

◆IMyCOM:通过IDispatch接口来调用对象的方法,该接口可以使对象被不支持虚拟函数表(VTable)的语言(如Visual Basic)说调用。这是一种很慢很苯的接口调用方式。

◆TCOMIMyCOMT:通过所谓的智能接口来调用对象的方法,既可以实现IDispatch调用,也可以采用VTable进行调用,从而实现最快的调用速度。

◆IMyCOMDispT:通过disp接口来调用对象的方法,可以提高Idispatch接口的访问速度,但还是比不上VTable接口。

◆CoMyCOM:通过使用CoClassCreator可以自动产生TCOMIMyCOM代理的实例。

下面介绍一下实现智能接口和Disp接口调用的客户程序。这个客户程序很简单,有两个按钮分别完成两种接口调用的方法,一个编辑框显示结果。

◆智能接口的VTable调用方法如下:

int x=6,y=6;

TCOMIMyCOM O;

O=CoMyCOM::Create(); //通过CoClassCreator完成初始化

O->AddInt(x,y,&y); //Vtable形式调用

Edit1->Text=IntToStr(y);

◆DISP接口的调用方法如下:

int x=6,y=6;

IMyCOMDisp app;

app.BindDefault(); //通过Bind完成初始化

app.AddInt(x,y,&y); //Disp形式调用

Edit1->Text=IntToStr(y);

4.小结

上面的程序举例是很简单的,但却详细说明了COM应用软件的开发过程。COM技术不是一个编程语言,而是一种编程规范和方法。采用COM技术可以开发功能强大的软件,有利于分布式应用技术的实现,有利于多人合作开发,也可以帮助我们理解Windows系统本身。COM的接口技术是比较复杂的,想进一步了解COM技术可参阅清华大学出版社的《COM技术内幕》一书。

C++ Builder是开发COM应用软件的好工具,它隐含了COM实现的细节,我们只需与它打交道就可以开发完善和强大的COM应用程序。希望有更多的人转到COM应用软件的开发上来,COM技术是软件技术未来的发展方向,是实现软件工程中软件即插即用的有效途径。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Bounds 函数 输入上下高宽返回一个矩形结构。 CollectionsEqual 函数 比较两个TCollection是不是相等。 CurrentGroup 变量 FindClass 函数 从输入字符串中返回一个从TPersistent继承的类 FindGlobalComponent 变量 返回一个最高阶的容器类。 GetClass 函数 返回一个已经注册了的从TPersistent继承的类 LineStart 函数 返回下一行的开始字符位置 ObjectBinaryToText 过程 转换对象的二进制流形式到文件。 ObjectResourceToText 过程 转换对象的二进制资源到文件。 ObjectTextToBinary 过程 转换保存对象的文件形式到二进制流。 ObjectTextToResource 过程 转换保存对象的文件形式到资源。 Point 函数 输入X,y坐标返回一个点结构。 ReadComponentRes 函数 从窗口资源中读出一个控件和她的属性。 ReadComponentResEx 函数 同上 ReadComponentResFile 函数 从窗口资源文件中读出一个控件和她的属性。 Rect 函数 输入上下左右返回一个矩形结构 RegisterClass 过程 注册一个从TPersistent继承的类,使她的类的类型能够被识别。 RegisterClassAlias 过程 注册一个和另一个除了名字外都相同的类。 RegisterClasses 过程 注册一组类 RegisterComponents 过程 注册一组控件 RegisterIntegerConsts 过程 RegisterNoIcon 过程 注册一组没有图标的控件 RegisterNonActiveX 过程 防止一组控件被ActiveX wizard识别。 UnregisterClass 过程 反注册一个类 UnregisterClasses 过程 反注册一组类 UnregisterModuleClasses 过程 反注册定义在模块中的所有的类 WriteComponentResFile 过程 写控件和她的属性到文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值