Link to home
Start Free TrialLog in
Avatar of galcott1
galcott1

asked on

Delphi app sometimes not closing properly

I am the developer of a widely distributed application written in Delphi 5. Recently there have been some reports from users that when closing the app, it remains in the taskbar and can only be terminated using Task Manager. I have occasionally seen this myself and have also seen an instance where it wasn't in the taskbar but the process was still running, but I haven't found any specific action that causes it. This has only been happening for the last few months so I assume it has something to do with a recent code change, but I don't know exactly what causes this and have no idea how to search for the possible cause. I would appreciate any hints on what causes this and how to find the part of the code that needs fixing.
Avatar of 8080_Diver
8080_Diver
Flag of United States of America image

Does your application involve multiple threads?
How are you closing the application in the code? (I've seen this in D5 when the there are objects not shut down when an attempt is made to close the application.
Step through your recent code changes, looking to make sure that you have freed/relesed everything that gets created.  (Speaking of which, if you are creating forms on the fly, then be sure that you use the FormName.Release; method and not the FormName.Free method . . . there are some subtle difference that can bite you in the, uh, app. ;-)
Are you considering upgrading to Delphi 2009?  If not, you probably should.  (And, no, I have no financial interest in CodeGear . . . but D5 is ancient in computer years.. ;-)
Avatar of galcott1
galcott1

ASKER

The app doesn't involve multiple threads.

I'm not sure what you mean when you ask how I'm closing it. It's an MDI app so when the user clicks the Close icon on the main form it loops through all the child windows and closes them. Very straightforward.

I do create forms on the fly but I don't use either of the methods you mention to free them. I use the line "Action=caFree" in the FormClose event.

No, I'm not considering upgrading. This is a huge old app and D5 has everything I need for it. Anyway, I never moved past D7 which is what I use for all my current development.

Avatar of Geert G
try adding Eurekalog, or Madshi to find the error, you'll get a bug report with a stack trace (Ctrl-F3).
You can see where the app gives the AV.
You don't need to alter the code, just install one of those components,
enable it from the project menu and recompile.

Madshi: www.madshi.net
Euraklog: www.eurekalog.com

or check the finalization code of each unit.
maybe you are using a instance of an object which got freed just before using it ...
Do you use Application.Terminate in your Main FOrm when the user initiates the OnClose event of the main form?  If the Main Form spawns the other forms, then the Application erminate should send Close event messages to the other forms.  
I generally use Form.Release so that any remnants of activities that the form may be engaged in can finish up.  (Release is a deferred Free.)  I also set the Form to Nil right after that . . . although, I think I got in that habit so that I could test for the form being assigned before creating it.  (Habits get to be strong and the reasons for them forgotten. ;-)
I was looking it up in my "Mastering Delphi 5" reference and the csFree is usually set in the FormCloseQuery (which fires before the FormClose) to indicate that the form can close.  If there is something that is set to indicate that the form can't close at that moment (e.g. a flag for some activity that is in progress), then you would set something like caNone or the CanClose parameter to False.  This is the only way I have ever used the caFree . . . i.e. from the OnCloseQuery and not the OnCLose event handler.
Just thought of something and, I know, it is probably something that you have already checked but it is the sort of question that has to be asked as part of the Check Off list. ;-)
Am I safe in assuming that you have gone through the code for each child-form to make sure that there are no OnCloseQuery event handlers that may be preventing the form from closing and that all of the OnClose event handlers do, indeed, use the Form.caFree?
Geert-
I do already use Eureka Log. There is nothing in the log file when this happens, since it doesn't actually generate an error. There is no finalization code in the entire app.

Diver-
I don't use Application.Terminate on the close event of the main form. This shouldn't be necessary because according to the docs it is called automatically when the main form closes.
Okay, so, have you checked the OnCose events?
Also, as a passing observation, If everything that was supposed to be working automatically was indeed working automatically, would this be happening? ;-)
Te trick is going to be determine which form is not closing.  I say this because, if there is something in the task bar, that sure sounds like a form is miimizing instead of closing.  The next place I would look would be the mechanism you use when the user clicks the Close icon on the main form it loops through all the child windows and closes them.
Are you maintaining a list of the forms?  If so, is it possible that you are somehow either missing on or not noting that one has been opened twice and only closing it once?
I ihaven't checked each individual form's OnClose events yet but I will do that later.

