Click here to Skip to main content
13,357,219 members (77,210 online)
Click here to Skip to main content
Add your own
alternative version


13 bookmarked
Posted 5 Oct 2011

Using COFF C object files with Delphi X2

, 8 Oct 2011
Rate this:
Please Sign up or sign in to vote.
Still struggling to find the ultimate COFF to OMF converter to link some code with Delphi? You don't need to anymore.


People who develop software with Delphi and want to link extraneous object files are happy with Embarcadero for two reasons: the first one is because Embarcadero did not come up with any OMF (Object File Format) extension to use with the new 64-bit compiler, it just uses COFF. The second one is because the Delphi XE2 can also link with COFF object files when compiling for 32-bit (in addition to OMF). 


There are literally millions of lines of 'C' code waiting to be compiled and linked with Delphi in order to receive a nice GUI. I know that Embarcadero has a C++ Builder, but for pure (no GUI) C or C++ development, it stays behind Visual Studio in every respect. 


How can we use "C" code compiled with Visual Studio inside Delphi? Till now, it was nearly impossible because Visual Studio compiles to COFF, Delphi understood only OMF, and converters from COFF to OMF were not reliable because the OMF used by Delphi has many proprietary undocumented portions. 


I mentioned earlier that the latest Delphi release can read COFF object files, but I have not said that it is a piece of cake to actually link those object files with Delphi. 


So, is it easy or not to link with Delphi XE2 code compiled with Visual Studio? 


The answer is: "It is not difficult, just pay attention to a few details. It is even easier for 64-bit code."


Using the code

To show how it is done, let's open VS 2010, select New Project in the File menu, and go to the Visual C++ section. You can select the Empty Project on the General node, but is preferable to select either Win32 Console Application or Win32 Project on the Win32 Project node because this allows you to test the whole project in VS. After doing this, add a C++ file and a Header file from the Project menu. These two files are the ones where we are going to put the code that will compile and link with Delphi. In Solution Explorer, right-click the new C++ file you just added, select Properties, and in the sub-node Advanced of the C/C++ node, select Compile as C Code (/TC), and in the Precompiled Headers sub-node, select Not Using Precompiled Headers. Now, in the Project properties, General node, select Use Unicode Character Set. Disable all settings of the project that deal with exception handling to avoid complications (i.e., Enable C++ Exceptions="No" and Buffer Security Check = "No"). Finally, set Whole Program Optimization to "No" in the Optimization sub-node.

You are set and can start coding away your C functions. When done, right-click on the source file and select Compile. Do compilations for 32-bit and 64-bit, then copy the .obj files to the folder where you have the Delphi XE2 project.

The Delphi linker has neither information about the parameters of the functions you defined in VS2010 nor information about all the externals that need to be resolved at link time by the Delphi Linker. So, you need to declare all that in a Delphi unit. 

To link with 32-bit Delphi programs, the golden rules are:

  1. All the function declarations will be proceeded by an underscore and the calling convention should be cdecl (all right, this is the default).
  2. All the externals should have the cdecl calling spec; if it is a call to a Windows API, you must use in VS an intermediate function with a cdecl calling spec (see our demo program to see how we solved the call to the MessageBoxW API).
  3. Genuine externals with the cdecl calling spec are probably exports from msvcrt.dll and you can use this DLL from your Delphi program without the need to figure out replacement functions (see our demo program to see how).

To link VS object files with 64-bit Delphi XE2 programs, it is easier because:

  1. There is only one calling convention, fastcall.
  2. Function names are not underscored.
  3. Externals to the Windows API are directly resolved without our action.
  4. Using msvcrt.dll functions is just a question of declaring them in Delphi.

I provide a demo with full source code which you are recommended to download and study if you are not experienced in these matters. 

The Delphi source file is shown below. Notice all the points I mentioned earlier. For 32-bit: underscored functions, cdecl calling convention, direct use of msvcrt.dll exported functions, and use of an intermediate function in Visual Studio with the cdecl extension when a call to Windows API (stdcall) is to be made from Delphi.  

As you can see below, for 64-bit, it is easier mostly because there is only one calling convention. 

unit VsAndDelphi;


{$IF CompilerVersion < 23}
  ->   Requiires Delphi XE2 or later

  Winapi.Windows, Winapi.Messages, System.SysUtils, 
  System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

  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 declarations }
    { Public declarations }

  Form2: TForm2;
