Re: Terminating Access Applications

From: Wayne Pearson (YellowDory00_at_hotmail.com)
Date: 03/22/04


Date: Mon, 22 Mar 2004 02:00:34 -0330

Hi Albert,

First I would like to thank for your comments. Your proposed solution worked
well and your comments enabled me to improve my test application. You also
correctly identified the theme of my post, which is you must be cautious when
using DoCmd.Quit to terminate an Access application. Incidentally, this
cautionary advice is also true for Application.Quit and CloseCurrentDatabase.

Next I'll state my conclusions and general comments

Note that the errors I encountered may be specific to my particular setup and
machine; I didn't have the opportunity to test my application on other
machines. I'm using Access 2000, Office 2000, and Windows 2000; each of which
has the latest service packs applied.

If one of your application's forms (lets call it frmTest) has a member variable
that is a dynamic array, ie an array dimensioned in the style
Dim m_Numbers() As Long, or a member variable whose type is a
user defined class then you must exercise caution when exiting your application
using any of DoCmd.Quit, Application.Quit, or CloseCuurentDatabase. If
frmTest is OPEN and you exit the application by calling any of these three exit
methods (possibly by pressing the exit button on a switchboard form) then
frmTest's member variable, as described above, will be destroyed before
BEFORE Form_Unload is called (and therefore BEFORE Form_Close is called).
Additionally, if the member variable has a type of user defined class then
its Terminate event will NOT fire. These problems can be avoided if a routine
that closes all open forms is executed prior to calling any of these three
exit methods. Thanks to Albert D. Kallal for proposing this solution.

Executing a routine that closes all open forms prior to calling any of the
three exit methods listed above WILL avoid the problems I experienced. However,
I strongly feel that the de facto method for exiting an Access application
should ITSELF take responsibility for closing all open forms if, in fact, this
is required to ensure a graceful exit of the application . the developer
should NOT have to write a special routine to close all forms prior to
calling the application exit method. I would assume that the user, upon
pressing the exit button, would prefer that the application exit gracefully
rather than bail out. Typically, rudely exiting an application entails
bringing up the task manager, selecting the target application, and pressing
the End Task button. This action is frequently accompanied by the muttering
of colorful expletives.

I initially tested three options for exiting the application
1) selecting file . exit, 2) pressing the x in the upper right corner of
Access, and 3) pressing the Exit button which generated a call to DoCmd.Quit.
Option 3 (DoCmd.Quit) is the ONLY exit method of the initial three that
caused me trouble. Later, I discovered that Application.Quit and
CloseCuurentDatabase were also problematic.

Note that posting a WM_CLOSE message to the application's main window
(using PostMessage NOT SendMessage) enables the application to exit gracefully
without requiring the use of a special procedure to close all open forms.
I'm not sure if there are any caveats to using this technique but, so far,
it works for me. If I exit the application in this manner then all open forms
are closed, all Form member variables are still available in both Form_Unload
and Form_Close, and the Terminate event for Form class member variables fires
AFTER the Form_Close event.

I have finished stating my conclusions and general comments at this point and
will now endeavor to respond to your specific comments below.

> First...why not use the forms close event...and why use the un-load event?
> The un-load event is NOT for clean up, but only to cancel the un-load...

I like your suggestion to place the code for Form member variable cleanup in
Form_Close. I will most likely adopt this technique in future projects. I've
actually recoded my test project to use Form_Open and Form_Close instead of
Form_Load and Form_Unload. I also added error handling to catch an issue
concerning execution of my explicit cleanup code for frmSecondary's m_Test
member variable. However, in this instance, moving frmSecondary's code from
Form_Load and Form_Unload to Form_Open and Form_Close, respectively, AND
adding the error handling, did NOT change the results (with the exception
that the errors occurred in Form_Close rather than Form_Unload).

> 2nd, where, and why are you executing the application docmd.quit?

First the where

In my case I have an application that has two forms; frmPrimary and
frmSecondary. frmPrimary serves essentially the same purpose as a
switchboard form and it has two buttons, a Display button and an Exit button.
Clicking frmPrimary's Display button invokes cmdDisplay_Click, which then
displays frmSecondary. Clicking the frmPrimary's Exit button invokes
cmdExit_Click, which then calls DoCmd.Quit. frmSecondary has a member variable,
m_Test, that is a user defined class variable of type Test. frmSecondary's
Form_Open event instantiates and populates this variable. frmSecondary's
Form_Close event procedure attempts to print m_Test's Value. It later attempts
to explicitly cleanup m_Test by executing the statement Set m_Test = Nothing.

I first bring up frmPrimary, press its Display button to show frmSecondary and
then press frmPrimary's exit button while frmSecondary is still open.

Next the why

