Solved

Invalid Pointer Operation - TApplication.Components holds bad reference

Posted on 2012-03-29
17
668 Views
Last Modified: 2012-03-30
[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
0
Comment
Question by:MichaelStaszewski
  • 10
  • 7
17 Comments
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37784640
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.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 37785079
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
0
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37785184
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.
0
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37785191
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.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 37785199
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)
0
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37785205
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.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 37785310
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;
0
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37785746
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
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 36

Accepted Solution

by:
Geert Gruwez earned 500 total points
ID: 37786288
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 ?
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 37786297
ugh ... sometimes i do get a bug in Toad ... :)
0
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37786621
:-)

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
0
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37786653
I mean... Delphi *2009*

And the sample is terrible. It's poor code for sure... sometimes unexpected conditions allow such scenarios to exist. :-(
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 37786688
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;
0
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37787002
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

0
 
LVL 4

Author Closing Comment

by:MichaelStaszewski
ID: 37787704
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.
0
 
LVL 36

Expert Comment

by:Geert Gruwez
ID: 37788173
you may also want to look at FreeNotification
Components wanting to be notified of other components being freed use FreeNotification
and RemoveFreeNotification
0
 
LVL 4

Author Comment

by:MichaelStaszewski
ID: 37788638
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.
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now