Board index » delphi » Calling C/C++ DLL's from Delphi Example

Calling C/C++ DLL's from Delphi Example

        Well, I finally got my 16-bit DLL issues ironed out in C++. Afterwards,
it took me all of thirty minutes to design, build, and execute a small,
example Delphi application that called its functions. If anyone else has had
problems with this task or is about to venture into this pseudo-documented
area of Delphi development, I'd be glad to either post the example in this
conference or email to individuals personally. The text file contains all the
files required to reproduce my test application as well as some information
on how to put it together. It's a bit over 330 lines of text in total which is
why I didn't automatically post it here. If enough people request it and there
are no objections, I can post it here for all to see.

        I really think that this capability makes Delphi significantly more
powerful. It should be all the more impressive when 32bit-Delphi is released.
Meanwhile, Borland could issue the Coup-de-Grace on VB & VC++ by releasing an
OS/2 version of the product thus providing the world's most powerful
platform-independent development environment available. Time for Borland to
retake its position as the world leader in PC-based development tools.
Saddens my heart when I glance through the tattered remains of my Turbo
Pascal 2.0 manual and see all the strange little OS's it supported....

        later,

                Ben Scherrey
                scher...@proteus-tech.com

 

Re:Calling C/C++ DLL's from Delphi Example


Per many requests to post and none not to, here is the file:

    How to call a C/C++ DLL from Delphi:

Following are all the files that compose the example system. I've
edited this thing down as much as possible to keep it short. First
is the C++ code for the DLL :
------------------------------ cut here -----------------------------
//
// TEST.HPP     Header Code File for Sample DLL
//

#ifndef TEST_HPP
#define TEST_HPP

#include "iostream.h"

#define STRICT
#include "windows.h"
#pragma hdrstop

extern "C"
{
    #pragma argsused
    int FAR PASCAL LibMain( HINSTANCE hInstance, WORD wDataSegment,
                            WORD wHeapSize, LPSTR lpszCmdLine )
    {
        if (wHeapSize !=0) UnlockData(0);
        return 1;
    }

    extern char* _export FAR getHello( void );
    extern char* _export FAR getGoodBye( void );

Quote
};

#endif // TEST_HPP
------------------------------ cut here -----------------------------
//
// TEST.CPP     Source Code file for Sample DLL
//

#include "test.hpp"

char* HelloText = "Hello from C++!";
char* GoodByeText = "GoodBye from C++!";

char* _export far getHello( void )
{
   return HelloText;

Quote
}

char* _export far getGoodBye( void )
{
   return GoodByeText;
Quote
}

------------------------------ cut here -----------------------------

    Build the two previous files as a Windows 16-bit DLL called
    TEST.DLL. Make an import library called TEST.LIB by running
    IMPLIB on the DLL. You may have to turn on all the case-
    sensitivity options in your linker settings (something I
    recommend that you do anyway).

    Next is a C++ file that will create an executable that tests
    the DLL under C++.

------------------------------ cut here -----------------------------
//
// DLLTEST.CPP       Code that uses sample dll.
//                   Opens a file, temp.txt and sends output to it.
//

#include "iostream.h"
#include "fstream.h"
#include "windows.h"

extern "C"
{
   extern char* FAR getHello( void );
   extern char* FAR getGoodBye( void );

Quote
};

#pragma argsused
int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow )
{
   char* Text;
   fstream Out;

   Out.open( "Temp.txt", ios::app );

   Out << "Let's say hello." << endl << "\t";
   Text = getHello();
   Out << Text << endl;

   Out << "Time to say GoodBye." << endl << "\t";
   Text = getGoodBye();
   Out << Text << endl;

   Out << "Done." << endl;

   return 1;

Quote
}

