Results 1 to 5 of 5

Thread: Classic VB - How should I close my form/program/class?

  1. #1

    Thread Starter
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Classic VB - How should I close my form/program/class?

    One kind of question that repeatedly gets asked on the forums is about properly (or completely) closing a form/class, or an entire program.

    The guidelines below will hopefully cover everything you need to know.. but don't be put off by the length of this article, as most of it is just explainations and examples of the bullet points in this first post (you can skip sections that don't apply to you). Depending on your program, closing a form/class/program may be just a couple of lines of code!

    If after following this guide you find that something is not unloading properly (ie: in VB itself you need to press the "stop" button, or when compiled your program is still listed in Task Manager), see the last post in this thread, as it explains how to find the issue.

    Closing a form
    A form will start to close when the user presses the "X" button on the titlebar, or you tell it to close, or one of a few other reasons (such as Task Manager is closing your program).

    If you want to tell a form to close (such as in an "Exit" button you add to the form), use the Unload statement, eg:
    Code:
    Unload Form1        'tell the form to unload
    Set Form1 = Nothing 'free the memory used by the variables
    Note that if this code is in the form itself, you should add "Exit Sub" (or "Exit Function") immediately afterwards, so that no more code runs (otherwise any code that refers to the form or its controls/properties will re-load it!).

    Events that occur during the unloading process
    When a form starts unloading, the Form_QueryUnload event will fire, and you can use this to cancel the unload process of your form (or forms, if multiple are closing for the same reason). For details of how to do that, see the article How can I show a confirmation message when my form is closing?. If the process is Cancelled, the form(s) will stay open, and no more events will fire.

    Next the Form_Unload event will fire, and this is the recommended place to put your tidy up code, as it does not interfere with the _QueryUnload process (which is important if you have multiple forms, and particularly so for MDI applications), and does not have the problems which the next event has.

    There is also a Form_Terminate event, but using it for your tidy up process is not recommended as it fires after most visual aspects of the form have unloaded, so if you use that, you need to be much more careful what code you use - otherwise the form will re-load!

    Note that if you use the Unload statement, all three of these events will fire before the next line of code runs (in my example above, the "Set" line).

    Making sure the form is unloaded properly
    No matter how the closing of the form was started (via code, the user pressing the "X" button on the titlebar, etc), you should ensure that you tidy up properly - if you don't, the form might not actually unload.

    To tidy up, you should perform the following steps in the Form_Unload event:
    (these links point to sections of this page with more details - if you click the links, you can press your Back button to return to this list)

    Properly closing a class
    A class will automatically start to close when you explicitly set the variable you used to Nothing (eg: "Set MyClassVariable = Nothing"), or when the variable goes out of scope.

    When this happens, the Class_Terminate event will fire. In this event you should perform the following steps:
    (these links point to sections of this page with more details - if you click the links, you can press your Back button to return to this list)

    Closing your entire program
    Several people use (or even recommend) using the "End" statement to close a program, however there are many reasons why you should not do that, as explained in the article Why is using the 'End' statement (or VB's "stop" button) a bad idea?.

    The correct method is briefly explained in the help for "End":
    Quote Originally Posted by VB's help for the End statement
    For normal termination of a Visual Basic program, you should unload all forms. Your program closes as soon as there are no other programs holding references to objects created from your public class modules and no code executing.
    Here is a more detailed version of that:
    (these links point to sections of this page with more details - if you click the links, you can press your Back button to return to this list)

    Don't forget that if you are using code to initiate the close process, you need to add "Exit Sub" or "Exit Function" after it, so that no code after that point runs (as that is likely to keep your program open).

  2. #2

    Thread Starter
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Classic VB - How should I close my form/program/class?

    Unload all forms
    You can unload a single form by using the code in the first post, which is:
    Code:
    Unload Form1        'tell the form to unload
    Set Form1 = Nothing 'free the memory used by the variables
    If you have multiple forms in your project it is tedious to write it out for each form. It can also be unreliable if you forget to add any of the forms to that section of code (which is likely if you add a new form to your project later), or if you have used code to duplicate your forms.

    An easier (and always accurate) solution is to loop the Forms collection, which contains a reference to each form that is currently loaded. You can do that as shown in the following examples.

    To unload all forms from a module (assuming the code hasn't been called by a form), you can use this simple version:
    Code:
    Dim objForm as Form
        'unload all forms
      For Each objForm In Forms
        Unload objForm
        Set objForm = Nothing
      Next objForm
    To unload all forms from code within a form (or code that is called by the code in a form), you should ensure that you close that form last, so need to do a little more work:
    Code:
    Dim objForm as Form
        'unload all forms except this one
      For Each objForm In Forms
        If objForm.hWnd <> Me.hWnd Then  'only the hWnd property is guaranteed to be unique
          Unload objForm
          Set objForm = Nothing
        End If
      Next objForm
        'unload this form
      Unload Me


    Stop any timer controls
    If you do not stop a timer control before unloading the form, the timer may fire at the 'wrong' time, and so re-load the form (this is particularly true if any of the code refers to form properties).

    To stop this from happening, just disable it like this:
    Code:
    Timer1.Enabled = False
    If you have multiple timers, you can loop the Controls collection to do the same for all of them, eg:
    Code:
    Dim objCtrl As Control
      For Each objCtrl In Me.Controls 
        If TypeOf objCtrl Is Timer Then
          ctl.Enabled = False 
        End If 
      Next objCtrl


    Close any files that you opened with the Open statement, but have not yet closed.
    When you open a file with the Open statement, you should close it when you have finished with a matching Close statement. For example, if you used this to open:
    Code:
    Open "C:\temp\file1.txt" For Input As #1
    ..you should close it like this:
    Code:
    Close #1
    Note that if you opened the file "for Output" or "for Append", the file may not be updated until you run the Close statement for that file.

    If you want to close more than one file, you can specify multiple with the same Close statement, eg:
    Code:
    Close #1, #2, #3
    ..or to close all of the files that are currently open, simply call Close without any parameters.


    Stop any code which is still running
    There are two main reasons that code can be still running, one is because your 'close' routine has been called by other code (so any code after the call will run after your 'close' code has finished), and the other is because you have used DoEvents (which allows various things to happen, such as the user pressing the "X" button on a forms titlebar, or Task Manager telling your program to close!).

    With either of these cases, you should exit the routine as soon as possible, which you can do with Exit Sub (or Exit Function ). If you don't, you might accidentally re-load something, or do something which seems odd to the user. Also note that if the routine was called from another part of your program, you also need to exit the other routine!

    It is easy to spot the need for Exit Sub when your code explicitly calls your 'close' routine, but dealing with DoEvents is a little harder, as you may not expect your program to close at that point.

    If the code after DoEvents is quick, does not reference the form it is on (or the controls on the form), and does not interact with the user (such as showing a MsgBox) then you probably don't need to worry.

    In other cases, you should exit that routine immediately after the DoEvents if the form/program is closing. One way to check this would be to add a Boolean variable, which you set to True as part of your unloading process. For a Form, that could be done like this:
    Code:
      'in the General Declarations section (at the top of the code file)
    Private m_boo_IsUnloading as Boolean
    
      'in Form_Unload
    m_boo_IsUnloading = True
    
      'after each DoEvents line
    If m_boo_IsUnloading Then Exit Sub  '(or "Exit Function", etc)
    In addition to this, an important point to think about is whether that routine is called by other code. If it is, it would be a good idea to also add that If statement immediately after each call to the sub/function.

  3. #3

    Thread Starter
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Classic VB - How should I close my form/program/class?

    Unload any controls which you created with code
    This applies to controls which you specifically create with code, and does not apply to ones which you manually placed on a form (attempting to unload those will give you an error).

    How you unload the controls depends on how your created them:

    If you used the Load statement
    If you created a control array by making a 'template' control on your form, and adding more copies of it at runtime with different indexes, simply unload the ones you created, eg:
    Code:
    'create a copy of our template control
    Load cmdMyButton(1)
    
    'unload the control we created
    UnLoad cmdMyButton(1)
    ..or if you created multiple copies of the same control, you can use a loop to unload them (with an If statement so you don't unload the original, as that gives an error), eg:
    Code:
    'Unload members of the control array we created with code
    Dim ctlTemp As Control
      For Each ctlTemp In cmdMyButton  'loop each control in the array (change cmdMyButton to the name of your control array)
        If ctlTemp.Index <> 0 Then     'dont attempt to unload the original (change 0 to the Index of your template control)
          Unload ctlTemp
        End If
      Next ctlTemp
      Set ctlTemp = Nothing
    If you used Controls.Add

    If you added the control(s) with Controls.Add, you can unload the control by using Contols.Remove along with the name you gave the control, eg:
    Code:
    'add the control
    Controls.Add "VB.CommandButton", "cmdMyButton"
    
    'unload it
    Controls.Remove "cmdMyButton"
    ..and if you stored a reference to the control in a variable, you should also release that variable, eg:
    Code:
    'in declarations section
    Dim cmdMyButton As CommandButton
    
    'add the control
    Set cmdMyButton = Form1.Controls.Add("VB.CommandButton", "cmdMyButton")
    
    'unload it, and release the memory used by the variable
    Controls.Remove "cmdMyButton"
    Set cmdMyButton = Nothing
    If you used an array instead of a simple variable, add a loop to do the same for each array item.


    Unload all objects (including Class modules, and more)
    No matter what the specific data type is called (eg: Class1, Form1, Object, Excel.Application, ADODB.Recordset, ...), if you use "Set" or "Dim .. As New ..", that variable is a kind of Object variable.

    Note that it is recommended to not use "Dim .. As New ..", as that (among other things) can hinder unloading, as explained in this article.

    Most objects should be unloaded immediately after you finish with them (so that the memory is released, etc), but there are times that you want objects for as long as your form/program is running, and so you need to close them when the form/program closes instead of in the routine where they were created.

    How you unload an object variable depends on the specific data type, but there are two basic steps: tell it to close (if apt), and then disconnect from it.

    Step 1 - Telling the object to close (if apt)
    Many object variables do not need to be told to close (as they will automatically do that when you disconnect from them), but others will cause problems if you do not explicitly close them - such as staying in the memory (thus stopping other programs from using it), showing error messages to the user when the computer shuts down, and more.

    There are so many kinds of object that it is not practical to explain them all. I have listed some, but for any others you should check the documentation for the type of object you are using.
    • A Collection does not need to be told to close.

    • Any Class modules in your project do not need to be told, unless there has been a sub/function added to them specificaly for that purpose (note that if there is, it is usually better to put that code into the Class_Terminate event instead, which is automatically called when the class is disconnected).

    • Many Word/Excel/Outlook objects (particularly the .Application and any documents) do need to be told to close, and you can find examples in the "Visual Basic 6" section of our Office Development FAQs.

    • Most database objects also need to be told. Assuming you are using ADODB code, for each Recordset and Connection you do that with "variable.Close", but note that you will get an error if it's already closed, so you should check the .State property first, like this:
      Code:
        'as .State can contain multiple values at a time, use bit-masking to check if it is Open
      If (variable.State And adStateOpen) = adStateOpen Then variable.Close
      Note that this should be done for all Recordsets first, and then the Connection afterwards.

    Step 2 - Disconnect from the object
    This is much simpler, as it always applies, and is the same for all types of object variables, which is just:
    Code:
    Set variable = Nothing
    Doing this allows the _Terminate event of the object to fire, and frees the memory and system resources which it used.

  4. #4

    Thread Starter
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Classic VB - How should I close my form/program/class?

    Tidy up after certain API calls
    For those of you who don't know what an API is, it is a routine from outside your program, and to use one you need to have a Declare Sub or Declare Function line in the "General" -> "Declarations" section at the top of a code file, which will look something like this:
    Code:
    Declare Sub Sleep Lib "kernel32" Alias "Sleep" (ByVal dwMilliseconds As Long)
    Many API's (such as Sleep) don't need any tidying up, but others (like CreateBitmap and LoadCursor) do need it, and failing to do so can lead to various problems including file corruption, stopping programs from using a shared resource such as graphics (which may cause them to crash), and even crashing Windows!

    As there are so many API's, it is not practical to explain them all here, but I will comment on the ones I see used most often:
    Tidy up not required:
    • Sleep
    • ShellExecute
    • GetTickCount
    • GetComputerName
    • GetUserName
    • GetDriveType
    • Beep
    • BitBlt / StretchBlt / TransparentBlt / PlgBlt
    • GetPixel / SetPixel
    • CopyFile / DeleteFile / MoveFile / SHFileOperation
    • CopyMemory / MoveMemory (also known as RtlCopyMemory / RtlMoveMemory)
    • FindWindow
    • GetLastError
    • keybd_event / mouse_event
    • GetPrivateProfileString / WritePrivateProfileString / WritePrivateProfileSection
    • SendMessage / PostMessage
    • LoadCursor

    Do require a tidy up:
    • FindFirstFile
    • RegOpenKey
    • SetWindowsHookEx
    • CreateBitmap
    • ExtractIcon
    • ..many which start with "Create" or "Load"
    • ..some which start with "Get"
    There is another list here which provides more information (it has more items, and explains in each case what API's to use to tidy up).

    If you aren't sure whether the API's you have used need any tidying up (or aren't sure how to do it), you can check the reference at Microsofts web site, which lists API's in alphabetical order and grouped by category. If you do need to tidy up for a particular API, there will usually be an explanation in the "Remarks" section of the page (but it may be in the "Parameters" section instead, or via an 'Overview' document which is linked in the "See Also" section).
    Last edited by si_the_geek; Oct 23rd, 2008 at 12:00 PM.

  5. #5

    Thread Starter
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,929

    Re: Classic VB - How should I close my form/program/class?

    If it doesn't actually close, how can you find out why?
    If you find that the program doesn't close when it is supposed to (if running in VB you need to press the "stop" button, or when compiled it is still listed in Task Manager), then you aren't unloading everything properly.

    This may be because you have missed (or not correctly coded) one of the steps above, or because you have not unloaded items correctly in individual routines (or you have in the routine itself, but not in the error handler for it!).

    Unfortunately it isn't always easy to find out what the cause of the problem is - you are going to have to check and/or debug your code, but hopefully these tips will reduce how much of it you need to check.

    Finding which forms are still loaded
    If your program has multiple forms, you can press the "pause" button in the IDE, and put code like this into the Immediate window (which you can see by going to "View" -> "Immediate"):
    Code:
    For Each f In Forms:  MsgBox f.name:   Next f
    When you press Enter, this will tell you which forms are still open. While that doesn't give you the information you really want (which particular item or piece of code is causing the problem), at least it narrows down the possibilities.

    Finding out if forms are being re-loaded
    To find out when a form is loaded (or re-loaded, which can happen if you reference any of the properties (such as: Form1.Caption) after it has unloaded), you can add code like this to the Form_Load event of each form:
    Code:
    Debug.Print CStr(Time) & " loading form: " & Me.Name
    This will print the time that each form is loaded to VB's Immediate window, and if the time shown is at/after the time you tried to unload, the form is being re-loaded by other code.

    Tracking down the problem
    If the above methods don't pinpoint the issue, you will need to Debug the code. To do this, put a breakpoint on the line of code where you start unloading (or better, the line that calls it) by pressing F9 on that line. Now run the program, and when it stops at that breakpoint, press F8 to run a single line of code at a time, and continue to do so until you find the problem. More information about debugging can be found in the tutorial Using VB6 Debug - Introduction

    If after that you still can't find the problem, post a new thread in the Visual Basic 6 and Earlier forum, giving as much detail as you can.

    .
    Last edited by si_the_geek; Sep 23rd, 2009 at 07:42 AM. Reason: added link to debug tutorial

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