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.

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;

Open in new window


Thanks in advance,
Michael
LVL 4
MichaelStaszewskiAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

MichaelStaszewskiAuthor Commented:
I forgot to add that the problems go away if nil is used as TForm2's owner (as expected) and that Frm.release and freeandnil(frm) have no effect.

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.
Geert GOracle dbaCommented:
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
MichaelStaszewskiAuthor Commented:
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.
CompTIA Network+

Prepare for the CompTIA Network+ exam by learning how to troubleshoot, configure, and manage both wired and wireless networks.

MichaelStaszewskiAuthor Commented:
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.
Geert GOracle dbaCommented:
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)
MichaelStaszewskiAuthor Commented:
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.
Geert GOracle dbaCommented:
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;
MichaelStaszewskiAuthor Commented:
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
Geert GOracle dbaCommented:
it's a bit of a bad sample in your header question
> myclass is not instantiated

we use devart components ODAC too (former corelab) and don't have this issue
we aren't using the XE2 yet.
> we have had issues with ODAC but they got resolved within 1 or 2 weeks.

usually when it comes to hanging, i have to investigate the order of freeing objects
and especially the order of freeing objects in the finalization of units.

maybe you should open a case with Embarcadero or DevArt ?

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Geert GOracle dbaCommented:
ugh ... sometimes i do get a bug in Toad ... :)
MichaelStaszewskiAuthor Commented:
:-)

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
MichaelStaszewskiAuthor Commented:
I mean... Delphi *2009*

And the sample is terrible. It's poor code for sure... sometimes unexpected conditions allow such scenarios to exist. :-(
Geert GOracle dbaCommented:
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;
MichaelStaszewskiAuthor Commented:
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;

Open in new window

MichaelStaszewskiAuthor Commented:
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.
Geert GOracle dbaCommented:
you may also want to look at FreeNotification
Components wanting to be notified of other components being freed use FreeNotification
and RemoveFreeNotification
MichaelStaszewskiAuthor Commented:
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.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.