Passing a method-containing record between host application and DLL

1.5k Views Asked by At

Is it possible (without the use of Runtime Packages or the Shared Memory DLL) to pass a Record type between the host application and a DLL module where the Record type contains Functions/Procedures (Delphi 2006 and above)?

Let's presume for the sake of simplicty that our Record type doesn't contain any String fields (as this of course requires the Sharemem DLL), and here's an example:

TMyRecord = record
  Field1: Integer;
  Field2: Double;
  function DoSomething(AValue1: Integer; AValue2: Double): Boolean;
end;

So, to state this simply: can I pass an "instance" of TMyRecord between the host application and a DLL (in either direction), without the use of Runtime Packages or Shared Memory DLL, and execute the DoSomething function from both the Host EXE and the DLL?

2

There are 2 best solutions below

6
On BEST ANSWER

If I understood your question correctly, then you can do it, here's one way to do it:

testdll.dll

library TestDll;

uses
  SysUtils,
  Classes,
  uCommon in 'uCommon.pas';

{$R *.res}

procedure TakeMyFancyRecord(AMyFancyRecord: PMyFancyRecord); stdcall;
begin
  AMyFancyRecord^.DoSomething;
end;

exports
  TakeMyFancyRecord name 'TakeMyFancyRecord';

begin
end.

uCommon.pas <- used by both application and dll, unit where your fancy record is defined

unit uCommon;

interface

type
  PMyFancyRecord = ^TMyFancyRecord;
  TMyFancyRecord = record
    Field1: Integer;
    Field2: Double;
    procedure DoSomething;
  end;

implementation

uses
  Dialogs;

{ TMyFancyRecord }

procedure TMyFancyRecord.DoSomething;
begin
  ShowMessageFmt( 'Field1: %d'#$D#$A'Field2: %f', [ Field1, Field2 ] );
end;

end.

and finally a test application, file -> new -> vcl forms application, drop a button on the form, include uCommon.pas in the uses clause, add reference to external method

procedure TakeMyFancyRecord(AMyFancyRecord: PMyFancyRecord); stdcall;
  external 'testdll.dll' name 'TakeMyFancyRecord';

and in the button's on click event, add

procedure TForm1.Button1Click(Sender: TObject);
var
  LMyFancyRecord: TMyFancyRecord;
begin
  LMyFancyRecord.Field1 := 2012;
  LMyFancyRecord.Field2 := Pi;
  TakeMyFancyRecord( @LMyFancyRecord );
end;

DISCLAIMER:

  • works in D2010;
  • compiles on my machine!

enjoy!


David Heffernan' edit

Just to be 100% clear, the DoSomething method that is executed is the method defined in the DLL. The DoSomething method defined in the EXE is never executed in this code.


10
On

I would not suggest that, whether it works or not. If you need the DLL to operate on TMyRecord instances, the safest option is to have the DLL export plain functions instead, eg:

DLL:

type
  TMyRecord = record 
    Field1: Integer; 
    Field2: Double; 
  end; 

function DoSomething(var ARec: TMyRecord; AValue1: Integer; AValue2: Double): Boolean; stdcall;
begin
  ...
end;

exports
  DoSomething;

end.

App:

type 
  TMyRecord = record  
    Field1: Integer;  
    Field2: Double;  
  end;  

function DoSomething(var ARec: TMyRecord; AValue1: Integer; AValue2: Double): Boolean; stdcall; external 'My.dll';

procedure DoSomethingInDll;
var
  Rec: TMyRecord;
  //...
begin 
  //...
  if DoSomething(Rec, 123, 123.45) then
  begin
    //...
  end else
  begin
    //...
  end;
  //...
end;