bigarray = array[0..127] of char;

{$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
      _myDelphiPublicIntVariable : integer;
      _myDelphiPublicStrVariable : string;
{$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
      myDelphiPublicIntVariable : integer;
      myDelphiPublicStrVariable : string;


{$R *.dfm}

procedure TForm2.btGeetCVarsClick(Sender: TObject);
   myCInt : integer;
    myCInt := integer((@_publicCInteger)^);
    myCInt := integer((@publicCInteger)^);

procedure TForm2.btGetStringClick(Sender: TObject);
  myCArray : pchar;
     myCArray := pchar((@_publicCArray)^);
     myCArray := pchar((@publicCArray)^);

procedure TForm2.btAddValuesClick(Sender: TObject);
  retValue : integer;
  value1, value2 : integer;
    value1 := strToInt(edFirstValue.Text);
    value2 := strToInt(edSecondValue.Text);
    _myDelphiPublicIntVariable := strToInt(edPublicIntVal.Text);
    retValue := _addNumbers(value1, value2);
    myDelphiPublicIntVariable := strToInt(edPublicIntVal.Text);
    retValue := addNumbers(value1, value2);
    showMessage('Sum is '+inttoStr(retValue));

procedure TForm2.btShowMessageClick(Sender: TObject);
  retVal : bigArray;
  arrayLength : integer;
      arrayLength := length(retVal);
      _myDelphiPublicStrVariable := edPublicStrVal.Text;
      _cShowGetMessage(edCaption.Text, edMessage.Text, arrayLength, retVal);
      myDelphiPublicStrVariable := edPublicStrVal.Text;
      cShowGetMessage(edCaption.Text, edMessage.Text, arrayLength, retVal);

function _MessageBoxW2(theHwnd:HWND; lpText : PWideCHAR; 
         lpCaption : PWideCHAR; uType:UINT):integer; cdecl;
  result := MessageBoxW(theHwnd, lpText, lpCaption, uType);


All right, that's all about it. I won't show here the 'C' source files, they are pretty basic anyway and the most important task do be done in Visual Studio is its configuration (as I referred above). Have a careful look at the .vcxproj file. 

A final note, in VS stdafx.h file, there is a #define DEBUGGING. You enable it to test the routines in VS, you disable it when you compile just the CtoDelphi.cpp file.


  • Oct 4 2011 - Initial version release.
  • Oct 8 2011 - Shows how to access Visual Studio public variables from Delphi and Delphi public variables from Visual Studio.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Jose A Pascoa
AtelierWeb Software
Portugal Portugal
Jose Pascoa is the owner of AtelierWeb Software ( We produce security and network software and mixed utilities since 1999. The first program I published (in a BBS) was a MS-DOS utility, had the size of 21 KB and was done in Assembly Language. Nowadays, my low level languages are more likely to be "C", "C++" and "Delphi" rather than Assembly Language but I still like it. I have nothing against more fashionable languages like C# and technologies like WPF, actually I have played with them and published software with them.

You may also be interested in...


Comments and Discussions

QuestionGreat Article , but there is already an Object File Conversion library Pin
Jamming19-Oct-11 9:45
memberJamming19-Oct-11 9:45 
AnswerRe: Great Article , but there is already an Object File Conversion library Pin
Jose A Pascoa9-Oct-11 10:35
memberJose A Pascoa9-Oct-11 10:35 
GeneralRe: Great Article , but there is already an Object File Conversion library Pin
Jamming19-Oct-11 11:18
memberJamming19-Oct-11 11:18 
GeneralRe: Great Article , but there is already an Object File Conversion library Pin
Jose A Pascoa10-Oct-11 1:13
memberJose A Pascoa10-Oct-11 1:13 
GeneralRe: Great Article , but there is already an Object File Conversion library Pin
Jose A Pascoa15-Oct-11 12:33
memberJose A Pascoa15-Oct-11 12:33 
GeneralRe: Great Article , but there is already an Object File Conversion library Pin
Jamming115-Oct-11 14:16
memberJamming115-Oct-11 14:16 
GeneralRe: Great Article , but there is already an Object File Conversion library Pin
Jose A Pascoa15-Oct-11 23:24
memberJose A Pascoa15-Oct-11 23:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.180111.1 | Last Updated 8 Oct 2011
Article Copyright 2011 by Jose A Pascoa
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid