I have a MDI application written in Delphi 2007.
If the user exits a form within it whilst code is executing it causes an exception, because the code is trying to update a component or use an object that has been freed with the form.
Is there anyway I can tell if code is executing in the exit event or is there a standard way to deal with this situation?
Update with more infomation
The exception usually happen in the following circumstance.
A button on the child mdi form is pressed, this activates a function in the form, the function will go to the database and retrieve data, it will then re-format it and display it in a visual component on the form (usable a TListView).
If the code is taking a long time to execute (say if there is a lot of data to process) the user will lose interest and click the close button (the speed of the code is been worked on to try to avoid this).
The code inside the function is still executing even though the form it belongs to has been freed (The code is in the private section of the form), now when it trys to update the visual components they no longer exist (as they were freed with the form) and it throws a exception.
The code in the child form is usably in a loop when this happen, cycling records and update the listview accordingly, the loops contain code that looks like so
inc(i); if (i mod 25) = 0 then begin StatusPnl.Caption := 'Loading ' + intToStr(i) + ', Please wait'; application.ProcessMessages; end;
Other Code samples
the fromClose event looks like so
//Snip if (Not (Owner = nil)) then with (Owner as IMainForm)do begin //Snip DoFormFree(Self,Self.Name); end else //Snip
DoFormFree is a function in the main mdi parent form and looks like so
//Snip (G_FormList.Objects[x] as TBaseForm).Release; G_FormList.Objects[i] := nil; G_FormList.Delete(i); //Snip
All forms are stored in a list, as for various reasons, and all child forms extend the TBaseForm class.
Ideally I would like a way to tell if code in a form is executing, and prevent the user from closing the form, or hide it until the code is finished, as in some instances it may be generating a report and update as status panel when the exception happen, in that case the report will be incomplete.
as all forms are sub classes of TbaseFrom some global way of doing this would be ideal, so I can add the code to the base form and have it work on all descended forms.
You provide not enough information, but the easiest solution that comes to mind is to test in the OnCloseQuery handler whether code is executing, and if so set CanClose to False.
Alternatively you can decouple the code from the MDI form, by creating an intermediate object that both the form and the background code know about. You let this have a reference to the form, which is reset when the form is closed. By routing all access to the form through this intermediate object you can prevent the exceptions.
Edit: You need to provide information on how you execute the code that tries to access the MDI form after it has been freed. There are some ways to execute worker code, like:
Note that in the first case the form could only be freed if you either do it yourself in code, or if you call Application.ProcessMessages. Without more information about what your code looks like, nobody can give you a specific answer to your question.
Edit 2: With your added information it seems that the code in question is always executed in methods of the form. This is easy to catch by creating a boolean member that is set to True when the execution starts, and that is set to False when the execution has finished. Now you only need to add a handler for OnCloseQuery in your base class, and set CanClose to False if the member (fExecuting for example) is True. You can silently forbid closing, or show an information box. I’d simply show a progress form or display something in the status bar, so as not to interrupt the user too much with modal info boxes.
What I would definitely do is allowing the user to cancel the long running process. So you could also show a message box asking the user whether they want to cancel the operation and close. You still need to skip the closing of the form then, but can store the request to close, and process it once the execution has ended.