Results 1 to 7 of 7

Thread: Correctly unloading chain of forms

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Correctly unloading chain of forms

    Hey

    My programs' structure is more or less as follows:
    1. The entry point is Private Sub Main() in the Loader module. This sets up a Public g_db ADODB connection and some other stuff.
    2. Then it fires up a LoginScreen form.
    3. If login succeeds (user + password correct), it fires up the actual program, MainForm.Show and Unload LoginScreen.
    4. The MainForm loads some public and private collections and objects used throughout the program, and at some points can temporarily show other forms.
    5. The user can either log out or just close the program.

    Question:
    What is the correct way of unloading the forms and making sure nothing remains dangling when closing the program? Currently in MainForm I do:
    Code:
        For Each objForm In Forms
            If objForm.hWnd <> Me.hWnd Then
                Unload objForm
                Set objForm = Nothing
            End If
        Next objForm
        Unload MainForm
        Set MainForm = Nothing
    Do I also need to manually set the Private/Public objects and collections to Nothing, or will they be destroyed when the form is set to Nothing?

    Since the LoginScreen form has been unloaded and set to Nothing, can I unload MainForm (and destroy all collections and objects) from within MainForm, or do I have to do that from the Loader module?

    Bonus: how do I verify that my program closes cleanly?

  2. #2
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Correctly unloading chain of forms

    Objects are released when no more references exist. Doesn't matter where they are declared. A well written object will clean up its own internal objects when the last reference to that object is released. Circular references are an exception.

    Setting a form to Nothing should release all objects declared within that form's Declarations section. There is no harm in setting those form-level objects to Nothing during the form's Unload event, as a matter of habit.

    If you create/show forms without modality, then using a loop like you posted works well. If a form is shown as owned by any form, then whenever the owner form closes, all owned forms are triggered to close also.

    When the last form unloads and all other code has completed, VB will release objects declared in modules, if applicable.

    If your program closes cleanly, you won't see it in the task manager.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  3. #3

    Thread Starter
    Addicted Member
    Join Date
    Jul 2016
    Posts
    230

    Re: Correctly unloading chain of forms

    Thank you LaVolpe!

    Confirmed that the program closes cleanly.

    However, I'm still having a problem: if instead of completely closing the program the user clicks logout (in the program, which takes you from the main form to the login form) and logs back in, Private m_isSomeBool As Boolean in other modules remain set to True from the previous run, whereas I'm lead to believe that if the MainForm is destroyed, then all booleans should be destroyed too and so should print False when I check them after logging back in.

    1. Start the Loader module, run LoginForm.Show vbModeless
    2. Log in, run MainForm.Show vbModeless and Unload LoginForm. The main program appears.
    3. Click a logout button which runs Unload MainForm, the Form_Unload routine stops all timers, sets all collections and objects in MainForm to Nothing.
    4. Still in the Form_Unload routine, I list all forms (code below): only MainForm is listed.
    5. Last step in the Form_Unload routine I call LoginForm.Show vbModeless. If I step through using F8, all that happens in MainForm is that some routines finish: Form_Unload ends and goes to btnLogout_Click which ends.
    6. Now in the LoginForm I enter the username and password and hit the login button, which first prints a list of all forms: only LoginForm is listed, so I'm lead to believe all booleans set by MainForm and other modules have been destroyed, but that is not the case. As I proceed to log in and load MainForm and initialize some other modules, I see that their booleans (declared as Private m_someBool As Boolean) remain True.

    What am I missing here?

  4. #4
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    19,541

    Re: Correctly unloading chain of forms

    Non-objects don't reset ever on their own. Variables in the Declarations sections of modules keep their current values as long as the application is running. Variables in the Declarations sections of forms, classes, and other non-modules will reset when that form, class, etc is eventually set to Nothing.

    You must manage non-objects separately. In non-modules, typically reset them in Form_Load or Form_Unload. In modules, reset them wherever it makes sense, if they even need to be reset.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Resource Image Viewer/Extractor}
    {VB and DPI Tutorial} {Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  5. #5
    Fanatic Member
    Join Date
    Feb 2019
    Posts
    706

    Re: Correctly unloading chain of forms

    Quote Originally Posted by OldClock View Post
    Thank you LaVolpe!

    Confirmed that the program closes cleanly.

    However, I'm still having a problem: if instead of completely closing the program the user clicks logout (in the program, which takes you from the main form to the login form) and logs back in, Private m_isSomeBool As Boolean in other modules remain set to True from the previous run, whereas I'm lead to believe that if the MainForm is destroyed, then all booleans should be destroyed too and so should print False when I check them after logging back in.

    1. Start the Loader module, run LoginForm.Show vbModeless
    2. Log in, run MainForm.Show vbModeless and Unload LoginForm. The main program appears.
    3. Click a logout button which runs Unload MainForm, the Form_Unload routine stops all timers, sets all collections and objects in MainForm to Nothing.
    4. Still in the Form_Unload routine, I list all forms (code below): only MainForm is listed.
    5. Last step in the Form_Unload routine I call LoginForm.Show vbModeless. If I step through using F8, all that happens in MainForm is that some routines finish: Form_Unload ends and goes to btnLogout_Click which ends.
    6. Now in the LoginForm I enter the username and password and hit the login button, which first prints a list of all forms: only LoginForm is listed, so I'm lead to believe all booleans set by MainForm and other modules have been destroyed, but that is not the case. As I proceed to log in and load MainForm and initialize some other modules, I see that their booleans (declared as Private m_someBool As Boolean) remain True.

    What am I missing here?
    What's happening is that MainForm is unloaded, but Form_Terminate was not called yet, so the form object is not destroyed(MainForm is NOT Nothing), and so module-level variables remain in memory and retain their value. I found these solutions to fix this issue:

    1 - Use this line as the last line in Form_Unload of MainForm:

    Set MainForm = Nothing

    You can't use "Set Me = Nothing", it won't compile.

    2 - In Sub main or the login form, declare a form variable rather than using MainForm. Example:

    VB Code:
    1. Public Sub Main()
    2.   Dim fMainForm As MainForm
    3.  
    4.   Set fMainForm = New MainForm
    5.   fMainForm.Show
    6.   Set fMainForm = Nothing
    7.  
    8. End Sub

    See "Returning to the Created, But Not Loaded State" in this MSDN article for more details.
    Last edited by qvb6; Dec 17th, 2019 at 10:51 AM. Reason: Clarified that Form_Terminate wasn't called, so variables retained their values.

  6. #6
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Correctly unloading chain of forms

    Quote Originally Posted by OldClock View Post
    Code:
        For Each objForm In Forms
            If objForm.hWnd <> Me.hWnd Then
                Unload objForm
                Set objForm = Nothing
            End If
        Next objForm
        Unload MainForm
        Set MainForm = Nothing
    Just to say it, that Set objForm = Nothing line isn't doing much of anything. The minute your loop loops again, that's going to implicitly happen. At the most you'll need is to execute that statement after the loop completes.

    For me, what's most important to recognize is that something like Set objWhatever = Nothing only clears out that specific objWhatever variable, and doesn't necessarily uninstantiate the object. The object is uninstantiated only if/when the last variable referencing it goes to Nothing. (Unless its internal reference counter is borked, but that's a whole different discussion.)

    Quote Originally Posted by OldClock View Post
    Do I also need to manually set the Private/Public objects and collections to Nothing, or will they be destroyed when the form is set to Nothing?
    From my perspective, neither of those is true. It's important to remember that a form has two pieces: 1) the UI piece, and 2) the COM/code piece. Unload only addresses the UI piece and leaves the COM/code piece instantiated. (And your Set objForm = Nothing doesn't uninstantiate it either because objForm certainly isn't the primary variable for referencing the COM/code piece).

    But (and here's why neither of your statements is true), an instantiated COM/code object won't keep a VB6 project from terminating. There are only two things that keep a VB6 project running: 1) a loaded (but not necessarily showing) form's UI piece, and/or 2) actively executing code. Now, just because an object (i.e., COM/code) is instantiated does not mean that it's actively executing. It may be just sitting there waiting on some event to fire.

    So, that's my summary. And yeah, for me, it's good practice to uninstantiate a form's COM/code piece when the form's UI is unloaded, but it's not necessary to get that VB6 program to terminate.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  7. #7
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,853

    Re: Correctly unloading chain of forms

    Quote Originally Posted by qvb6 View Post
    You can't use "Set Me = Nothing", it won't compile.
    Even if this was allowed, you wouldn't be doing much of anything. The "Me" implicit object variable is just yet another reference to a COM/code object. Setting it to Nothing would still leave all other references active.

    And, the "Me" reference is an internal reference that gives us access to the COM/code object while we're inside of it (which often comes in very handy). Also, the existence of this "Me" object variable explains why we can de-reference (i.e., set to Nothing) all other references to a COM/code object and it stays instantiated until whatever code is being executed within it completes execution (and then, that "Me" object variable goes out of scope, possibly allowing the COM/code object to uninstantiate). And this is actually a good explanation of why "Set Me = Nothing" isn't allowed. We can't allow COM/code to uninstantiate while it's executing.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width