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

Time:10-14

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:

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

CodePudding user response:

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;
  • Related