在Delphi中使用Visual Studio编译的C代码

在软件开发中,C语言因其性能和广泛的应用而备受青睐。Delphi,作为一种面向对象的编程语言,提供了丰富的库和组件,非常适合开发具有图形用户界面(GUI)的应用程序。然而,将Visual Studio编译的C代码与Delphi项目链接一直是一个挑战,因为两者使用的编译目标文件格式不同。本文将介绍如何克服这一障碍,实现两者的无缝集成。

尽管Embarcadero提供了C++ Builder,但在纯C或C++开发方面,它仍然落后于Visual Studio。幸运的是,最新的Delphi版本支持读取COFF对象文件,这为将Visual Studio编译的C代码与Delphi项目链接提供了可能。

准备工作

首先,确保已经安装了Visual Studio和Delphi XE2或更高版本。接下来,将创建一个Visual Studio项目,编写C代码,并将其编译为对象文件。然后,将这些对象文件与Delphi项目链接

创建Visual Studio项目

打开Visual Studio 2010,选择“文件”菜单中的“新建项目”,然后选择“Visual C++”部分。建议选择“Win32控制台应用程序”或“Win32项目”,这样可以在Visual Studio中测试整个项目。

接下来,添加一个C++文件和一个头文件。这两个文件将包含将要编译并与Delphi链接的代码。在“解决方案资源管理器”中,右键单击新添加的C++文件,选择“属性”,在“高级”子节点下,选择“编译为C代码(/TC)”,并在“预编译头文件”子节点下,选择“不使用预编译头文件”。

在项目属性的“常规”节点下,选择“使用Unicode字符集”。禁用所有与异常处理相关的设置,以避免复杂性(例如,将“启用C++异常”设置为“No”,将“缓冲区安全检查”设置为“No”)。最后,在“优化”子节点下,将“全程序优化”设置为“No”。

编写C代码

现在,可以开始编写C函数了。完成后,右键单击源文件并选择“编译”。为32位和64位编译,然后将.obj文件复制到DelphiXE2项目的文件夹中。

在Delphi中声明函数

Delphi链接器没有关于在Visual Studio 2010中定义的函数参数的信息,也没有关于Delphi链接器在链接时需要解析的所有外部引用的信息。因此,需要在Delphi单元中声明所有这些信息。

对于32位Delphi程序,黄金规则是:所有函数声明前都要加上下划线,调用约定应为cdecl(默认情况下是这样)。所有外部引用都应具有cdecl调用规范;如果是调用Windows API,必须在Visual Studio中使用具有cdecl调用规范的中间函数(请参阅示例程序,了解如何解决MessageBoxW API的调用)。

对于64位Delphi XE2程序,链接Visual Studio对象文件更容易,因为:只有一个调用约定,即fastcall。函数名不加下划线。直接解析Windows API的外部引用,无需采取行动。

示例代码

以下是一个Delphi源文件的示例。请注意之前提到的所有要点。对于32位:下划线函数,cdecl调用约定,直接使用msvcrt.dll导出的函数,以及在Visual Studio中使用cdecl扩展的中间函数,以便从Delphi调用Windows API(stdcall)。

对于64位,它更容易,主要是因为只有一个调用约定。