------------------------------ cut here -----------------------------

    Compile the previous file to a Windows 16-bit executable and link
    it to the TEST.LIB you created previously. This will allow it to
    load the DLL without having to write additional source code.
    Run the program with TEST.DLL in the same directory and a file,
    TEMP.TXT should be created that look like this:

                        ------ cut here --------
                        Let's say hello.
                            Hello from C++!
                        Time to say GoodBye.
                            GoodBye from C++!
                        Done.
                        ------ cut here --------

    Note - this executable is not required for Delphi, it simply
    confirms that we have a working and correct DLL before we start
    messing with Delphi issues. If you get a message saying that it
    can't find the function you're looking for then make sure that
    the linker options for this executable and the DLL have all the
    case-sensitivity options turned on.

    Next comes the Delphi setup. The first file is a text version
    of the form associated with unit Main. I'm not quite certain
    how to load it back up again as a form (which is normally
    stored in a binary format) so you might have to re-create it
    on screen. Anyway, this text version tells you everything you
    need to get the sample form put together. It's pretty basic.
    The file after it, MAIN.PAS, is the source file for the unit
    Main.

------------------------------ cut here -----------------------------
{ MAIN.DFM }
object Form1: TForm1
  Left = 200
  Top = 110
  Width = 640
  Height = 503
  Caption = 'Form1'
  Font.Color = clWindowText
  Font.Height = -17
  Font.Name = 'System'
  Font.Style = []
  PixelsPerInch = 120
  TextHeight = 20
  object MainPanel: TPanel
    Left = 8
    Top = 8
    Width = 617
    Height = 457
    Caption = ' '
    TabOrder = 0
    object Label1: TLabel
      Left = 109
      Top = 40
      Width = 399
      Height = 32
      Caption = 'Delphi to C++ DLL Test System'
      Font.Color = clNavy
      Font.Height = -27
      Font.Name = 'Arial'
      Font.Style = [fsBold, fsItalic]
      ParentFont = False
    end
    object Button1: TButton
      Left = 252
      Top = 112
      Width = 113
      Height = 57
      Caption = '&Go!'
      Font.Color = clRed
      Font.Height = -30
      Font.Name = 'Lucida Console'
      Font.Style = [fsBold]
      ParentFont = False
      TabOrder = 0
      OnClick = Button1Click
    end
    object Edit1: TEdit
      Left = 124
      Top = 240
      Width = 369
      Height = 35
      Font.Color = clGreen
      Font.Height = -23
      Font.Name = 'Times New Roman'
      Font.Style = []
      ParentFont = False
      TabOrder = 1
      Text = 'Should say hello.'
    end
    object Edit2: TEdit
      Left = 124
      Top = 336
      Width = 369
      Height = 35
      Font.Color = clGreen
      Font.Height = -23
      Font.Name = 'Times New Roman'
      Font.Style = []
      ParentFont = False
      TabOrder = 2
      Text = 'Should say goodbye.'
    end
  end
end

------------------------------ cut here -----------------------------
{ MAIN.PAS }
unit Main;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, ExtCtrls, StdCtrls, TEST;

type
  TForm1 = class(TForm)
    MainPanel: TPanel;
    Label1: TLabel;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  NowActive: Boolean;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var CHello: PChar;
var CGoodbye: PChar;
begin
     if NowActive then
         begin
             NowActive := false;
             Button1.Caption := '&Go!';
             Edit1.Text := 'Should say hello.';
             Edit2.Text := 'Should say goodbye.';
         end
     else
         begin
             NowActive := true;
             Button1.Caption := '&Reset';

             { C++ returns pointers to null terminated chars. }
             CHello := TEST.getHello;
             CGoodBye := TEST.getGoodBye;

             { Convert those pointers to Pascal strings. }
             Edit1.Text := strPas( CHello );
             Edit2.Text := strPas( CGoodbye );
         end;
end;

initialization
    NowActive := false;
end.
------------------------------ cut here -----------------------------
{ TEST.PAS - Provides access to C++ DLL from Delphi Application. }
unit TEST;

interface
    uses WinTypes, WinProcs;

    function getHello: PChar; cdecl; far;
    function getGoodBye: PChar; cdecl; far;

