Jump to content
Jean_D

Calling a Delphi DLL function from a C++ Builder application

Recommended Posts

I have created a DLL with one exported function using the latest version of Delphi (12.1). The function takes one parameter: a record type variable.

library MyDLL;

uses
  System, SysUtils;

type
  TMyRecord = record
    MyString: AnsiString;
    MyInteger: Integer;
  end;

function FillRecord(var Rec: TMyRecord): Boolean; stdcall; export;
begin
  Rec.MyString  := 'Hello from Delphi';
  Rec.MyInteger := 42;
  Result := True;
end;

exports
  FillRecord;

begin
end.

In my C++ Builder 6.0 application, I have declared the following:

struct TMyRecord {
  char *MyString;
  int MyInteger;
};

extern "C" __declspec(dllimport) bool __stdcall FillRecord(TMyRecord *Rec);

When calling the 'FillRecord' function from my C++ Builder application, I do not get the expected results:

   TMyRecord iMyRec;
   Memo1->Lines->Clear();
   Memo1->Lines->Add(Format("Address: %p", ARRAYOFCONST((&iMyRec))));
   if (FillRecord(&iMyRec)) {
      String iData = iMyRec.MyString;
      Memo1->Lines->Add("iMyRec.MyString:  " + iData);
      int iNumber = iMyRec.MyInteger;
      Memo1->Lines->Add("iMyRec.MyInteger: " + IntToStr(iNumber));
   } else {
      Memo1->Lines->Add("Error calling FillRecord");
   }

I am expecting:

iMyRec.MyString:  Hello from Delphi
iMyRec.MyInteger: 42

But I am getting:

iMyRec.MyString:  H
iMyRec.MyInteger: 42

 

I am drawing a blank when trying to figure out what I am doing wrong. Any inputs/suggestions to solve my issue would be greatly appreciated. Thank you
 

 

 

Share this post


Link to post

Either include the ShareMem unit in both Delphi and C++ so both sides understand Delphi's strings, or pass the string as a pointer (e.g. PAnsiChar) from the Delphi unit--which is what the C++ code is expecting.

Share this post


Link to post

Thank you very much. Passing the string as a pointer from the Delphi unit did fix my issue.

Share this post


Link to post
Posted (edited)
5 hours ago, Jean_D said:

When calling the 'FillRecord' function from my C++ Builder application, I do not get the expected results:

The C++ code is not creating a valid AnsiString object for the Delphi code to manipulate.  The struct would have needed to look more like this instead:

struct DECLSPEC_DRECORD TMyRecord
{
  AnsiString MyString;
  int MyInteger;
};

But, that still would not have worked correctly in your case, because the internal structure of AnsiString changed in 2009, so passing an AnsiString object between C++Builder 6 and Delphi 12.1 would cause Undefined Behavior,

 

It it never safer to pass non-POD types across a DLL boundary, unless very strict prerequisites are met (ie, sharing a common RTL, sharing the same memory manager, etc), which is not the case in this example.

 

To do what you are attempting, you would have to allocate AND  free the string memory inside of the DLL, and then pass that memory around as needed, eg:

library MyDLL;

uses
  System, SysUtils;

type
  TMyRecord = record
    MyString: PAnsiChar;
    MyInteger: Integer;
  end;

function FillRecord(var Rec: TMyRecord): Boolean; stdcall;
begin
  Rec.MyString  := StrNew(PAnsiChar('Hello from Delphi'));
  Rec.MyInteger := 42;
  Result := True;
end;

procedure DisposeRecord(var Rec: TMyRecord); stdcall;
begin
  StrDispose(Rec.MyString);
  Rec.MyString := nil;
end;

exports
  FillRecord,
  DisposeRecord;

begin
end.
struct DECLSPEC_DRECORD TMyRecord {
  char* MyString;
  int MyInteger;
};

extern "C" __declspec(dllimport) bool __stdcall FillRecord(TMyRecord *Rec);
extern "C" __declspec(dllimport) void __stdcall DisposeRecord(TMyRecord *Rec);

TMyRecord iMyRec = {};
Memo1->Lines->Clear();
Memo1->Lines->Add(Format("Address: %p", ARRAYOFCONST((&iMyRec))));
if (FillRecord(&iMyRec)) {
    AnsiString iData = iMyRec.MyString;
    Memo1->Lines->Add("iMyRec.MyString:  " + iData);
    int iNumber = iMyRec.MyInteger;
    Memo1->Lines->Add("iMyRec.MyInteger: " + IntToStr(iNumber));
    DisposeRecord(&iMyRec);
} else {
    Memo1->Lines->Add("Error calling FillRecord");
}

 

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×