Link to home
Start Free TrialLog in
Avatar of Pummel
Pummel

asked on

TCollection crashes intermittently (don't you hate that word?)

I have a certain window created at run-time.  In this window is a table that serves as a master for many other tables on the form.  All tables on this form have, as their DatabaseName property, the name of a database component that's in a database module which belongs to the project.  To me, that seems like a normal thing to do.

*Sometimes* when I close the window, the form crashes.  I have delved deep into the Delphi code several times to try and find the bug.  It turns out that a TCollection, of type TFieldDefs, is crashing when it frees one of its FItems, a TFieldDef.  I can, by reason, find out which of the TFields in the TDataSet crashed, but I can't find out WHY!  Oh, and sometimes it's a different table and a different field...

The program continues to run semi-normally.  Closing the program produces more errors, but starting the program again gets the user back on his/her feet.

The good news is that, luckily, none of the data in the tables on the form is ever lost.  However, no more windows of that type can be created at run-time.  An error appears saying that a component by the same name cannot be created.  I've tried circumventing this side-effect to no avail.  I want to nail the problem at its heart anyway, not treat the symptoms.

Since I do not explicitly free any objects that belong to the form, I don't think I'm at fault.  In fact, my call stack confirms this plea of Not Guilty: every call in the stack is a Delphi-packaged source file.  Objects are properly freeing their ancestors before themselves, and they free their children components.  Nothing seems wrong.  Something must be freeing a lonely TFieldDef on accident.  Maybe?

Also, at design-time I have no TFieldDefs.  They must be being created at run-time by the datasets.

What can I do to stop my form from crashing?
Avatar of kretzschmar
kretzschmar
Flag of Germany image

if you mean with close a window,
did you mean you free a form?

if so, use the release method for this job or
set the action-parameter in the onclose-event of the form to cafree

just guessing

meikl ;-)
Avatar of Lee_Nover
Lee_Nover

I've had similar problems regarding dbs

try to set the bound controls DataSource to nil and the DataSources DataSet to nil
that helped me, hope it helps you also :)
Avatar of Pummel

ASKER

kretzschmar,
Well, I don't release the form manually, either.  When I create it, I hand the Application object in as its owner.  The user closes it using the Windows X button on the title bar.  I don't call that function.

I do, however, handle the OnClose and OnDestroy events, freeing up stuff I created with no parent, and asking the user if they want to make any changes and so forth.

Lee_Nover
If I set those things to nil, then I won't have any data-bound controls!  The user wouldn't be able to see or add things to the database.  How did that kind of solution work for you?
Hi

I want to ask what database is used (I mean Paradox, Oracle or something else). And try to test following aspects:
- creation order of your forms and datamodule (you have to make your datamodule to be created before any form)
- test field names - probably you use a reserved word
- try to close Datasets explicity (for example in OnClose event handler)

Hope this will help
>> *Sometimes* when I close the window, the form crashes <<

set those datasources and datasets to nil when you close the form :)
eg. on the OnClose or OnDestroy event
It sounds like it is trying to free components that have already been freed.  Since freeing a component does not automatically overwrite its memory, sometimes freeing it again will work.  However, since Windows or the VCL may snag that freed memory for their own uses between the free operations, the operation will fail randomly.

Freeing an object does not remove its references from collections or overwrite the value.  Since an object reference is implemented as a pointer, the main error detection/prevention tool is if Assigned (x).  The Assigned function simply tests to see if the pointer is NIL ... it does not confirm that the memory referenced by the pointer is in fact properly and currently allocated to the current process.

If the TFieldDefs are owned by the form and contained by the TDataset, then it is likely that both the form and the dataset are trying to free them.  Make sure that the owner of the TFieldDefs is the dataset.

Also, I have found event loops are sometimes culprits.  Check your call stack and see if you are in an event loop.  This usually happens when you try to do something that the VCL does for you already.
Lee: Setting the object references to NIL leaves you at risk of not freeing them correctly.  It may mask the bug, but it will not necessarily resolve it.  I've inherited a project that was built on the philosophy of hiding the symptoms, and I am still finding coding errors two years later.  :o)
ASKER CERTIFIED SOLUTION
Avatar of kretzschmar
kretzschmar
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Pummel

