What is the 'preferred' method of shutting down a VB application. Is Unloading all Forms, Closing all Open Files, removing all Object References enough? Is it OK to place an End Statement in the Terminate() Event of the Form from which you want to Close the Project? Thanks in advance.
In answer to the first question, yes.
For the second..
You should not need an end statement. I can't remember the last time I used one or even saw one in well written code.
End is catastrophic. A Form has no Terminate event. Object references are freed when the variables go out of scope, so unless you've created circular references you normally never assign them Nothing when their parent module unloads.
And basically, placing an End in the main form's Terminate event shouldn't have an advantage; that's likely the last event to fire. If your project does not close properly, End is a novice solution. The real solution is to determine why and there are several causes for such a scenario.
Insomnia is just a byproduct of, "It can't be done"
Unload all forms, set all forms to Nothing, and if you have variables in bas modules holding any type of object set them to Nothing.
If you don't have circular references that should be all you need to do.
I spent part of this weekend going back over object reference destruction.
Been a lot of misinformation about it over the years. Destroying Form Instances
-- one at a time -- seems to be especially troubling. Should the user get involved or should one let VB take care of the matter and how does one track (confirm) whether the specific Form instance was destroyed.
For local scope variables, it also appears how they are destroyed in dependent on how they were created. He's what I've got so far on locals (unconfirmed).
Code:
'///////////////////
'Source: https://stackoverflow.com/questions/42164719/how-to-destroy-an-object
'1) A Dim statement is transient
'2) = Nothing is handled differently depending on the Declaration
'CASE 1:
Sub Test2()
Dim Fs As New FileSystemObject '<< object variable declared with NEW
' because a Dim statement isn't an executable statement
' the variable is in-scope throughout the procedure;
Set Fs = Nothing '<< Object variable destroyed
MsgBox Fs.Drives.Count '<< VB recreates the object variable automatically after = Nothing
' because object variable re-referenced and still in local scope
' so count says object NOT destroyed
End Sub '<< Object variable destroyed as at end of scope
'CASE 2:
Sub Test2()
Dim Fs As FileSystemObject '<< object variable declared
Set Fs = New FileSystemObject '<< object variable referenced (points to object)
Set Fs = Nothing '<< Object variable destroyed
MsgBox Fs.Drives.Count '<< No object variable recreated and count is correct
' object variable was destroyed
End Sub '<< End of scope (unknown whether another call made, probably Not since count decremented ?)
'Case 3:
Sub Test()
With New FileSystemObject '<<object is set and contained within the With block
MsgBox .Drives.Count
End With '<< Class terminate is called and object is destroyed
End Sub '<< End of Scope
Last edited by vb6forever; Jul 7th, 2020 at 07:37 AM.
'CASE 2:
Sub Test2()
Dim Fs As FileSystemObject '<< object variable declared
Set Fs = New FileSystemObject '<< object variable referenced (points to object)
Set Fs = Nothing '<< Object variable destroyed
MsgBox Fs.Drives.Count '<< No object variable recreated and count is correct
' object variable was destroyed
End Sub '<< End of scope (unknown whether another call made, probably Not since count decremented ?)
The line in red will throw an error, because you are trying to access a property with a variable that has Nothing. (Object variable not set)
> Should the user get involved or should one let VB take care of the matter and how does one track (confirm) whether the specific Form instance was destroyed.
Both are hard questions. What I prefer for logging puposes is to have nested init/terminiate events logged for referenced objects like this
Parent Init
Child Init
Child Terminate
Parent Terminate
For this to happen the Parent class has to use something similar to this
Code:
Private Sub Class_Initialize()
DebugPrint "Parent Init"
'... more init here
End Sub
Private Sub Class_Terminate()
Set m_oChild = Nothing
'... more cleanup here
DebugPrint "Parent Terminate"
End Sub
Without the explicit Set m_oChild = Nothing the reference in the member variable will get auto-cleared right past the Terminate event so the debug log will get inter-leaved like this
Also there is a bug in the IDE, if you add watches (or evaluate expr) in Terminate event that is called from internal run-time cleanup, then the interpreter goes berzerk and skips tear-down altogether with catastrophic results, so setting child references to nothing prevents this bug as well.
Tracking instances has been discussed here before. There is nothing built-in besides the Forms collections and it's not tracking strictly form instances (rather the UI part of these forms). For robust instance tracking one has to be prepared to implement in VB6 techniques similar to memory allocation tracking in MFC or other C/C++ frameworks. It turns out that writing robust production-grade VB6 code is *not* easier than writing it in C/C++ which was a kind of a revelation for me after all those years in the field. I'm inclined to believe that writing production code is similarly hard in *any* language.
Debated whether to post as work-in-progress but applicable to this thread, form closing, and tracking closing specific instances.
Not my thread, but Eduardo, wqweto thanks for responding.
Re:
I'm inclined to believe that writing production code is similarly hard in *any* language.
Logic is logic, just syntax differences. Kids today don't realize how good they have it. For us old guys -- going from punch cards to edline was a big deal as well as not having to code based on who manufactured the machine.
Last edited by vb6forever; Jul 7th, 2020 at 10:30 PM.
I have been away from VB for awhile and each day the recollection is coming back thanks to you guys. I am currently working on a Project that involves the Loading/Unloading and Showing/Hiding of several Forms. As a method of exiting cleanly, and to insure that all Forms are Unloaded, I was considering the following Code to be placed in the Click() Event of my Exit Command Button on my Main Form. No Close Box, Min, or Max Buttons exist on this Form. Is this approach advisable, and is it the proper location for the Code? Thanks in advance.
Code:
Dim frm As Form
For Each frm in Forms
Unload frm
Next
@ADezii: This is the most straightforward approach and what every other approach boils down to.
Couple of comments:
- You might want to wrap this in a function.
- A form might have more that one instance in the Forms collection so don't be surprised to find two instances of Form1 there.
- You might want to check Forms.Count after the loop. If any form is left not unloaded this probably means program termination is cancelled (form unloading is cancelled in Form_QueryUnload).
- There is a *separate* Forms collection for each ActiveX projects, besides the main Std-Exe one. So if you app is using AppSales.DLL and AppPurchases.DLL as ActiveX DLL projects both these will have separate Forms collections that might want to be unloaded too before exiting your app.
- Unloading main Std-EXE projects Forms collection terminates the process at once, i.e. unloading ActiveX projects Forms collection is not automatic and has to be taken care of manually or lose unsaved changes.
It sounds like child Forms were shown modelessly without specifying the owner Form. That's the only scenario I can recall where you'd need to iterate over the Forms collection.
Specifing an owner form controls both lifetime and z-order -- two completely unrelated things unfortunately.
The end effect is that the child form cannot be moved in the background behind it's parent but this kind of UI is rare, I can't remember ever seeing it in MS Office for instance except. . . mmmno, not under any occasion.
Then again in the same MS Office opening a couple of documents renders a couple of separate top "unowned" forms so this is *the* familiar behavior users expect from every Windows application IMO.
Looping Forms collection on exit is the norm, using a single (modal) form that can be easily unloaded on exit is the case for (some) utility applications only.
The end effect is that the child form cannot be moved in the background behind it's parent but this kind of UI is rare,
Yes, now that you mention, it is rare.
I used that approach in my last program. It has one main window that shows search result (not web, but from a database) and the possibility to open some different child windows for specific results or specific data.
I guess that most programmers would have gone to a MDI environment, or to show the child windows modally.
I didn't need the child windows to be modal and in this way disallowing to make new queries on the main window (or to open a different child windows from its toolbar) while one or more child window is open.
But I didn't want, if the user clicked the main window, the child window(s) to hide behind the main. So all these child windows are modeless and owned.
They can be resized and moved, and the program remembers the last position and size, but of course, they overlap part to the main window (unless the user has more than one monitor, or the screen is really big, the main window is not maximized and the child windows are moved away from the main).
The users can see the child windows are modals dialogs if they want, but they are not.
Originally Posted by wqweto
I can't remember ever seeing it in MS Office for instance except. . . mmmno, not under any occasion.
I think all depends on the specific needs of the program, I don't think that Office needed to do that.
Edit: now I'm paying attention to the owned windows, and I see that Notepad and Wordpad, for their Find and replace dialogs, use non modal owned windows.
Last edited by Eduardo-; Jul 7th, 2020 at 04:08 PM.
Specifing an owner form controls both lifetime and z-order -- two completely unrelated things unfortunately.
The end effect is that the child form cannot be moved in the background behind it's parent but this kind of UI is rare...
A couple other effects also. Here, I'll refer to an owned form as a child form; though technically it is not a child.
Minimizing the owner form, minimizes all child forms too (if not already minimized by user). Restoring owner, restores all child forms that were not minimized before the owner was minimized. Closing the owner form, closes all child forms and customizes the QueryUnload event's UnloadMode parameter for owned forms. It's value is: vbFormOwner
The one odd thing about the z-order relationship between owner and child forms... If a child form is maximized, it will permanently hide its owner form, until that child form is restored/minimized. You can't bring the owner form to the forefront of one of its child forms.
Insomnia is just a byproduct of, "It can't be done"