5
votes

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
I suspect that you want to pass both data and code to the DLL. The correct way to do this is with an interface. Your accepted answer does not pass code, only data. - David Heffernan
David: Both the DLL and EXE share a common unit containing the Record types.... in the test-case done by Dorin, the Record passed from the host executable to the DLL allows the DLL to execute the "DoSomething" method, so his solution is perfectly viable for my needs. Ordinarily I would agree that Interfaces were the way to go, but since this relates specifically to the "Smart Types" I'm working on, I need to be able to pass the record itself between host and DLL (now I can). Thanks :) - LaKraven
Just so that you know that the method executed is the one in the DLL, even when the record is created in the EXE. - David Heffernan
Yup... that's good to know, but in this instance that's the desired behaviour :) - LaKraven

2 Answers

4
votes

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.


7
votes

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;