MichaelStaszewski
asked on
Invalid Pointer Operation - TApplication.Components holds bad reference
[EDIT] I am using Delphi XE2 with update 3. We cannot upgrade to 4 at this time.
Hey there, hopefully there is a simple solution here because this problem is rather urgent. I will likely not be able to test solutions until morning though.
This closely mirrors our code. It uses a session component and a query component in the ODAC suite by Corelab. I am not sure that those exact components matter. I think I could get the same results with any TComponent. Following this access violation, shutting down the application generates Invalid Pointer Operation and the application hangs. It will not close. Application.Components still maintains a reference to the created TForm2 even though the free is being called.
Thanks in advance,
Michael
Hey there, hopefully there is a simple solution here because this problem is rather urgent. I will likely not be able to test solutions until morning though.
This closely mirrors our code. It uses a session component and a query component in the ODAC suite by Corelab. I am not sure that those exact components matter. I think I could get the same results with any TComponent. Following this access violation, shutting down the application generates Invalid Pointer Operation and the application hangs. It will not close. Application.Components still maintains a reference to the created TForm2 even though the free is being called.
uses
Unit2, Ora;
type
TMyClass = class
public
Sess: TOraSession;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Frm: TForm2;
MyClass: TMyClass;
begin
Frm := TForm2.Create(Application);
try
Frm.Qry.Session := MyClass.Sess; // AV generated here
finally
Frm.Free;
end;
end;
Thanks in advance,
Michael
you aren't showing MyClass := TMyClass.Create; line
the AV you get here is correct as MyClass has not been instantiated and thus
if Assigned(MyClass) is false
if Assigned(MyClass) then
frm.qry.Session := MyClass.Session;
maybe MyClass is owned by a other form which gets freed just before Form2 gets freed
the AV you get here is correct as MyClass has not been instantiated and thus
if Assigned(MyClass) is false
if Assigned(MyClass) then
frm.qry.Session := MyClass.Session;
maybe MyClass is owned by a other form which gets freed just before Form2 gets freed
ASKER
I apologize, but I should have been clearer. I am not creating TMyClass intentionally to produce the AV. We have an AV condition in our code that we can solve, but it illustrates a scenario I thought was impossible. In my code, above, frm.free is being called. It is my understanding that this will remove the object from the components list of its owner. In this case, Application.Components retains a reference to the freed form and invalid pointer operation is raised at shutdown. The exception is only seen when debugging in the IDE. The application is still running with no taskbar button and pegs the CPU. Why is this? Why does Application retain the reference?
I'll try to find a reproducible scenario using native components in the morning. Thanks.
I'll try to find a reproducible scenario using native components in the morning. Thanks.
ASKER
I should also add that i have 2 forms. TForm2 contains a query component and no code. TForm1 contains a TButton and only the code shown above in the implementation section.
it should come down to where you create the session and where it is freed
and after it is freed, where it is still referenced
always use freeandnil to free a component (even a form)
and after it is freed, where it is still referenced
always use freeandnil to free a component (even a form)
ASKER
Freeandnil has no effect here. The raised exception is somehow preventing the call to free from notifying its owner that it is no longer instantiated. The owner still thinks it owns this freed object.
the form will only free objects which it owns
you probably have this
unit Form1:
var MyClass: TClass;
MyClass := TClass.Create(Form1);
Form2
Close > MyClass.Free;
why not add logging to the destructor of TMyClass ?
destructor TMyClass.Destroy;
begin
//>> Breakpoint and Call stack Ctrl-F3 to find where freed
inherited Destroy;
end;
you probably have this
unit Form1:
var MyClass: TClass;
MyClass := TClass.Create(Form1);
Form2
Close > MyClass.Free;
why not add logging to the destructor of TMyClass ?
destructor TMyClass.Destroy;
begin
//>> Breakpoint and Call stack Ctrl-F3 to find where freed
inherited Destroy;
end;
ASKER
No. I have only as described and what you see in my sample code. My sample project, used purely for illustrative purposes has two forms. TForm1 (AKA Application.mainform) has a single TButton with assigned OnClick and a simple class. All code for the class and OnClick are shown above. There is nothing more in TForm1. Another form, TForm2, contains a single component. It is a TSmartQuery from a 3rd party library we use. There is no code at all in TForm2. This is the *entire* code in my sample app created to isolate the behavior I'm seeing.
The AV is trivial. We have isolated and fixed it in our production app. What is unclear to me is why TApplication continues to think that it owns an instance of TForm2 (in my sample app) when it has clearly been freed. It is the first time in my 10 years of Delphi experience that I've seen this and the same goes for my colleagues who each have more Delphi experience than myself. Ideally what we'd like to do is prevent this scenario from occuring for cases where we may not catch exceptions before we release our product. Our product is large, 3.2 million lines of code, and although it is tested thouroughly before release some bugs do escape detection and in those cases it would be nice to avoid this hanging scenario. If it's just the way it is then so be it, but I've not knowingly seen this before.
I hope the problem is more clearly described now.
Thanks,
Michael
The AV is trivial. We have isolated and fixed it in our production app. What is unclear to me is why TApplication continues to think that it owns an instance of TForm2 (in my sample app) when it has clearly been freed. It is the first time in my 10 years of Delphi experience that I've seen this and the same goes for my colleagues who each have more Delphi experience than myself. Ideally what we'd like to do is prevent this scenario from occuring for cases where we may not catch exceptions before we release our product. Our product is large, 3.2 million lines of code, and although it is tested thouroughly before release some bugs do escape detection and in those cases it would be nice to avoid this hanging scenario. If it's just the way it is then so be it, but I've not knowingly seen this before.
I hope the problem is more clearly described now.
Thanks,
Michael
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ugh ... sometimes i do get a bug in Toad ... :)
ASKER
:-)
That's what we try to avoid. I'll try a case w/ devart and post back. We are on the devart version, latest version, but i see this in an earlier one too in Delphi 9.
Thanks,
Michael
That's what we try to avoid. I'll try a case w/ devart and post back. We are on the devart version, latest version, but i see this in an earlier one too in Delphi 9.
Thanks,
Michael
ASKER
I mean... Delphi *2009*
And the sample is terrible. It's poor code for sure... sometimes unexpected conditions allow such scenarios to exist. :-(
And the sample is terrible. It's poor code for sure... sometimes unexpected conditions allow such scenarios to exist. :-(
this is just guessing, but could it be a timing issue ?
sample:
set a timer on form1 with a event handler from form2
(only possible in code)
after freeing form1 sometimes the event handler still gets called
procedure TForm1.OnCreate;
begin
Timer1.OnTimer := Form2OnTimer;
end;
>> Free(Form1);
procedure TForm2.OnTimer;
begin
Form1.Caption := 'Timer called'; >> Av here
end;
sample:
set a timer on form1 with a event handler from form2
(only possible in code)
after freeing form1 sometimes the event handler still gets called
procedure TForm1.OnCreate;
begin
Timer1.OnTimer := Form2OnTimer;
end;
>> Free(Form1);
procedure TForm2.OnTimer;
begin
Form1.Caption := 'Timer called'; >> Av here
end;
ASKER
I have seen TTimer issues many times in the past doing exactly what you suggest. My solution for that has always been to disable all timers in TForm.OnDestroy handler. I'm not sure that the same issue applies here. Nevertheless I thought perhaps you were on to something so I wrapped my code in a try..except just to see if the open exception dialog in the application while the form had been freed was the cause. No joy.
procedure TForm1.Button1Click(Sender: TObject);
var
Frm: TForm2;
MyClass: TMyClass;
begin
Frm := TForm2.Create(Application);
try
try
Frm.Qry.Session := MyClass.Sess;
except
end;
finally
Frm.Free;
end;
end;
ASKER
Thanks Geert_Gruwez for listening and providing your comments. After some sleep and coffee the problem is more clear. An ODAC control is raising an exception in its destructor due to the buggy code and it's bypassing necessary code in TComponent.Destroy. Ughh. The solution... don't create buggy code. :-) Everything is functioning as designed.
A correction. I saw massive exceptions in Delphi 2009, but it does not hang in that version afterall. I was mistaken. Eventually the application closes. This looks like an XE2 specific thing. I don't have XE to test with. It's a slow day, I'll try to get to the bottom of the hang and send to Embarcadero to see if there's a bug there. Crappy code aside it would be nice if the application could recover and close without CPU pegging.
A correction. I saw massive exceptions in Delphi 2009, but it does not hang in that version afterall. I was mistaken. Eventually the application closes. This looks like an XE2 specific thing. I don't have XE to test with. It's a slow day, I'll try to get to the bottom of the hang and send to Embarcadero to see if there's a bug there. Crappy code aside it would be nice if the application could recover and close without CPU pegging.
you may also want to look at FreeNotification
Components wanting to be notified of other components being freed use FreeNotification
and RemoveFreeNotification
Components wanting to be notified of other components being freed use FreeNotification
and RemoveFreeNotification
ASKER
That was our solution to this particular problem. I was more or less looking to see if anyone had a possible solution to avoid application hang in the event of such an AV.
ASKER
Also, the code that is failing is when TApplication destroy's its components. Going to test Delphi 2009 now to see if this issue is a new addition to Delphi.
Delphi 2009 also fails. Any insight on how to avoid this would be fantastic. Ideally we'll not code in exceptions :-) but we have a ton of code and unfortunately things slip through. It would be nice to have EurekaLog grab the unhandled ones and still let the application shutdown without hanging.