# Visual Basic > Visual Basic FAQs >  Classic VB - How should I close my form/program/class?

## si_the_geek

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:


```
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 form*s*, 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:
Stop any timer controls _on that form_Stop any code which is still running _in that form_Close any files that you opened with the Open statement _for use in that form_, but have not yet closed.Unload any controls which you created with code _on that form_ (not controls which you put on the form in the usual way)Unload all objects (including Class modules, and more) _which are declared in that form (or only used by that form)_Tidy up after certain API calls _which you used in that form_._(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:
Stop any code which is still running _in that class_Unload all objects (including Class modules, and more) _which are declared in that class_Close any files that you opened with the Open statement _for use in that class_, but have not yet closed.Tidy up after certain API calls _which you used in that class_._(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":



> 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:
Unload all forms  (note that each form needs its own code in the Form_Unload event, as described above)Stop any code which is still running _in modules_Unload all objects (including Class modules, and more) _which are declared in modules, and have not been unloaded by forms_Close any files that you opened with the Open statement, but have not yet closed.Tidy up after certain API calls _which you used in modules_._(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).

----------


## si_the_geek

*Unload all forms*
You can unload a single form by using the code in the first post, which is:


```
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:


```
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:


```
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:


```
Timer1.Enabled = False
```

If you have multiple timers, you can loop the Controls collection to do the same for all of them, eg:


```
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:


```
Open "C:\temp\file1.txt" For Input As #1
```

..you should close it like this:


```
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:


```
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:


```
  '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.

----------


## si_the_geek

*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 statementIf 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:


```
'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:


```
'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:


```
'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:


```
'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:


```
  '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 objectThis is much simpler, as it always applies, and is the same for all types of object variables, which is just:


```
Set variable = Nothing
```

Doing this allows the _Terminate event of the object to fire, and frees the  memory and system resources which it used.

----------


## si_the_geek

*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:


```
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:
SleepShellExecuteGetTickCountGetComputerNameGetUserNameGetDriveTypeBeepBitBlt / StretchBlt / TransparentBlt / PlgBltGetPixel / SetPixelCopyFile / DeleteFile / MoveFile / SHFileOperationCopyMemory / MoveMemory (also known as RtlCopyMemory / RtlMoveMemory)FindWindowGetLastErrorkeybd_event / mouse_eventGetPrivateProfileString / WritePrivateProfileString / WritePrivateProfileSectionSendMessage / PostMessageLoadCursor
Do require a tidy up:
FindFirstFileRegOpenKeySetWindowsHookExCreateBitmapExtractIcon..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).

----------


## si_the_geek

*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"):


```
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:


```
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.

.

----------