unit VsAndDelphi; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm2 = class(TForm) GroupBox1: TGroupBox; lblFirstValue: TLabel; edFirstValue: TEdit; lblSecondValue: TLabel; edSecondValue: TEdit; btAddValues: TButton; GroupBox2: TGroupBox; lblCaption: TLabel; lblMessage: TLabel; edCaption: TEdit; edMessage: TEdit; btShowMessage: TButton; lblDispStrings: TLabel; Label5: TLabel; GroupBox3: TGroupBox; lblPubIntVar: TLabel; edPublicIntVal: TEdit; btGeetCVars: TButton; btGetString: TButton; lblPublicStrVar: TLabel; edPublicStrVal: TEdit; procedure btAddValuesClick(Sender: TObject); procedure btShowMessageClick(Sender: TObject); procedure btGeetCVarsClick(Sender: TObject); procedure btGetStringClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form2: TForm2; type bigarray = array[0..127] of char; {$IFDEF CPUX86} {$L CtoDelphi32.obj} function _addNumbers(value1 : integer; value2: integer): integer; cdecl; external; function _wcscpy_s(S1: PChar; count: size_t; S2: PChar): Integer; cdecl; external 'msvcrt.dll' name 'wcscpy_s'; function _wcscat_s(S1: PChar; count: size_t; S2: PChar): Integer; external 'msvcrt.dll' name 'wcscat_s'; procedure _cShowGetMessage(incaption: string; intext: string; size: integer; var retVal: bigArray); cdecl; external; function _MessageBoxW2(theHwnd:HWND; lpText : PWideCHAR; lpCaption : PWideCHAR; uType:UINT): integer; cdecl; // Actually, these are not procedures but pointers to the VS variables: procedure _publicCInteger; external; procedure _publicCArray; external; // Public variable to be accessed from C var _myDelphiPublicIntVariable : integer; _myDelphiPublicStrVariable : string; {$ELSE} {$IFDEF CPUX64} {$L CtoDelphi64.obj} function addNumbers(value1 : integer; value2: integer): integer; external; procedure cShowGetMessage(incaption: string; intext: string; size: integer; var retVal: bigArray); external; function wcscpy_s(S1: PChar; count: size_t; S2: PChar): Integer; external 'msvcrt.dll' name 'wcscpy_s'; function wcscat_s(S1: PChar; count: size_t; S2: PChar): Integer; external 'msvcrt.dll' name 'wcscat_s'; // Actually, these are not procedures but pointers to the VS variables: procedure publicCInteger; external; procedure publicCArray; external; // Public variable to be accessed from C var myDelphiPublicIntVariable : integer; myDelphiPublicStrVariable : string; {$ENDIF} {$ENDIF} implementation {$R *.dfm} procedure TForm2.btGeetCVarsClick(Sender: TObject); var myCInt : integer; begin {$IFDEF CPUX86} myCInt := integer((@_publicCInteger)^); showMessage(inttostr(myCInt)); {$ELSE} {$IFDEF CPUX64} myCInt := integer((@publicCInteger)^); showMessage(inttostr(myCInt)); {$ENDIF} {$ENDIF} end; procedure TForm2.btGetStringClick(Sender: TObject); var myCArray : pchar; begin {$IFDEF CPUX86} myCArray := pchar((@_publicCArray)^); showMessage(myCArray); {$ELSE} {$IFDEF CPUX64} myCArray := pchar((@publicCArray)^); showMessage(myCArray); {$ENDIF} {$ENDIF} end; procedure TForm2.btAddValuesClick(Sender: TObject); var retValue : integer; value1, value2 : integer; begin value1 := strToInt(edFirstValue.Text); value2 := strToInt(edSecondValue.Text); {$IFDEF CPUX86} _myDelphiPublicIntVariable := strToInt(edPublicIntVal.Text); retValue := _addNumbers(value1, value2); {$ELSE} {$IFDEF CPUX64} myDelphiPublicIntVariable := strToInt(edPublicIntVal.Text); retValue := addNumbers(value1, value2); {$ENDIF} {$ENDIF} showMessage('Sum is '+inttoStr(retValue)); end; procedure TForm2.btShowMessageClick(Sender: TObject); var retVal : bigArray; arrayLength : integer; begin arrayLength := length(retVal); {$IFDEF CPUX86} _myDelphiPublicStrVariable := edPublicStrVal.Text; _cShowGetMessage(edCaption.Text, edMessage.Text, arrayLength, retVal); {$ELSE} {$IFDEF CPUX64} myDelphiPublicStrVariable := edPublicStrVal.Text; cShowGetMessage(edCaption.Text, edMessage.Text, arrayLength, retVal); {$ENDIF} {$ENDIF} showMessage(retVal); end; function _MessageBoxW2(theHwnd:HWND; lpText : PWideCHAR; lpCaption : PWideCHAR; uType:UINT): integer; cdecl; begin result := MessageBoxW(theHwnd, lpText, lpCaption, uType); end; end.
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485