The following code is based on this article: http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html.
When the event handler code inside the anonymous procedure is fired (upon changing a row in the grid), the first 'if' reports that dbgrid is not nil but the second one reports it is nil.
Any idea about what's going on here? You can get the full source code from here (you might have to change the FileName property of the TClientDataSet to point to the directory where you unzipped the project).
unit Main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Vcl.DBGrids, Datasnap.DBClient, Vcl.Grids;
type
AfterScrollEventHandler = reference to procedure (sender: TDataSet);
TForm3 = class(TForm)
dbgrdGrid: TDBGrid;
cdsDataSet: TClientDataSet;
dsDataSet: TDataSource;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
procedure MethodReferenceToMethodPtr(const MethRef; var MethPtr);
procedure InjectEventHandler(dataSet: TDataSet; dbGrid: TDBGrid);
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure InjectEventHandler(dataSet: TDataSet; dbGrid: TDBGrid);
var
eventHandlerRef : AfterScrollEventHandler;
eventHandlerPtr : TDataSetNotifyEvent;
begin
eventHandlerRef := procedure (sender: TDataSet)
begin
if dbGrid <> nil then
MessageDlg('1: AfterScroll: The grid is not nil!', mtInformation, [mbOK], 0)
else
MessageDlg('1: AfterScroll: The grid is nil! It''s going to crash...', mtInformation, [mbOK], 0);
if dbGrid <> nil then
MessageDlg('2: AfterScroll: The grid is not nil!', mtInformation, [mbOK], 0)
else
MessageDlg('2: AfterScroll: The grid is nil! It''s going to crash...', mtInformation, [mbOK], 0);
end;
MethodReferenceToMethodPtr (eventHandlerRef, eventHandlerPtr);
dataSet.AfterScroll := eventHandlerPtr;
end;
procedure MethodReferenceToMethodPtr(const MethRef; var MethPtr);
type
TVtable = array[0..3] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
// 3 is offset of Invoke, after QI, AddRef, Release
TMethod(MethPtr).Code := PPVtable(MethRef)^^[3];
TMethod(MethPtr).Data := Pointer(MethRef);
end;
procedure TForm3.FormCreate(Sender: TObject);
var
eventHandlerRef1, eventHandlerRef2 : AfterScrollEventHandler;
begin
InjectEventHandler(cdsDataSet, dbgrdGrid)
end;
end.
Update:
This code works as expected (the anonymous procedure stores the dbGrid parameter in a local variable):
procedure InjectEventHandler(dataSet: TDataSet; dbGrid: TDBGrid);
var
eventHandlerRef : AfterScrollEventHandler;
eventHandlerPtr : TDataSetNotifyEvent;
begin
eventHandlerRef := procedure (sender: TDataSet)
var grid: TDBGrid;
begin
grid := dbGrid;
if grid <> nil then
MessageDlg('1: AfterScroll: The grid is not nil!', mtInformation, [mbOK], 0)
else
MessageDlg('1: AfterScroll: The grid is nil! It''s going to crash...', mtInformation, [mbOK], 0);
if grid <> nil then
MessageDlg('2: AfterScroll: The grid is not nil!', mtInformation, [mbOK], 0)
else
MessageDlg('2: AfterScroll: The grid is nil! It''s going to crash...', mtInformation, [mbOK], 0);
end;
MethodReferenceToMethodPtr (eventHandlerRef, eventHandlerPtr);
dataSet.AfterScroll := eventHandlerPtr;
end;