|
蔡倩
4 l7 o! G3 r* {! M3 Z5 ?* T( z主题词: COM ActiveX C++ Builder
% t4 ?2 L; s# v! A; m1 S1.COM技术概述
5 b. a/ V( V ~COM表示Component Object Model(组件对象模型),它是Microsoft大力推广的软件开发技术。采用COM规范开发的应用软件具有强大的功能,主要有如下几点: / n; h8 y5 [/ N* R7 \/ x0 ~- N
◆COM是二进制编程规范,可以编写被多种语言使用的代码。
* m/ V# A6 A, k" b4 S! \◆用于创建ActiveX控件。 6 V/ r* X. d5 S: ]
◆通过OLE Automation 控制其它的程序。
) m% y% N9 D: e9 W7 z7 \* N. L◆与其它机器上的对象或程序进行对话,构成分布式应用程序。
0 o1 I1 L: d- i4 _/ OMicrosoft推出Windows 98和Windows NT 5.0后,整个操作系统的核心都围绕着COM来建立。我们可以把Windows系统看作是一系列的COM接口,在需要是可以调用这些接口。如DirectX就是一系列的COM接口服务程序,通过它可以进行高性能的Windows图形程序设计。
: m8 w# V2 g+ v2 D+ H2 h用COM技术开发的应用程序从理论上说是客户/服务器模式的程序。程序员可以使用一系列的COM服务程序来构造他们自己的应用程序,这些服务程序可以根据需要随时嵌入到主程序中。在分布式系统中,可以通过网络来访问这些服务程序。将来,操作系统和整个网络可能会被看作是一套以COM对象形式提供的服务集。一部分程序员负责建立这些服务,而另一部分程序员只负责如何调用它们。其目的是实现软件的即插即用。 6 d! H! p. W! N
开发COM应用程序是比较复杂的,通常需采用ActiveX模板库(ATL)来编程。在这里我们推荐采用C++ Builder来开发COM程序,Inprise(Borland)公司的面向对象技术一直处于世界领先水平,C++ Builder采用可视化方法,隐藏了ATL的实现细节,自动生成COM接口所需的代码。
7 E9 A+ y( M; \以下的程序举例采用C++ Builder 4.0 编制,在中文Windows98环境下运行。 8 b* S+ k+ i; i/ N, U$ m
2.建立COM服务程序 % }2 W- B' E- Q5 G8 R) }
COM服务程序有三种形式,第一种是驻留在本地机器上以DLL形式提供,该服务程序被调用时,嵌入到调用程序的线程中运行;第二种是驻留在本地机器上以EXE形式提供,该服务程序被调用时将占用独立的线程运行;第三种驻留在远端机器上以EXE形式提供,服务程序通过网络被调用,它在远端机器上运行,结果通过网络返回调用者。
4 I5 t% f) i7 J% h, C9 e3 S/ W7 A 在此采用第一种形式建立COM服务程序,这也是最常用的形式,DirectX就是采用这种形式提供的。 ' d, f2 b/ |0 I( `4 p8 M
C++ Builder建立COM服务程序的方法如下:
7 H" S, c% h( i! Z! x2.1创建支持COM接口对象的动态连接库文件: - E, v, V4 \, E: m
◆打开File/New/ActiveX项目页,选择ActiveX Library;
& p, I8 W! \ k2 k+ h$ t: b◆选择Save All 将项目以PCOMServer文件名保存;此时C++ Builder 自动生成如下的文件: 7 E0 y. o) x# |8 r! j! n( K& q
PCOMServer.bpr:工程的项目文件;
9 Q5 I$ u5 |1 m0 w* n PCOMServer.h,PCOMServer.cpp:支持COM对象的动态连接库源文件,其中有许多函数用于COM接口对象的自动装配,大家不用去编辑它们; / ~' {$ _7 r4 Z- b6 J) F4 h4 w
PCOMServer_ATL.h,PCOMServer_ATL.cpp:ATL形式的文件供C++ Builder编译器调用,大家也不要去编辑它们。 ! u1 f% i9 y1 N9 N! U2 }& T; m
◆打开Project/Options/Linker 属性页不选中Use dynamic RTL选项,打开Project/Options/Packages属性页不选中Builder with runtime packages选项,这两步操作可以使开发的COM动态连接库不依赖C++ Builder的VCL动态连接库,有利于独立发行,但在一般情况下还是建议选中这两项。
% ]- C' x, a. b( p I, Q$ w1 z0 m2.2建立COM接口对象
@' y3 U, T# Z+ k 7 }( F' p6 D; G
; T, o! O5 f+ h3 G
打开File/New/ActiveX属性页,选择Automation Object表示向服务程序中插入一个自动类型的COM对象,我们选择这种类型的COM对象是为了可以自动注册,并且自动支持可以被其他语言调用。此时出现如下的对话框,输入COM类的名字MyCOM即可,对话框中的其它选项用于规定COM对象的性质,可查看帮助信息。 & w2 _9 p) v. {
2.3通过类型库编辑器编辑COM对象中相应接口对象的属性和方法 " R' e, P1 U, s# Y& ~
; ]/ }! @/ g3 X' \+ v2 Z9 l( k% j6 }
9 F4 u2 H9 q b8 n& B8 F 此时自动进入类型库编辑器,类型库用于存储COM对象的说明,是一个可以被多种语言调用的头文件包。在类型库中,可以定义COM对象的接口,定义接口对象的属性和方法等。类型库编辑器如下所示:
/ o! ~, T# o4 O5 Z8 a5 v8 w+ J 可以看出此时自动产生了MyCOM类的一个接口类IMyCOM,在COM应用软件中我们实际上是与接口对象打交道,下面通过类型库编辑器为IMyCOM接口定义方法和属性。
2 ^- L9 A3 P7 r8 N# D$ b/ o◆单击编辑器顶部的Method按钮;
& Q9 B" g5 B' f# J( B% ~0 s◆在Arributes页面的Name字段中输入方法的名称,本例中是AddInt用于整数加法;
, _5 s" R0 T# J! B! N2 F◆在Parameters页面中,单击Add按钮编辑方法中的参数;
6 B) `& O/ Y qx和y是输入的两个整数,ret用于返回运算的结果,必须定义为指针型
$ Q3 a6 }9 i E t5 X◆切换到Flags页面,可以对接口的属性作调整; 9 ~8 n6 a% t; M; o
◆在Text页面中可以检查生成的IDL代码:
4 x M0 h/ `* G/ y[id(0x00000001)] # K* }$ I0 {& d8 E2 ?' Q
! M! P* H+ W! j
+ O, ? E! ?' k# n# |HRESULT _stdcall AddInt([in] int x, [in] int y, [out, retval] int * ret );
2 i; f* m1 u9 U% e$ S; s {◆单击Refresh按钮,此时可以关闭类型库编辑器。当需要为接口添加新的属性和方法时,可以通过View/Type Library重新打开编辑器。选择Save All用C++ Builder提供的缺省文件名保存类型库的相关文件如下:
8 A3 ~/ v5 j; T7 f3 [3 xPCOMServer.TLB: 类型库文件;
) W2 g- T- y4 q! @1 B) ?' z9 D6 d* x; U* PPCOMServer_TLB.cpp:包含COM接口和对象的说明,其主要目的是方便访问,在客户程序中需将本文件包含到客户程序的工程中; 7 I3 E8 L) x. ?: y# r
PCOMServer_TLB.h: PCOMServer_TLB.cpp的头文件,通过#include引入到客户程序中。 4 J" C, r3 L% P4 R* a
MyCOMImpl.cpp: 该文件是我们需要编写程序代码的地方,实现类型库定义的接口对象的方法和属性;
5 Q1 k! Q, {1 B1 DMyCOMImpl.h: MyCOMImpl.cpp的头文件。
' r7 b; C( n( E& [5 B/ p/ Z: r: |2.4 实现COM接口中的方法
0 h- s0 f3 \" j, Y4 \" n% E1 r 打开MyCOMImpl.cpp文件会发现我们在类型库编辑器中定义的方法,为该方法编写代码如下:
5 o4 q! ]5 o2 ], D3 t$ s STDMETHODIMP TMyCOMImpl::AddInt(int x, int y, int* ret) 2 {3 G% | @/ I6 \+ a
{
4 |# V W3 e/ \1 d! E *ret=x+y; ) N. A1 A9 V8 S/ R
return S_OK;
; J3 F% T! U$ z- X, ~} # c% _/ _. P" a, K5 F
2.5 生成DLL文件并注册COM对象
8 N8 H* a4 H, Z' t◆选择Project/Builder PCOMServer 生成PCOMServer.DLL文件。
1 E% l- `6 o& A◆打开类型库编辑器,单击Register按钮完成对COM对象的注册。 ; U6 e7 g1 u) N% ]( `3 q) t9 G
通过Windows任务栏中的Run菜单运行REGEDIT程序,在Windows注册表的HKEY_CLASSES_ROOT键下查找到PCOMServer.MyCOM子键,PCOMServer为DLL文件的名字,MyCOM为COM对象的名字,在下面可以看到该COM对象的全局唯一描述符CLSID如下:
" W. ]& K6 _8 O( R4 T {59834F03-49F1-11D3-B85B-00E09804A418} g& Z, f2 D$ E; o1 v0 D( `
注意:不同的机器生成的描述符不同. * S6 |& m/ [% V2 U1 f
在HKEY_CLASSES_ROOT键下查找到CLSID子键,在它下面找{59834F03-49F1-11D3-B85B-00E09804A418}子键,下面有如下的条目:
% K8 }6 D' k- U3 D( t; E* {+ H InprocServer32:存储PCOMServer.DLL的路径目录; / d8 i* t5 J2 b; W
ProgID:存储COM对象的注册名:PCOMServer.MyCOM;
& D& z8 Q6 ]' l" U Typelib:存储COM对象的CLSID值{59834F03-49F1-11D3-B85B-00E09804A418}。 ! T$ f2 i* N4 g, h
COM对象就是通过在注册表中的纪录实现DLL与客户程序的自动连接。 $ H- C% b4 b2 i F
3.建立COM客户程序 . v+ ~) d9 |: ]1 [
客户程序将访问PCOMServer.DLL服务程序中的MyCOM对象,这些对象的说明保存在前面所述的TLB文件中。我们可以直接将PCOMServer_TLB.cpp加入到客户程序的项目文件中,并在客户程序中引用PCOMServer_TLB.h文件;也可以通过Project/Import Type Library引用PCOMServer_TLB.TLB文件,重新生成.cpp和.h文件,自动完成上述过程。 8 G" y7 u% K. ` B. L, w8 M
客户程序的编程重点是实现对服务程序中COM对象的方法的调用,调用的方法有多种,都是通过所谓的代理接口来完成的,这些代理接口在PCOMServer_TLB.h中有详细的定义,从这些定义中可以看出这些代理接口调用对象方法的过程。
6 O, z/ L' W( y& `1 C& N4 R- M8 _PCOMServer_TLB.h文件很重要,包含了调用MyCOM对象的各种接口信息,该文件主要内容如下:
. S% T: _5 Z, ?% |9 B* D, m// Type Lib: D:\CAI\com\PCOMServer.tlb
4 M/ u( [8 K9 g( C// IID\LCID: {5BD378E5-4B57-11D3-B85B-00E09804A418}\0
2 L) m' i. v8 A' u8 Q* Q7 Y// Helpfile: ' l' M% W% M( }
// DepndLst: 4 q- R. i. E- o) s. M# O
// (1) v2.0 stdole, (C:\WINDOWS\SYSTEM\STDOLE2.TLB)
' d1 N r7 d2 s( `// (2) v4.0 StdVCL, (C:\WINDOWS\SYSTEM\STDVCL40.DLL) / j; d2 v8 k) w1 P
// ************************************************************************ 3 \. X7 @# S& t/ n! K7 A4 }
#ifndef __PCOMServer_TLB_h__ 3 {1 y9 }3 p; q
#define __PCOMServer_TLB_h__ & \1 E0 x( `7 P l1 T) K
#pragma option push -b -w-inl
# T4 ]% E% ]1 c; Q) y8 X" J, T7 e#include <vcl/utilcls.h>
H. m' y: m5 l8 i* e: H3 ^#if !defined(__UTILCLS_H_VERSION) || (__UTILCLS_H_VERSION < 0x0101) % _2 v8 m; g% I) ]3 `$ a
#error "This file requires an newer version of the header file UTILCLS.H" , Z$ Q8 i }2 f% a, U
#endif ! m. @5 a8 D) I- Q+ p0 a% v
; Y7 [4 z' b$ z1 P0 Z+ e2 H
#include <olectl.h> 4 o) g- D% R5 @5 T) L3 ^
#include <ocidl.h> ; L5 K A. R ]7 }
#if defined(USING_ATLVCL) || defined(USING_ATL)
# ]3 s$ E& _7 ^+ n% }& g8 Z#if !defined(__TLB_NO_EVENT_WRAPPERS)
0 `6 K1 t; n$ H: ~6 {#include <atl/atlmod.h> $ h$ e+ m1 W* {" _+ v9 |- {
#endif 1 A- y# {5 s3 T7 y: v3 T
#endif - |% n; B2 l$ h- l
- r$ O `! C9 T8 G+ n& Z! ?" Tnamespace Stdvcl {class IStrings; class IStringsDisp;}
; Z1 b( S/ K _using namespace Stdvcl;
% m$ U& x; q. e+ ^ U" h
W) C7 S; N1 O$ T& ^) u8 Knamespace Pcomserver_tlb - f# j! ~; }, h2 z" l2 L
{ 6 g. S- n- w9 M$ X+ _% ]9 _
DEFINE_GUID(LIBID_PCOMServer, 0x5BD378E5, 0x4B57, 0x11D3, 0xB8, 0x5B, 0x00, 0xE0, 0x98, 0x04, 0xA4, 0x18);
' T0 C: l$ e6 \) _" M5 DDEFINE_GUID(IID_IMyCOM, 0x5BD378E6, 0x4B57, 0x11D3, 0xB8, 0x5B, 0x00, 0xE0, 0x98, 0x04, 0xA4, 0x18);
) g, q- ?% P6 MDEFINE_GUID(CLSID_MyCOM, 0x5BD378E8, 0x4B57, 0x11D3, 0xB8, 0x5B, 0x00, 0xE0, 0x98, 0x04, 0xA4, 0x18);
/ M) G k9 E4 S1 m4 }interface DECLSPEC_UUID("{5BD378E6-4B57-11D3-B85B-00E09804A418}") IMyCOM;
; }$ c$ K: H0 L+ C2 p' ^: v9 Ftypedef IMyCOM MyCOM; % ~$ Z2 r: f2 g
' V" \6 P# P, k0 N, f' q#define LIBID_OF_MyCOM (&LIBID_PCOMServer) , q% ^& ]4 }9 P8 \ b6 i
interface IMyCOM : public IDispatch
) R1 y x5 n) T{
& Z# _4 U3 D: R! s" ?% Apublic: 3 K' S6 U" H! S& P/ I
virtual HRESULT STDMETHODCALLTYPE AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/) = 0; // [1]
) [1 |) Z$ }) y: \+ f#if !defined(__TLB_NO_INTERFACE_WRAPPERS) 5 y7 x ]+ W5 I
int __fastcall AddInt(int x/*[in]*/, int y/*[in]*/) $ p8 z( W. A: W+ q
{ 4 p+ v. ~8 O* c- C- `+ ]* G
int ret; / x4 ~4 g3 U2 |$ A/ S
OLECHECK(this->AddInt(x, y, &ret)); ' v1 @8 |! `5 W
return ret; 5 T; J0 ~6 b# N5 j+ ?$ Q2 R2 |+ q
}
7 |/ q. J1 h: O8 P& b2 R#endif // __TLB_NO_INTERFACE_WRAPPERS
0 e# c7 U5 x v* _+ m. G" N# F}; ) z4 [) }+ y9 D8 f- W
#if !defined(__TLB_NO_INTERFACE_WRAPPERS)
+ r8 \! l- |6 K9 `) Utemplate <class T /* IMyCOM */ >
0 J O* y$ t, x6 ]' n& c2 w 2 m' L" o# y) P$ ^: V* I
class TCOMIMyCOMT : public TComInterface<IMyCOM>, public TComInterfaceBase<IUnknown> 2 I5 M, J- T3 n! C/ K
{
$ l; X8 L* L, z7 z* M4 @public: T4 D% J+ a3 ?$ s3 g1 T
TCOMIMyCOMT() {}
( h' ?' ~6 Y' w+ [! ^ TCOMIMyCOMT(IMyCOM *intf, bool addRef = false) : TComInterface<IMyCOM>(intf, addRef) {}
/ Y* [( S# [2 ]# A( z8 S1 R TCOMIMyCOMT(const TCOMIMyCOMT& src) : TComInterface<IMyCOM>(src) {}
0 f7 l' O" j: G& X TCOMIMyCOMT& operator=(const TCOMIMyCOMT& src) { Bind(src, true); return *this;}
& y$ Z+ r. m' Y HRESULT __fastcall AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/);
% u: y5 ~$ Z. {2 K3 r) p int __fastcall AddInt(int x/*[in]*/, int y/*[in]*/); & [- z1 O# j" w
}; ) T1 o6 D9 I' t: t" U
typedef TCOMIMyCOMT<IMyCOM> TCOMIMyCOM; " J- G6 K" V9 Y( _, m& G
0 d* Z0 E8 T0 |; o& |9 S( s
template<class T> 6 T6 y' P. c' S* \
class IMyCOMDispT : public TAutoDriver<IMyCOM>
8 M- R6 }9 t( t7 `% \* c/ L{
7 _& X7 i3 u0 Z* O: ?; Vpublic: 6 ^; _! g8 {4 H
IMyCOMDispT(){}
! }' w5 Z1 ~# c4 c1 I9 J6 e9 Y IMyCOMDispT(IMyCOM *pintf) 0 ^# f7 h& G; F3 K( `6 Q" x
{ $ `" k7 r$ }. d: L# E/ H
TAutoDriver<IMyCOM>::Bind(pintf);
4 P$ j. C9 k, }8 x4 E/ h- Q } " }$ h8 A" k0 D3 @# D% U
IMyCOMDispT& operator=(IMyCOM *pintf) + `' s- u. H- F6 o% B8 c# C
{
1 L" b* B; H+ o/ O TAutoDriver<IMyCOM>::Bind(pintf);
0 Z9 S2 @) ]8 W n return *this; $ o. k! p- `2 {, j: A9 U7 A% F
}
$ L# f q' y% Q: E# {. ~6 q HRESULT BindDefault(/*Binds to new instance of CoClass MyCOM*/)
, N4 L' l3 {; X { ! I3 K+ p: ]. g# t2 Y$ i) U
return OLECHECK(Bind(CLSID_MyCOM));
4 a6 t: l4 x `* q9 j ? }
1 n ]# K" }6 T, d9 O' h: O HRESULT BindRunning(/*Binds to a running instance of CoClass MyCOM*/) - e# v9 v, J/ w# N( x6 P- l2 T. [
{
/ V4 ?* o9 y* Z% o7 j! `4 L/ H5 c2 i return BindToActive(CLSID_MyCOM);
) ~6 g5 c" [( Y) ~. l }
J5 `! z2 U6 x' r HRESULT __fastcall AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/);
8 y: q( u5 d4 W+ ? int __fastcall AddInt(int x/*[in]*/, int y/*[in]*/); - D( C% B7 [$ f, U7 ?; [1 |* `, V; o
};
8 ^6 T% W* S: ?# k. \typedef IMyCOMDispT<IMyCOM> IMyCOMDisp;
4 T9 o6 c0 W) a( T$ i1 D
. I7 W1 j/ h1 H5 n/ qtemplate <class T> HRESULT __fastcall
5 ?, j' j; Q$ qTCOMIMyCOMT<T>::AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/)
$ n/ s( a' [- J; w3 R) w{
9 v% [& D7 a& C1 Z& T1 s/ g3 q return (*this)->AddInt(x, y, ret); 7 ?, ?4 D$ z z# T
}
& x' R7 a& l! P
' R$ H; b5 I, z" Ttemplate <class T> int __fastcall ) z! G: z/ D* M* k; b* L+ W! T( D
TCOMIMyCOMT<T>::AddInt(int x/*[in]*/, int y/*[in]*/)
" }4 Z( t( \) |2 y2 m7 X+ u{ 4 S: J- J- s7 Y) H! ^- T; t
int ret; * T* z" L& C7 n, E& j& [
OLECHECK(this->AddInt(x, y, &ret));
8 J4 R% x. T; H0 e1 H) v return ret; 7 ~! V% J4 ~- Y6 v+ J" m/ v7 N
}
1 c: ^7 M. _: A7 z5 d. a+ ` 6 D# k' N+ n8 r: o" t
template <class T> HRESULT __fastcall
$ I) ]& l$ [/ _3 v$ g* C9 }IMyCOMDispT<T>::AddInt(int x/*[in]*/, int y/*[in]*/, int* ret/*[out,retval]*/) . c- ~8 Z Y, z1 u9 \' i! t
{ # M$ q/ N3 w- y5 ^- R ], H7 L
static _TDispID _dispid(*this, OLETEXT("AddInt"), DISPID(1)); . q/ |% ?- o. i0 v |0 I
TAutoArgs<2> _args;
& @# B/ ~: g0 [/ K. v _args[1] = x /*[VT_INT:0]*/; 7 P+ h( A3 ~6 B1 F, a
_args[2] = y /*[VT_INT:0]*/; % a3 G5 B6 M9 O8 ]4 U
return OutRetValSetterPtr(ret /*[VT_INT:1]*/, _args, OleFunction(_dispid, _args));
4 c' v# E# S. l2 A" x) a0 H}
0 a/ n& e$ Y/ J1 x7 _
0 D1 d4 k4 f$ l7 ^4 ?0 z* B8 Ntemplate <class T> int __fastcall
) Q+ I; h2 q) l7 s/ K" v$ FIMyCOMDispT<T>::AddInt(int x/*[in]*/, int y/*[in]*/) & H: R$ m1 ]8 G4 _
{ 1 _' C6 L1 }+ b" W. O
int ret; ' U( N- [) V' ^: L9 C' ~2 i0 L
this->AddInt(x, y, &ret); 5 A, ^! Q$ f* \6 T1 w
return ret; % D; z9 a4 S( y( m/ {4 k& ?7 v
}
- U7 ~1 s. h3 _, D+ n& _ & c, ]9 P5 R/ S+ z8 E I7 v
typedef TCoClassCreatorT<TCOMIMyCOM, IMyCOM, &CLSID_MyCOM, &IID_IMyCOM> CoMyCOM; 3 V9 m' C! ]6 b* N/ Z/ f( F
#endif // __TLB_NO_INTERFACE_WRAPPERS 3 `' Q% y, j$ G7 C. v
}; // namespace Pcomserver_tlb % J9 t! [) E1 g
#if !defined(NO_IMPLICIT_NAMESPACE_USE) . M" S% b5 i9 b8 u
using namespace Pcomserver_tlb; % y/ O* o8 Q2 g+ s( A8 s3 G9 a* i' Z
#endif 0 D% a4 J5 f! B' m* @. d
#pragma option pop
3 ~9 j! ^" E% v4 H" B#endif // __PCOMServer_TLB_h__
+ J" c, p1 B9 v) M+ n y; t( I下面是文件中说明的主要对象及其定义: 0 S0 f. c! v5 s
interface IMyCOM : public IDispatch * D) a5 A. D, P; ^
class TCOMIMyCOMT : public TComInterface<IMyCOM>
! W4 g' Q( N1 Vclass IMyCOMDispT : public TAutoDriver<IMyCOM>
/ j% {' J0 T: j0 Z# T" Uclass CoMyCOM: public CoClassCreator & ]/ w" Z# p0 r) `* y% C1 g
◆IMyCOM:通过IDispatch接口来调用对象的方法,该接口可以使对象被不支持虚拟函数表(VTable)的语言(如Visual Basic)说调用。这是一种很慢很苯的接口调用方式。
# E3 G3 ]' p, G. L* ^◆TCOMIMyCOMT:通过所谓的智能接口来调用对象的方法,既可以实现IDispatch调用,也可以采用VTable进行调用,从而实现最快的调用速度。 ' Q$ d' v5 `+ n" T) ~
◆IMyCOMDispT:通过disp接口来调用对象的方法,可以提高Idispatch接口的访问速度,但还是比不上VTable接口。
% Q; ]# S+ R7 |7 D7 p3 y4 f◆CoMyCOM:通过使用CoClassCreator可以自动产生TCOMIMyCOM代理的实例。
2 J1 ^1 N4 n, q5 d; {) G' K* C! u 下面介绍一下实现智能接口和Disp接口调用的客户程序。这个客户程序很简单,有两个按钮分别完成两种接口调用的方法,一个编辑框显示结果。
7 f- j& h4 C6 ?8 U# {5 B+ N◆智能接口的VTable调用方法如下: 3 j8 o- ~. b8 V" d- V' T
int x=6,y=6; ! d, l( U: {" n
TCOMIMyCOM O; 1 A- }5 ]0 \1 Z% L) M
O=CoMyCOM::Create(); //通过CoClassCreator完成初始化
w: D6 P9 O5 o7 d7 t: @! z O->AddInt(x,y,&y); //Vtable形式调用 ^" D' ^; }- {; q) ]
Edit1->Text=IntToStr(y);
! ?) Q6 p! N* H d◆DISP接口的调用方法如下: & T& A0 B) D, U$ `5 S' U
int x=6,y=6; , c/ K+ Q. W' `5 p" |' C# f
IMyCOMDisp app;
. h9 N# z' l, t% u) l app.BindDefault(); //通过Bind完成初始化
2 ]+ i* G; ^1 T. ~4 k8 ^ app.AddInt(x,y,&y); //Disp形式调用
, a! w. U \; l e6 y Edit1->Text=IntToStr(y); % \; j/ L( B# c' j
4.小结 " N: \7 g7 K# \: J9 E
上面的程序举例是很简单的,但却详细说明了COM应用软件的开发过程。COM技术不是一个编程语言,而是一种编程规范和方法。采用COM技术可以开发功能强大的软件,有利于分布式应用技术的实现,有利于多人合作开发,也可以帮助我们理解Windows系统本身。COM的接口技术是比较复杂的,想进一步了解COM技术可参阅清华大学出版社的《COM技术内幕》一书。
7 x" s& {+ \7 B% G1 Z: ?! X# o C++ Builder是开发COM应用软件的好工具,它隐含了COM实现的细节,我们只需与它打交道就可以开发完善和强大的COM应用程序。希望有更多的人转到COM应用软件的开发上来,COM技术是软件技术未来的发展方向,是实现软件工程中软件即插即用的有效途径。 / T7 v3 }+ P; @, ~
/ c @1 \1 o2 K9 z% s$ t- g
|