TMethod(EventPtr).Code := @buttonClick; // Assign the procedure's code pointer
TMethod(EventPtr).Data := nil;
Btn.OnClick := EventPtr;
So you probably noticed that
Btn.OnClick := @buttonClick;
does not work?
"buttonClick" is a nested procedure. That means it has a secretly different calling "signature". (It takes different parameters that those listed in the declaration).
"buttonClick" expects procedure TSomeObject.ButtonClick(Sender : TObject);
Which is different from
procedure ButtonClick(Sender : TObject); // plain procedure, not a method in a class
The method actually gets called with a hidden "self" param.
procedure TSomeObject.ButtonClick((* Self: TSomeObject; *) Sender : TObject);
The plain procedure does not get this. So the argument "Sender" is not the first argument if it is a method. If TButton calls it via OnClick, then it passes "self".
This is why the type has
TNotifyEvent = procedure (Sender : TObject) of object; // "of object = needs a hidden self"
And NO,NO,NO => you cannot just declare "self" as a normal parameter. Because those events are stored in a "TMethod". But a variable for normal plain procedures generates a different variable, that can never store a TMethod.
(Well, if you use the assign to TMethod.Code, then you can declare self. But depending on target, you may have to change some details, so you may get issues when compiling for different targets).
Anyway, your ButtonClick is a nested procedureAnd that is different again, from both of the above.
You noticed you can't access the local variables of "SaveFileAs".
That is because nested procedures in reality look like this:
procedure ButtonClick((* parentfp: pointer; *) Sender : TObject); // maybe diff order, parentfp may be last, not sure
Where "parentfp" is set to point to the memory containing the local variables of the outer proc.
If you would call ButtonClick directly from SaveAsFile, then that parentfp would get the correct pointer.
But when TButton calls it (via your hack) then that pointer isn't there. And TButton would not have the correct value to set it for the call. (Besides that there is no way to declare that in a way that it would be possible to set it like that).
In short.
TNotifyEvent (TButton.OnClick) must be a method of a class. It can't be a plain procedure, and it can't be a nested procedure.