Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to assign a proc reference (passed as parameter) to a record field in Delphi?

When I pass a proc reference as parameter and want to assign it to another proc ref variable (TMyRec.proc), maybe it wants to call the proc and assign the result… the result is a GPF. How can I assign a parameter passed proc ref to another proc ref?

Example: it just plays around the assignment. There is no real tasks assigned as procs.

unit5.pas:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

unit Unit5;

interface

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

type
  TForm5 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

type
  TMyProc = reference to procedure ( i_ : integer );

  PMyRec = ^TMyRec;
  TMyRec = packed record
    i : integer;
    proc : TMyProc;
  end;

procedure TForm5.Button1Click(Sender: TObject);

  procedure createMyRec( i_ : integer; const proc_ : TMyProc );
  var
    pMR : PMyRec;
  begin
    getMem( pMR, sizeOf( TMyRec ) );
    try
      pMR^.i := i_;
      pMR^.proc := proc_; // <--- GPF occures here
    finally
      FreeMem( pMR );
    end;
  end;

begin
  createMyRec( 1, procedure ( i_ : integer ) begin end );
  createMyRec( 2, procedure ( i_ : integer ) begin end );
end;

end.

unit5.dfm:

object Form5: TForm5
  Left = 0
  Top = 0
  Caption = 'Form5'
  ClientHeight = 441
  ClientWidth = 624
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -12
  Font.Name = 'Segoe UI'
  Font.Style = []
  TextHeight = 15
  object Button1: TButton
    Left = 200
    Top = 176
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
end

>Solution :

The compiler shouldn’t be trying to call the procedure, since there is no input parameter being passed to it.

I suspect the real problem is your use of GetMem(), as it doesn’t initialize the record’s fields to zero. So, you are trying to assign to pMR^.proc when it contains an indeterminate value that is likely not nil. And since TMyProc is a reference-counted interface type under the hood, the compiler tries to Release() the "old" interface and crashes.

pMR^.proc needs to be nil before you assign to it. Try using AllocMem() instead, as it will zero out the allocated memory. Or better, use New() instead.

Also, when using either GetMem() or AllocMem(), you need to manually finalize the record before you free the memory, so that ‘proc”s reference count is decremented correctly to release the anonymous procedure. You don’t have to worry about that with New() as Dispose() will finalize the record for you.

Try this:

procedure createMyRec( i_ : integer; const proc_ : TMyProc );
var
  pMR : PMyRec;
begin
  pMR := PMyRec( AllocMem( SizeOf( TMyRec ) ) );
  try
    pMR^.i := i_;
    pMR^.proc := proc_;
  finally
    Finalize( pMR^ );
    FreeMem( pMR );
  end;
end;

Or:

procedure createMyRec( i_ : integer; const proc_ : TMyProc );
var
  pMR : PMyRec;
begin
  New( pMR );
  try
    pMR^.i := i_;
    pMR^.proc := proc_;
  finally
    Dispose( pMR );
  end;
end;
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading