How can I compare the value of a variable that contains a pointer to a function with a function address?
I'm maintaining some code, and it is failing in Delphi 2007. The declaration is:
var
EditorFrameWindow: Function: HWnd Of Object = Nil;
In a form activation, I've got:
procedure TEditForm.FormActivate(Sender: TObject);
begin
EditorFrameWindow := GetFrameWindow;
end;
And in the form deactivation I've got:
procedure TEditForm.FormDeactivate(Sender: TObject);
begin
if EditorFrameWindow = GetFrameWindow then
EditorFrameWindow := nil;
end;
So what is happening is that the form is being deactivated twice, and it is failing as nothing else got activated. The FormDeactivate is called, it matches, and the EditorFrameWindow global is set to (nil,nil) (according to the debugger). Then it is being called again, and the function stored in the variable is called, but of course there isn't one stored so it jumps through nil and creates an exception.
What should I do to stop this happening? (The framework has been changed to a tabbed system, so the operation probably changed.)
Would
procedure TEditForm.FormDeactivate(Sender: TObject);
begin
if Assigned(EditorFrameWindow) and (EditorFrameWindow = GetFrameWindow) then
EditorFrameWindow := nil;
end;
work per chance?
Edit:
You don't compare function addresses, you compare the results of those functions. So even though the fixed code above can no longer cause an exception it may still not do what you want it to. Another function that returns the same result would also reset the event handler.
To really check whether the variable is set to a specific event handler you will need to compare both elements in the TMethod
record. Something like:
procedure TEditForm.FormDeactivate(Sender: TObject);
begin
if (TMethod(EditorFrameWindow).Code = @TForm1.GetFrameWindow)
and (TMethod(EditorFrameWindow).Data = Self)
then
EditorFrameWindow := nil;
end;
There are two ways you might want to compare method pointers. Method pointers consist of two pointers, a code pointer and an object pointer. Delphi's native way of comparing method pointers compares only the code pointers, and it looks like this:
if @EditorWindowMethod = @TEditForm.GetFrameWindow then
EditorWindowMethod := nil;
It checks whether the code pointer in the EditorWindowMethod
variable matches the starting address of the GetFrameWindow
method in TEditForm
. It does not check whether the object reference in EditorWindowMethod
is the same as Self
. If you want to make the the object references are the same, too, then you need to break apart the method pointer into its constituent parts with the TMethod
record, which Mghie's answer demonstrates. (And you probably do want to compare the object references since it sounds like you have multiple edit forms. They all have the same GetFrameWindow
code pointer, but they have different object references.)
The reason for the @
in the code is to tell the compiler that you want to refer to the method pointers. Without it, the compiler will try to call the method pointers, and that's what was getting you into trouble. The first time the window was deactivated, you called EditorWindowMethod
and compared the resulting window handle with the return value from calling GetFrameWindow
. They matched, of course, so you unassigned EditorWindowMethod
. The next time the form was deactivated, you tried to call EditorWindowMethod
again, but it was a null pointer.
You should consider getting rid of your dependence on activation and deactivation notification. Instead, simply check whether the form is active inside GetFrameWindow
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With