一.引言
在当今计算机世界中,各种编程语言琳琅满目,从Basic、Pascal到C++、Java再到汇编语言,应有尽有。 Delphi和C++ Builder可以说是Inprise公司开发的编程语言系列中的“绝代双娇”。Delphi使用的是Object Pascal语法,它具有易学易用的特性,能使初学者迅速编写出表现力极强的程序;而C++Builder对于高水平的程序员而言无疑是优秀的开发工具, 它最大程度体现了C++语言的灵活性和适用性,大大提高了程序的运行效率。如果能够把这两种语言结合使用势必会大大提高程序的开发效率。
其实,用户完全可以在C++Builder和Delphi之随心所欲的进行多平台程序开发,Delphi和C++Builder之间具有十分密切的关系, 他们在性能上非常相似。你用Delphi编写的任何代码几乎都可用在C++Builder中,同样用C++Builder写的部分代码也可用在 Delphi中。这样在编写程序中,程序员们就可根据自己的喜好和特长决定实用哪种语言,而不必担心使用一种语言编写的代码不会被另一种语言接受。编好之 后只要遵从一定的规则把它们连接在一起即可使用。 二.理论依据
Delphi语言使用了面向对象的Pascal,而C++Builder使用的语言是C++,这两种工具编写的完全不同代 码是如何连接在一起的呢?这主要是因为两者使用相同的编译器后端,编译后产生的目标文件是一致的,不同之处仅仅是前端的语言分析差异而已。Delphi的 编译速度一般要比C++ Builder快,这是由于Delphi采用了以单元为基础的编译方式,生成一种更为灵活的二进制文件-DCU格式,但是C++Builder的增量编译 方式减少了二者编译速度上的差距。其次,C++Builder的VCL控件(Visual Component Library可视构件库,简称VCL)全部由Delphi编写,因此用Delphi编写的VCL控件可直接引入C++Builder的IDE中无须作任 何改动。第三,C++与Object Pascal语言的基本数据类型非常相似,这使的在编译时二者之间进行数据传送非常方便,以下为二者的基本数据类型的比较(表-1)
表-1. C++与Object Pascal的基本数据类型比较
C++ |
Object Pascal |
C++ |
Object Pascal |
Unsigned Char |
Byte |
Char |
Char |
Char A[size] |
Array[0..size-1] |
Int |
Integer |
Unsigned int |
Word |
Long |
Longint |
Unsigned long |
Longint |
Float |
Single |
Double |
Double |
Char far* |
Pchar |
Struct |
Record |
Union |
Record with variants |
Ansistring |
String |
VARIANT |
Variant |
第四,二者都具有相同调用约定格式Stdcall,无论是Object Pascal还是C++都规定,凡是要输出的例程必须采用Stdcall调用约定。由于Stdcall也是Windows95/98/NT的API采用的 调用约定。因此,用户可放心地使用Stdcall调用约定共享Object Pascal、C++以及Windows95/98/NT输出的例程。以下为C++和Object Pascal各自的调用约定(表-2)。其实,从本质上讲Object Pascal是C++的一个子集。
表-2. C++和Object Pascal的调用约定
Object Pascal |
C++ |
默认 |
Cdecl |
Cdecl |
C++的默认调用约定 |
Pascal |
Pascal |
Delphi1.0、Windows3.x的默认调用约定 |
Stdcall |
Stdcall |
Windows95/98/NT的默认调用约定 |
Register |
_Fastcall |
Delphi2.0/3.0/4.0/5.0的默认调用约定 |
三. 代码共享
面向对象编程的基本出发点就是代码重用。要实现Delphi与C++Builder之间的代码共享显然有两种方法:一是在C++Builder中使用Delphi写的代码,二是在Delphi中使用C++Builder写的代码。下面我们就这两种方式分别加以叙述。
3.1. 在C++Builder中使用Delphi的代码
Inprise公司推出C++Builder的时间要比推出Delphi的时间短,在推出C++Builder时中已经充分考虑到了Delphi优秀特 性,因此二者在开发环境(Integrated Development Environments,简称(IDEs)、可视组件库(Visual Component Library,简称VCL)、单元文件(Form Files)和组件包(Packages)上非常相似,C++Builder的整个VCL都是用Delphi写的,因此在在C++Builder中使用 Delphi的代码要比在Delphi中使用C++Builder的代码容易地多。以下就在C++Builder中使用Delphi代码方法的具体给予阐 述:
3.1.1.在C++Builder中直接使用Delphi的代码
C++Builder并不关心你加入在工程文件中的 单元文件(Unit)是用C++Builder编写的还是用Delphi编写的,C++ Builder将编译你加入的单元文件就象编译C++Builder的代码一样,尽管C++Builder不支持Delphi的某些语法格式,例如 try…finally 和with结构等,但这些语法格式都可以在C++Builder中找到相应的替代语法,你会发现你用Delphi写的大部分代码几乎不做任何改动就可以在 C++Builder中编译。你可以在C++Builder中直接加入以下Delphi代码:
1.Delphi的窗体(Forms)
2.Delphi的单元文件(Units)
3.包含有过程(Procedures)、函数、常量,数组等的Delphi单元文件(Units)
4.Delphi的组件(Components)
加入方法如下:
1.选择C++Builder的Project | Add 菜单
2. 在工程对话框中选择所要加入的文件(文件扩展名为*.pas)并确定C++Builder将会为所加入的Delphi文件自动生成头文件(*.hpp),但应当注意在C++Builder中直接使用Delphi的代码,应当遵循以下基本规则:
1. 不能在同一个单元(Unit)中既使用C++Builder的代码又使用Delphi 的代码,必须在各自单独的单元内使用。
2. 不能在C++Builder中使用Delphi DCU格式的二进制文件。
3. 在C++Builder中打开Delphi的表单(Form)后,不能对其进行编辑,但是可以先继承该表单,再进行编辑。
4.如果一个应用程序其工程文件(Projectname.cpp)用C++Builder写的,即使整个应用程序98%的代码是用Delphi编制 的,该应用程序仍然为C++Builder程序。这也意味着你不可以将Delphi的工程文件放入C++Builder中来编译。
3.1.2. 在C++Builder中使用Delphi的组件
组件是Delphi、C++Builder中代码重用的基础,几乎所有的Delphi组件都可以在C++Builder中使用而无须做任何改动,毕竟整 个C++Builder的组件都是由Delphi编写的。 通以下方法在C++Builder中使用Delphi的组件:
1.选择C++Builder的Component | Install菜单
2.选择添加组件(Add Components)
3.选择所要加入的组件并确定
C++Builder将会自动重新编译CMPLIB32.DLL文件, 你所加入的组件(Component)就会显示在组件模板(Component Palette)上。然后就可以象使用C++Builder的组件一样使用该组件。切记在操纵使用该组件时一定要使用C++Builder的语法而不是 Delphi的语法。因为在添加该组件时C++Builder已经为你自动作了转换。
3.1.3. 通过COM和OLE在C++Builder中使用Delphi的代码
Delphi内建了对COM的支持,由于COM 对象具有语言和平台无关性,用Delphi创建的COM对象可以与用C++Builder、Visual Basic、Java、Visual C++、Word、Excel,等其他OLE敏感控件之间的代码共享。此外,在多层Client/Server环境中,COM对象还可以封装商业规则,为 分布式的客户提供服务。有三种基本方法可以将Delphi COM 对象连接到C++Builder中:
1. 使用OLE自动化或双端接口(Interfaces).
2. 使用Delphi 创建ActiveX控件然后在C++Builder载入此控件
3. 编写原始的COM 或OLE代码来连接Delphi对象
C++Builder内建了对前两种方法的支持,第三种方法要求具备较高的OLE知识。操纵一个自动化对象有两种方法:一是通过COM 对象的Idispatch接口,C++Builder中通过VCL函数CreateOleObject和Variant数据类型内建了对 Idispatch接口的支持,这是使用自动化对象的最简单的方法;.二是通过COM 对象的双端接口(Dual Interface),使用双端接口可以获得较快的速度,但是该方法实现起来比较困难,需要具备很深的COM理论知识,具体方法和DLL中的虚拟方法表 (Mapping Virtual Method Tables)相似,限于篇幅本文不在起具体实现细节。先假设有一个Delphi的Idispatch接口,输出以下方法:
ITest = interface(IDispatch)
[`{1746E520-E2D4-11CF-BD2F-0020AF0E5B81}']
function Get_Value: Integer; safecall;
procedure Set_Value(Value: Integer); safecall;
procedure Prompt(const text: WideString); safecall;
property Value: Integer read Get_Value write Set_Value;
...
end;
再假设这个动态连接库(DLL)已经注册并且其工程表识为"TestLib.Test"。.以下为在C++Builder 中调用该接口的Prompt过程的程序:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Variant V = CreateOleObject("TestLib.Test");
V.OleProcedure("Prompt", "Hello World!");
}
当然你也可以通过以上方法调用该接口中的其他过程和函数。用户应当注意在C++Builder中Variant类型并不是一个数据类型,所以在使用Variant类型时应当在工程文件中加入Sysdefs.h.。
3.1.4. 在C++Builder中使用Delphi的ActiveX 控件
C++Builder和Delphi完全支持ActiveX,您可以很方便地创建、注册、安装、发布和使用ActiveX 控件、ActiveForm和OLE自动化对象, 这应当归功于“对象接口”技术和DAX 技术。通过以下方法在C++Builder中使用Delphi 的ActiveX 控件:
1. 选择Component | Install 菜单
2. 在添加组件对话框(Install Components dialog)中选择OCX按钮
3. 所有已经注册的组件都会列在添加OLE控件对话框中(Import OLE Control dialog),选择所要加入的控件并确定。(如要注册注册 新控件选择Register按钮)
4. 新的控件会出现在已安装的组件表中,选择确定退出。
C++Builder自动将控件的接口文件写入LIB目录下,并且自动重新编译CMPLIB32将你的控件加入到组件模板中(Component Palette)。新控件会默认的出现在组件模板的OCX页上。
3.1.5. 在C++Builder中使用动态连接库来共享Delphi代码
Windows引入一种重要机制——DLL,即动态连接库。DLL中包含有例程和资源,可以使应用程序之间共享例程和资源。使用DLL不但可以实现C ++Builder与Delphi之间代码共享,而且还实现C++Builder与Visual C++、Visual Basic、Borland C++等其他编程工具之间代码共享。在Delphi中编写一个DLL程序非常简单,在此本文不再祥述编写DLL的具体方法,着重介绍一下如何在DLL中实 现模式Form和无模式Form。
外部程序要访问DLL中的模式Form,DLL中必须建立和输出访问例程。外部程序调用访问例程,访问例程去建立Form类的实例,当用户单击OK、Cancal、Abort等按钮关闭Form时,访问例程返回相应的值。下面是一个典型的访问例程示例:
procedure ShowModuleForm(AppHandle:Thandle):Integer;
begin
Result:=-1;
Application.Handle:=AppHandle;
with TMoudleForm.Create(Application) do
try
Result:=ShowModal;
finally
Free;
end;
end;
访问例程至少要有一个Thandle类型的参数,用于传递调用DLL外部程序主窗口的句柄。如果应用程序是用Delphi开发的,就是 Application.Handle 。访问例程首先创建Form类的实例,然后调用ShowModal显示这个Form,最后调用Free删除这个Form实例。当然,根据不同的需要,访问 例程可以增加若干参数。在上例中,访问例程首先把返回值设为-1,这样即使Form类的实例没有创建成功,返回值总是一个确定数。函数最终的返回值由 ShowModal返回的。最后不要忘记在DLL中输出访问例程。
所谓无模式Form,就是即使Form没有关闭,用户也可以把输入焦点 移到其它窗口。要访问DLL中的无模式Form,DLL中需要建立和输出两个访问例程,一个用于创建Form类的实例和显示Form,另一个用于关闭 Form和删除类的实例。其它规则与访问模式Form基本相同。一个典型的创建和显示无模式Form的访问例程示例如下:
function CreateAndShow(AppHandle:THandle):Integer ;
var MyHandle:THandle;
begin
Application.Handle:=AppHandle;
MyHandle:=THandle(TNon-ModalForm.Create(Application));
{创建TNon-ModalForm类的实例,并强制转换为THandle类型}
Result:=MyHandle;{把Form的句柄作为返回值}
with TNon-TMoudleForm(MyHandle) do
try
Show;
except
Free;
Result:=0;
Raise;
end;
end;
一个典型的关闭和释放类实例的访问例程示例如下:
procedure CloseAndFree(FormHandle:THandle);
begin
if FormHandle=0 then
Raise Exception.Create('Form类的实例没有创建!');
else
with TNon-ModalForm(FormHandle) do
begin
Close;
Free;
end;
end;
我们也可以把类写成DLL,然后被其它程序或DLL共享,但要注意不能直接使用DLL中的类的方法和成员,而只能调用DLL中输出的类。同样DLL中也可以包含虚拟类,但是实现起来比较麻烦,限于篇幅本文不再叙述。
3.2.在Delphi中使用C++Builder的代码
从本质上讲Object Pascal是C++的一个子集,因此在Delphi中使用C++Builder的代码要比在C++Builder中使用Delphi的代码困难的多。在 Delphi中直接使用C++Builder的代码几乎是行不通的,但是由于OLE、COM、DLL都是Windows系统的规范,因此可以通过这三种方 法很方便在二者之间实现代码共享。在Delphi中通过这三种方法来使用C++Builder的代码和在C++Builder中通过这三种方法来使用 Delphi代码基本相同,在此本文不在另行叙述。
尽管Delphi也支持OBJ文件,在Delphi中连接C++Builder的 OBJ文件也很简单,只要用{$Link}或{$L}指示字指定要连接的OBJ,其格式为:{$L 文件名},但是Delphi的程序只能访问OBJ中很简单的例程。用户既不能访问OBJ中C++的类,也不能访问C++的RTL(运行期库),也不能访问 C++的几个全局变量。也就是说如果例程中包含对TRL的调用或者对Windows API的调用,或者OBJ中包含C++类的声明,Delphi的链接器都将失败。因此在Delphi中用C++的OBJ几乎没有任何实用价值。
四.结论
本文详细叙述了实现C++Builder与Delphi代码共享的各种方法,通过比较可以发现在C++Builder中共享Delphi的代码,要比在 Delphi中共享C++Builder的代码容易得多。但是通过DLL、OLE和COM我们也可以很方便的在Delphi中共享C++Builder 的代码。
此外,还可以使用一些中间层方式来实现C++Builder和Delphi之间代码共享,所谓中间层方式是指将C++ Builder的代码和 Delphi的代码分开编译成两个可执行模块,然后通过中间层实现两个模块的数据共享和相互操纵。中间层可以是剪贴板、DDE、磁盘文件、BDE以及 Windows的消息。不过这已经不是真正意义上的代码共享了,只是程序间的数据交换,因而使用这种结构的程序执行速度比较慢,如果对程序执行的速度要求 不是很高,也可以考虑使用中间层结构。
综上所述,我们发现在C++Builder和Delphi之间实现代码的共享是比较容易的事,不过 实现的难度有所不同,在C++Builder中共享 Delphi的代码要比在Delphi中共享C++Builder的代码要容易一些,因此我们提倡尽量用C++Builder建立工程文件,充分利用两种 语言各自的优点,在C++Builder中共享Delphi的代码,以加快整个软件开发的速度