implementation

    function getHello: PChar; external 'TEST' index 2;
    function getGoodBye: PChar; external 'TEST' index 3;

end.
------------------------------ cut here -----------------------------

    The primary point of interest in TEST.PAS is the implementation
    definition of the two function calls. Note that they name TEST
    as the DLL name and explicitly identify the index of the
    function call. The way this index is determined is by running
    TDUMP against the DLL. Route the output of TDUMP as follows:

        tdump test.dll > temp.txt

    Now open TEMP.TXT in a text editor and do a search for the
    function of interest, in the case, getHello. You should see
    some lines similar to the following:

        Resident Name Table     offset: 00E0h
            Module Name: 'TEST'
            Name: WEP                          Entry: 0001
            Name: _getHello                    Entry: 0002
            Name: _getGoodBye                  Entry: 0003

    The entry code along-side each function name is it's index
    value. Now you have everything required to build your Delphi
    unit allowing access to the C++ DLL.

    The end result should be a Delphi project with two units,
    Main and TEST with Main having the only form associated with
    it. Build the project and copy the resulting executable and
    TEST.DLL into the same directory then run it. The result
    should be a small form with a single button, Go!, and two
    edit fields saying:

            Should say hello.
            Should say goodbye.

    Upon hitting the button, the button's caption should say,
    Reset, and the edit fields should change to say:

            Hello from C++!
            GoodBye from C++!

    Hitting reset should put everything back to it's original
    state and allow you to start over. Doesn't do much, obviously,
    but it provides as simple a framework as I could make for
    this example. Extending it should be extremely easy.

        Let me know how this works out for you!

            later,

                Ben Scherrey
                scher...@proteus-tech.com

(C)opywrite 1995, Proteus Technologies, Inc. ALL RIGHTS RESERVED
Permission is granted to redistribute this file and its contents
as long as it remains unchanged. Any proposed modifications should
be forwarded to scher...@proteus-tech.com for inclusion into
upcoming versions of this file. Next we'll be looking at how to
provide class-level interfaces between Delphi & C++.

Re:Calling C/C++ DLL's from Delphi Example


Quote
Benjamin Scherrey writes:

>Meanwhile, Borland could issue the Coup-de-Grace on VB & VC++ by releasing an
>OS/2 version of the product thus providing the world's most powerful
>platform-independent development environment available. Time for Borland to
>retake its position as the world leader in PC-based development tools.

As much as many of us would like to see this, unfortunately, I do not see
this happening anytime soon.  I would love to have a RAD environment for OS/2
like Delphi, but at this time, I think Borland's position on OS/2 is to not
have one.  :-(

==========================================
 Kerry Sanders
 Email: ksand...@mindspring.com
 Programmer's Shack BBS -*- (205)988-4695
==========================================

Re:Calling C/C++ DLL's from Delphi Example


In message <42g0er$1...@firehose.mindspring.com> - ksand...@mindspring.com (Ker
Quote
ry Sanders) writes:

:>
Quote
:>Benjamin Scherrey writes:

:>>
:>>Meanwhile, Borland could issue the Coup-de-Grace on VB & VC++ by releasing an
:>>OS/2 version of the product thus providing the world's most powerful
:>>platform-independent development environment available. Time for Borland to
:>>retake its position as the world leader in PC-based development tools.
:>
:>As much as many of us would like to see this, unfortunately, I do not see
:>this happening anytime soon.  I would love to have a RAD environment for OS/2
:>like Delphi, but at this time, I think Borland's position on OS/2 is to not
:>have one.  :-(

        I would tend to agree with you except for the fact that Borland produces the
finest OS/2 C++ compiler in the world. As a matter of fact, the OS/2
compiler's environment is much nicer than the Windows one. Of course, it does
appear that Borland has little to no interest in an OS/2 Delphi for the
forseable future. Hopefully we can encourage them otherwise...

        later,

                Ben Scherrey
                scher...@proteus-tech.com

Other Threads