我有一个c++编译的动态连接库,里面有这样一个函数
int (*CertificateOperate)( char *UserID , char *InFile , char *OutFile , char * CertificateFile , int OperateType )
我现在在delphi 5中调用这个函数,请问怎么定义与,调用,
由于涉及到指针,所以两种语言形参,实参的定义,我都搞胡涂了,
int (*CertificateOperate)( char *UserID , char *InFile , char *OutFile , char * CertificateFile , int OperateType )
我现在在delphi 5中调用这个函数,请问怎么定义与,调用,
由于涉及到指针,所以两种语言形参,实参的定义,我都搞胡涂了,
解决方案 »
- 将界面显示如windows xp这样。
- 我想实现按F1-F12键触发一段程序,怎么实现?要用到哪些控件?怎么设置?
- onCustomDrawSubItem功能是什麼
- 寻能够用于编辑公式和电路图的控件,分不是问题!
- BDE 设置问题
- 对DBGrid中选中的行删除(含代码),在线等待~
- 控制在dbGrid的一个integer型字段中录入数据(EditFormat?)
- 请问这样的情况各位是怎样很好的解决的?
- 急急急!midas+socketconnection的服务器端首次运行实际做哪些配置?
- 菜鸟问题,怎么样才能但不运行到DELPHI的SOURCE 文件里面?
- 在DELPHI中开发DLL的步聚应该怎样?
- 请问哪里有优秀的报表控件下载?
Delphi以其独特的面向控件的开发方式、强大的数据库功能以及 快速的编译技术,使得它自发布起即格外引人注意。随着Delphi 3提 供丰富的Internet应用,Delphi日益成为最重要的软件开发工具之一, 它吸引了许多原Visual Basic、Foxpro、dBase甚至C++的程序员,而 这些程序员使用Delphi时需要解决的一个重要问题就是怎样利用他们 原有的代码。本文将介绍Delphi与C++程序集成的方法,包括:
S Delphi与C++之间函数的共享;
S 代码的静态链接和动态链接;
S 对象的共享。
函数的共享
在Delphi中调用C++函数与C++调用Delphi函数相当直接,需要注 意的是,Delphi 1默认的函数调用方式是Pascal方式,Delphi 2、Delp hi 3的默认方式则是优化的cdecl调用方式,即register方式。要在C+ +与Delphi程序之间实现函数共享,除非有充分的原因,否则应该使用 标准系统调用方式,即stdcall方式。为了使C++编译器不将函数标记 为"mang led",使Delphi编译器误认为函数是采用cdecl调用方式,应 该在C++代码中,以extern "C "说明被共享的函数,如下例所示:
原型说明:
在C++中:
extern "C" int _stdcall TestFunc();
在Delphi中:
function TestFunc:integer; stdcall;
调用语法:
在C++中:
int i=TestFunc();
在Delphi中:
var i:integer;
…
begin
…
i:=TestFunc;
…
end;
共享函数的参数必须是两种语言都支持的变量类型,这是正确传 递参数的前提。诸如Delphi的currency、string、set等变量类型,在 C++中没有相对应的变量类型,不能被用作共享函数的参数。可以用PC har类型以值参的方式传递字符串指针,这时用户必须注意字符串空间 的回收。
Delphi语言中的变参应被说明成C++中相应变量类型的引用形式, 如下:
在Delphi中:
function TestFunc(var i:integer):integer;
在C++中:
int TestFunc(int &i);
代码链接
在Delphi与C++之间实现代码链接可采用静态链接或动态链接的 方式。
1.静态链接方式
如果C++程序本身的代码量很小,而且无需担心与C运行库会有交 互过程,一般可选用静态链接方式,即把Delphi与C++的目标文件(*.OB J)链接成最终的可执行文件。具体的方法是使用{$L}编译指令,使Del phi编译器自动读取指定目标文件,说明如下:
function TestFunc:integer;stdcall;
{$L TestFunc.OBJ}
2.动态链接方式
如果C++代码已经相当全面或自成一个完整的子系统,代码量很大 ,或者用到了C运行库,在这种情况下,应该使用动态链接库(DLL)的方 式。此时,在两种语言的源代码中应做如下说明:
在C++中:
int stdcall export TestFunc();
在Delphi中:
function TestFunc:integer; stdcall;
external ‘TestFunc.DLL’;
对象的共享
在C++与Delphi之间的对象共享主要体现在对象方法(Method)的 共享方面,这种共享可分为两个层次:对象(Object)级共享与类(Class )级共享。
要实现对象级共享,程序设计语言需具备两个前提条件:
S 能够定义指向由另一语言创建的对象的指针;
S 可以访问由指针确定的对象中的方法。
要实现类级的共享,则还需考虑:
S 能够创建由另一种语言定义的类的实例;
S 可以从堆中释放一个实例所占用的空间;
S 派生新的类。
以下介绍在Delphi与Borland C++之间实现对象共享的方法。
1.C++共享Delphi对象
要实现从C++调用Delphi对象,首先要在Delphi单元的接口部分以 及C++的头文件中说明需要共享的对象的接口,在对象接口中定义该对 象包含哪些属性与方法,并说明可供共享的部分。对象的共享,关键在 于方法的共享。在Delphi语言中,要使一个对象可以被共享,可以把它 说明为两个接口部分,暂称为"共享接口"与"实现接口"。其中共享接 口指明对象中哪些方法可被另一种语言所共享;实现接口则继承共享 接口,并且在单元实现部分针对实现接口中的方法定义具体的实现。 要定义一个可供C++共享的Delphi对象,共享接口的说明应注意: S 在Delphi程序里,要共享的方法必须被说明为抽象(abstract), 而且虚拟(virtual );
S 在C++程序里,必须用关键字"virtual"及"=0"后缀,把从Delphi 共享的方法说明成"pure virtual";
S 共享的对象方法必须在两种语言里都被说明成相同的调用方式 ,通常使用标准系统调用方式(stdcall)。
下面,举例说明这些规则,假设有这样的一个Delphi对象:
TTestObject=class
procedure Proc1(x:integer);
function Func1(x:integer):PChar;
procedure Proc2;
function Func2:integer;
end;
如果C++程序需要共享其中的方法Proc1、Func1,可把上述说明修
改成以下形式:
STestObject=class
procedure Proc1(x:integer); virtual; abstract; stdcall;
function Func1(x:integer); virtual; abstract; stdcall;
end;
TTestObject=class(STestObject)
procedure Proc1(x:integer);
fuction Func1(x:integer):PChar;
procedure Proc2;
fuction Func2:integer;
end;
在C++程序中做如下对象原型说明:
class STestObject {
virtual void Proc1(int x)=0;
virtual char *Func1(int x)=0;
};
为了能在C++中成功地访问Delphi定义的类, Delphi接口说明时 必须包含一个可共享的"制造函数(Factory Function)"CreateTestOb ject,该制造函数可被定义在动态链接库或目标文件(.OBJ)中,例如:
Library TestLib;
exports CreateTestObject;
function CreateTestObject:STestObject; stdcall;
begin
Result:=TTestObject.Create;
end;
…
end.
经过这样的处理,现在可在C++程序中使用这个由Delphi定义的对
象,调用方式如下:
extern "C" STestObject stdcall *CreateTestObject();
void UseTestObject(void) {
STestObject *theTestObject=CreateTestObject();
theTestObject->Proc1(10);
Char *str=theTestObject->Func1(0);
}
当调用制造函数CreateTestObject时,实际上已经在Delphi一侧 占用了一个对象实例的空间,C++程序在针对该对象的所有处理完成后 必须考虑释放这一空间,具体的实现可在Delphi中定义一个类,如上述 Proc1的共享方法Free,以此来完成这一任务:
STestObject=class
procedure Proc1(x:integer); virtual; abstract; stdcall;
function Func1(x:integer); virtual; abstract; stdcall;
procedure Free; virtual; abstract; stdcall;
end;
…
implementation
…
procedure TTestObject.Free;
begin
…
end;
…
end.
2.Delphi共享C++对象
通常,程序员会考虑使用Delphi来编制用户界面,所以Delphi代码 调用C++代码似乎显得更加实际些。其实,Delphi共享C++对象的实现 方法与上述C++共享Delphi对象非常相似。
用同样的共享接口与实现接口说明方法来定义C++的类:
class STestObjedt {
virtual void Proc1(int x)=0;
virtual char *Func1(int x)=0;
};
class TTestObjedt :public STestObject {
void Proc1(int x);
char *Func1(int x);
void Proc2();
int Func2();
void Free();
};
然后实现这些方法。同样地,C++对象需要一个与之对应的制造函 数,这里以DLL为例
STestObject stdcall export *CreateTestObject() {
return (STestObject *) new TTestObject.Create;
}
Delphi代码可以通过调用制造函数CreateTestObject,很容易地 在C++中创建实例,获得指向该实例的指针值,并以这个指针值来调用 对象中的共享方法。当然,在进行完该对象的相关处理后,千万不要忘 了调用Free释放占用的空间。
那么在delphi 中调用时,应该怎么样定义这个函数你,
也就是返回指针那一部分
作者:苏涌 本文选自:赛迪网 2002年02月19日
根据Delphi提供的有关 DLL编写和调用的帮助信息,你可以很快完成一般的 DLL编写和调用的 应用程序。本文介绍的主题是如何编写和调用能够传递各种参数(包括对象实例)的 DLL。例如, 主叫程序传递给 DLL一个ADOConnection 对象示例作为参数, DLL中的函数和过程调用通过该对象 实例访问数据库。 需要明确一些基本概念。对于 DLL,需要在主程序中包含 exports子句,用于向外界提供调用 接口,子句中就是一系列函数或过程的名字。对于主叫方(调用 DLL的应用程序或其它的 DLL), 则需要在调用之前进行外部声明,即external保留字指示的声明。这些是编写 DLL和调用 DLL必须 具备的要素。 另外需要了解Object Pascal 中有关调用协议的内容。在Object Pascal 中,对于过程和函数 有以下五种调用协议: 指示字 参数传递顺序 参数清除者 参数是否使用寄存器
register 自左向右 被调例程 是
pascal 自左向右 被调例程 否
cdecl 自右向左 调用者 否
stdcall 自右向左 被调例程 否
safecall 自右向左 被调例程 否
这里的指示字就是在声明函数或过程时附加在例程标题之后的保留字,默认为register,即是 唯一使用 CPU寄存器的参数传递方式,也是传递速度最快的方式;
pascal: 调用协议仅用于向后兼容,即向旧的版本兼容;
cdecl: 多用于 C和 C++语言编写的例程,也用于需要由调用者清除参数的例程;
stdcall: 和safecall主要用于调用Windows API 函数;其中safecall还用于双重接口。
在本例中,将使用调用协议cdecl ,因为被调用的 DLL中,使用的数据库连接是由主叫方传递 得到的,并且需要由主叫方处理连接的关闭和销毁。 下面是 DLL完整源程序和主叫程序完整源程序。包括以下四个文件: Project1.DPR {主叫程序}
Unit1.PAS {主叫程序单元}
Project2.DPR {DLL}
Unit2.PAS {DLL单元}
{---------- DLL 主程序 Project2.DPR ----------}library Project2;uses
SysUtils,
Classes,
Unit2 in 'Unit2.pas' {Form1};{$R *.RES}{ 下面的语句用于向调用该 DLL的程序提供调用接口 }
exports
DoTest; { 过程来自单元Unit2 }begin
end.
{---------- DLL中的单元 Unit2.PAS ----------}unit Unit2;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, ADODB, StdCtrls, Menus;type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;{ 本地数据库连接 }
Memo1: TMemo; { 用于显示信息 }
private
public
end;{ 该过程向外提供 }
procedure DoTest(H: THandle; { 获得调用者的句柄 }
AConn: TADOConnection;{ 获得调用者的数据库连接 }
S: string; { 获得一些文本信息 }
N: Integer); { 获得一些数值信息 }
cdecl; { 指定调用协议 } implementation{$R *.DFM}procedure DoTest(H: THandle; AConn: TADOConnection; S: string; N: Integer);
begin
Application.Handle := H; { 将过程的句柄赋值为调用者的句柄 }
{ 上面语句的作用在于, DLL的句柄和调用者的句柄相同,在任务栏中就不会 }
{ 各自出现一个任务标题了。 }
with TForm1.Create(Application) do try{ 创建窗体 }
Memo1.Lines.Append('成功调用'); { 显示一行信息 }
ADOConnection1 := AConn; { 获得数据库连接的实例 }
Memo1.Lines.Append(
ADOConnection1.ConnectionString +
' - ' + S + ' - ' + IntToStr(N)); { 根据得到的参数显示另一行信息 }
ShowModal; { 模式化显示窗体 }
finally
Free; { 调用结束时销毁窗口 }
end;
end;end.
{---------- 调用者 Project1.DPR,很普通的工程文件 ----------}program Project1;uses
Forms,
Unit1 in 'Unit1.pas' {Form1};{$R *.RES}begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
{---------- 调用者单元Unit1.PAS ----------}unit Unit1;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Db, ADODB;type
TForm1 = class(TForm)
Button1: TButton; { 按此按钮进行调用 }
ADOConnection1: TADOConnection; { 本地数据库连接,将传递给 DLL }
procedure Button1Click(Sender: TObject);{ 调用 DLL}
private
public
end;var
Form1: TForm1;implementation{$R *.DFM}{ 外部声明必须和 DLL中的参数列表一致,否则会运行时错误 }
procedure DoTest(H: THandle; { 传递句柄 }
AConn: TADOConnection; { 传递数据库连接 }
S: string; { 传递文本信息 }
N: Integer); { 传递数值信息 }
cdecl; { 指定调用协议 }
external 'Project2.dll';{ 指定过程来源 }{ 调用过程 }
procedure TForm1.Button1Click(Sender: TObject);
begin
DoTest(Application.Handle,
ADOConnection1,
'Call OK',
256);
end;end.