ASKER

bes67,
Here are the concerns you addressed, in your original order:
1. The database I'm using is an AS/400.  I connect through an alias listed in the BDE Administrator.
2. I have verified that the creation order is how it should be.  The DataModule is created first.  The form in question is only created at run-time; there is no var variable in the form's unit.
2a. On another note, here is how I create my non-visual components creation order:
Master table
Master datasource
Detail table
Detail datasource
(the last two repeat for all dependent datasources)
In this way, details will be destroyed first.
3. In my department, we have a habit of naming fields in a very weird way.  Every field name starts with 'DP', and to my knowledge, no reserved words start this way.
4. Before I even posted this question, I had tried closing the datasets explicitly in both the OnClose, OnDestroy, and in an overridden Destroy method.  None of these ideas have worked unfortunately.

swift99,
As I mentioned earlier, I do not explicitly free anything.  I let the form do it all.  Testing for nil and who the owners are seems unproductive if the same process is going to try and free that memory again.

But, as I had not officially tried this approach, I decided to give it a shot.  The form crashed when I set FieldDefs to nil, right after calling the Free method.  So, I took out the nil, and left the Free.  The form still crashed.

kretzschmar,
I think we're both right on your first point.  I still think that more than one object is freeing a TFieldDef, but you're also right about the form not entirely closing when an exception occurs.  Since the window is not entirely done away with when an exception occurs, it makes sense that another window can't be created AND it makes sense why the application crashes on exiting, since it's still trying to get rid of the problem form.

While I was writing this reply, I also fiddled with the StoreDefs property.  The only thing that changed was which TFieldDef was crashing.  But then again, that always changes.

Any other ideas?
Avatar of Pummel

ASKER

Should I use a TSession component, or make one at run-time?  Currently, I just use the default, implicit Session object.  All of the tables on the form have, as their DatabaseName property, the name of a database on the main TDataModule of the program.

Since there can be more than one of these forms open at run-time, would it be wise to instantiate a sessions component?  And how?
Avatar of Pummel

ASKER

This error, which I have had for months, was fixed by systematically removing components and code from the form until I could not longer get it to crash.  This took a while since sometimes, 50 closes were required before the crash would occur.

The culprit was a TShellListView component on one of the tabs of the form.  It must be using up resources that the TTable had legally acquired.  After restoring my form to its original state (by using a backup) and removing just that component, my program has proceeded to run without crashing when closing the form.  I've closed over 100 forms and no crashes.
Good detective work.  I guess you get your points back   :o)
wow that must've been a real tough bug to find huh
yeah .. painfull debugging :)
just to say the same, as the others above ;-)

glad you get it sort out
Avatar of Pummel

ASKER

swift99,
I get my points back?  I've deleted other questions before and I continue to see them in my Asked Questions list with the status "PendingDelete".  I haven't paid enough attention to my notes, though.

DragonSlayer, Lee_Nover, and kretzschmar,
It took me three to four days to find.  Every time I'd remove a component, of course, I had to remove any code that referenced it.  Since I thought this was a database issue, I deleted little pieces at a time.  I even deleted tables from my database module, figuring that might be the problem.

After I deleted everything related to the database, including data-aware components (but not the four main master-detail tables), I started to delete other components unrelated to the database.  That's how I found it.  The TShellListView was one of those first non-database components I deleted.

As I debugged, sometimes I'd only have to open the form once or twice and it would crash as usual.  Other times I remember it crashing after more than 50 successive opens and closes.  After I had removed the TShellListView component, I opened and closed more than 100 windows without a crash.  I could only assume the problem was fixed.  Then I restored my old form and took out the TShellListView.  I haven't had any problems since!
Avatar of Pummel

ASKER

I want to free myself of these points and your answer was the closest to the solution.