Well, if one uses the toolbox to add a quit button to a form and selects
Application . Quit Application then a quit button is added to the form. This
button's OnClick event procedure contains a call to DoCmd.Quit. If the toolbox
supplies a button with this code then it MUST be Ok, right? Note that there is
NO code generated that iterates through all open forms and closes them
prior to calling DoCmd.Quit. I also checked out the wizard generated
Asset Tracking1 template database, and discovered that CloseCuurentDatabase is
the code generated to respond to the Exit button. Again, note that there is
NO code generated that iterates through all open forms and closes them
prior to calling CloseCuurentDatabase.

> 3rd, a forms level array declared is STILL available in both the close event
> code, and the un-load event code. I don't know why you state this. Even more
> telling is that the class objects terminate event DOES get executed.
> However, you do need to explicitly clear out your class object code.

If you check my original post you will see that I did put Set m_Test = Nothing
in the Form_Unload event procedure and, in fact, did not depend on the variable
going out of scope to clean it up. I do admit, however, that I screwed up and
didn't put error handling in my routine . even test code deserves
proper error handling. When the error was encountered on the line
Debug.Print m_Test.Value, code execution ceased and
Set m_Test = Nothing wasn't reached. I've since recoded my test routine,
adding error handling to ensure that Set m_Test = Nothing does, in fact,
get executed. However, in my case the m_Test variable is destroyed BEFORE
reaching the statement to explicitly clean it up and its Terminate event does
NOT fire. I use the term dynamic array to refer to an array dimensioned
in the style Dim m_Number() As Long. If a user has such a dynamic array or a
class member variable in his Form AND he exits his application following the
scenario that I indicated, EVEN with the added error handling to ensure that
Set m_Test = Nothing DOES get executed, then the member variable WILL be
destroyed BEFORE Form_Unload is called (and therefore BEFORE Form_Close
is called). Additionally, the class member variable's Terminate event will
NOT fire.

If you follow the scenario that I indicated then I'm unsure of why you're don't
encounter the errors I described. Possibly the errors are specific to my
machine and setup. I'm using Access 2000, Office 2000, and Windows 2000; each
of which has the latest service packs applied.

Thanks,

Wayne Pearson

"Albert D. Kallal" <pleasenonosspammkallal@msn.com> wrote in message news:OYvbKjoDEHA.628@TK2MSFTNGP10.phx.gbl...
> I going to skip reading most of this long post. It seems you got WAY TOO
> much time on your hands.
>
> First...why not use the forms close event...and why use the un-load event?
> The un-load event is NOT for clean up, but only to cancel the un-load...
>
> 2nd, where, and why are you executing the application docmd.quit?
>
> 3rd, a forms level array declared is STILL available in both the close event
> code, and the un-load event code. I don't know why you state this. Even more
> telling is that the class objects terminate event DOES get executed.
> However, you do need to explicitly clear out your class object code. You
> should always go:
>
> set clsMyClassObject = nothing
>
> The above code (if it runs!) will ensure the terminate event gets run.
>
> If you are not explicit clearing up your class objects, then that is a bit
> sloppy.
>
> Do NOT use the fact of vars going out of scope to clean up objects.
>
> For sure, if you are coding a docmd.quit, I would first of course at give
> ms-access some curtsies...and fire the close event of the form first!.
>
> All we really are saying here is that docmd.quit is a bail out, and it means
> to bail out...and yes, the event code does then loose the variable scope.
>
> So, why not just clean up things before you close?
>
> So, in the case of a form...
> Just go:
>
> DoCmd.Close
> DoCmd.Quit
>
> The above will then fire the close event, and the un-load event (which fires
> before the close event). And, all of the vars, and class objects are
> availing in this case.
>
> So, even if you do a "quit" command, the above setting of the object to
> nothing will cause the terminate event of the object to fire also.
>
> And, of course you do the same for any other reocrdset code etc. that you
> have. Any object you create should be destroyed by you. For general vars
> like strings etc, the system will do a fine job.
>
> I would also consider coding my own custom application Quit routine that
> correctly shuts down all open forms. It is a simple matter to iterate the
> forms collection, and THEN execute
>
> application.Quit
>
> So, if you are stating that application.Quit (or docmd.Quit) is a bit
> nasty...sure. But, if you are actually using the command in your code...then
> as mentioned just execute the forms close event first.
>
> If you are looking for a controlled shut down..then I would code your own
> shutdown routine.
>
> I have a simple application with 160 forms, and 27,000 lines of code. (I
> consider this application simple...since as mde..it still fits on a floppy
> disk). In that code I also have a form timer event that shuts down the
> application after two hours of inactivity. I never had a problem, but in my
> case about 90% of my forms are model, and when I launch from one for to the
> next, I always done a disk write, and as a result..never a problem. I always
> considered it risking to launch to another form without the current form
> having written its data.
>
> And yes, in my case the docmd.Quit will cause forms to un-load, and class
> objects to go out scope...but since I ALWAYS do a disk write before opening
> the next from in the sequence...never a problem...
>
> --
> Albert D. Kallal (MVP)
> Edmonton, Alberta Canada
> pleasenonoSpamKallal@msn.com
> http://www.attcanada.net/~kallal.msn
>
>