The code to loop through all the forms hasn't ever changed. It's very simple:
  for I := FormMain.MDIChildCount-1 downto 0 do
    if Assigned(FormMain.MDIChildren[I]) then
      FormMain.MDIChildren[I].Close;

Is there some other possible reason this would happen, aside from a form not closing?
more like try this:
some forms may close other forms:

var aform: TForm;
while FormMain.MDIChildCount-1 > 0 do
begin
  aForm := FormMain.MDIChildren[FormMain.MDIChildCount-1];
  FreeAndNil(aForm);
end;
You can always use halt(); to terminate the application, but be aware that this will not free anything, if you have an open DB for example it will stay open and the private reserved memory will not be freed.

Another thing to try if you are just closing the form to exit, is to invoke application.terminate instead which is safe.
When you create the MDI forms, do you assign the Application as the Owner?  That might be another thing to check in addition to checking the OnClose events.
Exxentially, as I understand it (and I haven't delved nto doing MDI's that much), the only way that the icon is still in the task bar is that something (more specifically, some form) has not shut down completely.  In effect, there is an orphaned form out there somewhere.  (hmmm, if you click on the ico to restore the form, does anything happen?)
@gpanta,
Using Halt() to stop the app is very much akin to using a big tree to stop your car instead of using your brakes. ;-)
one thing i sometimes came across when closing was a form was freed but not nil with using "Action=caFree"

i first started all forms mdi like this:

procedure TfrmTest.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Action := caFree;
end;

then with this code i tested the form assignment and i got a message !!!
if Assigned(frmTest) then
  ShowMessage('frmTest is still active');

so i changed all my close procedure to this:

procedure TfrmTest.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  Action := caFree;
  frmTest := nil;
end;

that solved it for me.
Geert,
Sorry about not followig this one more closely . . . some stuff was happening at work (like servers crashing ;-) tand I got wrapped up in work. ;-)
By definition, I would expect that just "freeing" the any object that you create will not case it to be set to Nil unlis you use FreeAndNil(object);
Freeing the object relesases the memory allocated to the object.  Setting the Object name to Nil changes the pointer value to Nil.  Behind the scenes, Delphi uses pointers to handle objects and a lot of other stuff.  (Which is why I get so frustrated with former C and C++ coders who force the use of pointers in Delphi which means they are really working with pointers to pointers. ;-)  
That is why I code my non-MDI forms so that there is a totally public function (returning TModalResult) at the top of the unit and I have that function receive any parameters, create the form, use the parameters to set up things in the form, show the form, and then, when the user clicks a button that closes the form, that function retrieves any pertinent values that may need to be returned to the calling unit and does a FreeAndNil of the form.  From then on, I don't have to worry about making sure that the form is freed and set to Nil whenever it is called because I have set it up in one place.
Now, as I have mentioned, I don't have a lot of experience with MDI forms, so I don't know if this approach would work with them.  However, if I were working with them, I might give it a try . . . because it might work and it makes the code much cleaner.
>>8080_Diver
actually i do the same thing

the one thing i don't really like is that you can't put the form declaration in the implementation section
to hide that too ... (without having to create the components manually on the form)
sometimes i even want to hide that too

for this problem
i came across this in several apps, and in most cases (companies) it was when using several units together
where unit A was using functions/vars/objects from unit B.
in the finalization of unit A, a var was called from unit B, but this unit B was allready finalized and all vars were nil.
so that caused the AV.

when i see this problem arise, this is in 95% of the cases the problem
the vars can also be form variables

like having a form to write all logs too, if you would use this form in the finalization you would get this problem too.
""@gpanta,

Using Halt() to stop the app is very much akin to using a big tree to stop your car instead of using your brakes. ;-)""

@8080_Diver One of the best descriptions of halt I have ever read :-P
.
we had this in one of our apps.  the last thing we did was send a synchronised message to the server telling the app was shutting down ...
the app was waiting on the server response to close

maybe you are doing something similar
like using sendmessage instead of postmessage
the solution you accepted has no meaning at all !
ASKER CERTIFIED SOLUTION
Avatar of 8080_Diver
8080_Diver
Flag of United States of America 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
galcott1
None of the solutions was helpful and for the time being I have put the problem aside.

Wow, I take my hat to that !

In other words, you give up ...
That is a viable option, i bet those customers really look up too you ?

This has only been happening for the last few months so I assume it has something to do with a recent code change, but I don't know exactly what causes this and have no idea how to search for the possible cause. I would appreciate any hints on what causes this and how to find the part of the code that needs fixing.

If i just look at this i would recommend the following:

a recent code change -->
a good developper (i am not one of them, as i don't exactly follow this either) has a versioning system checking in his code after a change.

no idea how to search -->
try Ctrl-F

any hints -->
then why delete ?

code that needs fixing -->
we didn't see a lot of code, (just 1 piece) and that was fine.
you didn't post any other code
you should check all the finalization and destroy (onDestroy, or onClose) code

you asked for hints on how to find your problem so you should give points for hints then
you didn't ask for a solution to your problem, just how to find it
Geert,

In response to your sarcastic comments I would like to explain myself a bit further. I am the developer of the app, but my client is the owner/vendor who actually deals with the customers. I posted this question in response to a request from him because he was a bit concerned that this had started happening. Since I posted, he hasn't had any new reports of the problem so he isn't as concerned now and asked me to drop it. So no, it was not a case of me giving up.

When I said that none of the solutions was helpful, it was because they either covered things I had already checked (OnClose code, etc). or didn't apply (finalization sectdions, which the app doesn't have).

SOLUTION
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
I realize that you and others were trying to help but my impression was that solutions were supposed to be accepted only if they actually answered the question. Just to close this out, I am going to accept multiple solutions and split the points.
Just to close this out, I am splitting points between the two who provided the most answers, even though none of them solved the problem and I am no longer working on it.
@modus_...,
My recommendation is either #2 (Delete - no refund) or #3 (Accept one or more Expert posts as the answer).  
@galcott1,
You have not contributed much toward this discussion, other than a negative attitude about the suggestions being made.  If you expected us to provide a detailed, complete solution to your problem with virtually no input from you and virtually no code to review, then you must also believe in and consult psychics regarding the rest of your life.  I, for one, am not a psychic (at least, not in that area ;-) and I cannot "channel" you or your code; therefore, I cannot determine what you have and have not checked, much less whether your process of checking has been complete or only targetted at those areas where you think the problem may have arisen.
If you made code changes and then this began happening, the code to review would, obviously, be that code that you changed.  Of course, you would need to also investigate the interaction of that code with the rest of the code.
As for your client telling you that the problem was no longer a concern on his part and your then dropping it.  Perhaps, if you had been providing some feedback to us along the way, we, too, might have dropped it.  However, "dropping it" and "giving up" are pretty close to synonymous, from where I sit.  I have been told that things are no longer a concern by clients (and, yes, I have done and still do independent development) but I have always continued to investigate to make sure that the problem did not reurn or show up in another project . . . but that may just be me and my personal ethics and my professional pride. ;-)
 
I am sorry if you think my attitude was negative as it was not intended this way. I knew it wasn't possible to get a detailed, specific solution because the situtation was too vague, but I was hoping for a suggestion that would contain something I hadn't thought about and which was relevant to the application. I didn't really get that. As far as providing code, this is a large app with over 200 forms and modules. What code was I supposed to provide?

Regarding my dropping the problem, I believe your comments are inappropriate. My client is paying for my time and he told me that he hadn't gotten any new reports of it, wasn't concerned about it any more and didn't want to pay for any more time spent on it. He has hundreds if not thousands of uses and often gets overly worried when 2 or 3 users report a particular problem, then realizes he has exaggerated the seriousness of it. So there was no reason for me to spend any more time on it but this is not "giving up", it is simply reapportioning my time to other things that are more important. And this investigation has nothing to do with my other projects. None of them are similar to this one and no such problem has occurred in any of them. So please don't give me the superior attitude about ethics and pride!