What unloads the modules when a vb6 program terminates - vb6

The proper way to end a vb6 program is to do something like this :
Dim frm As Form
For Each frm In Forms
Unload frm
Next frm
end
That takes care of forms, what takes care of modules in memory?

There is no need to explicitly unload modules in VB6. They are unloaded automatically when the last form is unloaded. The language doesn't support references to standard modules at all, only to the (global) functions and variables defined therein. Since you can't reference the module, you can't unload them either.

Don't use End, you don't need to (ever). Then all memory gets freed properly.
Even using End, memory should be freed automatically. There was a rumor that some class instances are forgotten and don't get terminated correctly, despite reference counting. Thus, it's been established as best practice to set all object instances to Nothing explicitly (especially, but not limited to instances allocated in modules). I've never seen any confirmation that this is actually true (it might still be, though!).

If by "module" you mean static .BAS modules (they're all modules: Forms, Classes, UserControls, etc.) you don't need to "unload" them because they're static.
Most other module types are dynamically loaded. In the case of Forms typically via the predeclared global reference variable with the same name and type as the Form class (yes, a Form is a kind of class).

Note that if you use global variables then you should check if any of them need to get cleaned up. If so you should have a CleanUp method of your module and call that during the unload event of your main form. Another gotcha is sometimes the ORDER of how you unload things is important. By doing an explicit cleanup you can control that.
In VB6 you should rarely use END. If you have any circular references the program will remain as a process chewing up resource causing various strange bugs when you fire it up again. With COM objects is very easy to in inadvertently setup a chain of object thats is circularly linked.
End exists as a compatibility holdover from previous version of QuickBASIC and Visual BASIC. It did not start causing major problems until VB 4.X introduced the ability to create classes. It started gaining attention in 5.X.
Prior to this one of the only ways to make this happen in VB 3.X is to have two forms set references to each other.

The method of Unloading ALL MODULES on exit had resolved a funny bug for retrieving an exit code with API call ExitProcess()

Related

Executable loads same dll as already loaded dll

I'm about to start making major modification to my project and I just want to clarify something as I think my design maybe somewhat complicated.
I have an executable that loads a dll, lets call this dll1, this then loads dll2.
The executable also loads dll2.
What I'm asking is do I have two instances of dll2's global and static member variables, does the second load of dll2 happen or can an execuable only ever load 1 of dll2 even if dll2 was loaded by a different dll?
I know I should only have one copy of dll2's code in memory this is fine. It is the global and static variables I'm interested in.
You can only have one instance of any particular DLL loaded per process.
You can of course load different instances of the same DLL, this is practice is not common, but it is technically possible. Have a try with Process Explorer. See the snapshot below.

Prism shell regions not populated when hosted in an MFC CView

We are working on a new application that uses C#, WPF4, Prism4 and MEF. The application comprises a main shell window which defines a grid with some splitters and five regions, and a number of separate modules that contribute UI functionality in to the various regions through view discovery and view injection. All standard stuff, nothing exotic. The application is very basic and everything works fine, i.e. the modules correctly contribute their views in to the shell's regions at run time.
We also have a requirement to host the same shell (and UI from the contributing modules) in to a large legacy MFC application we have. Here's where we're having problems. The WPF/MFC interop code (using HwndSource) seems to be working fine, as evidenced by the fact that the shell gets displayed correctly as a child of the parent MFC CView and has basic functionality like a grid splitter that is defined in the shell itself. However, none of the regions in the shell are being populated with views defined in the Prism modules. Debugging shows that the modules are indeed being loaded, however, the IRegionManager instance that gets injected in to each module contains zero regions for the module to add its views in to. It's as if Prism is not aware that the shell defines any regions at all, consequently, attempts to add views in to these "non-existent" regions fail.
We derived a new custom bootstrapper class that our MFC code calls the Run() method on. This bootstrapper class is identical to its equivalent in the standalone application (which works fine), the only difference is that we no longer override the InitializeShell() method, we just rely on the base class implementation. Typically this method is overridden to set the Application.Current.MainWindow to the shell and then show the shell, however, in our case there is no current application because we're hosted inside an MFC app. Various attempts at overriding the Bootstrapper's run functionality to yield control back to the MFC app to display the shell at the appropriate time have failed (failed in the sense that the shell's regions are still unpopulated, but the shell still displays).
Has anybody successfully used Prism 4 (specifically, got shell regions populated) within an MFC application? Any advice on how to host a Prism-enabled shell with regions in an MFC CView and have the MFC application initiate the bootstrapping process would be appreciated. Thanks.
Okay, I figured it out. Painstakingly stepping through the Prism code, I came across a method on the RegionManager called OnSetRegionNameCallback(). This method conditionally calls CreateRegion() based on the result of another method call, IsInDesignMode(). If we are not in design mode, a region is created, otherwise no region is created. Closer examination of IsInDesignMode revealed that three separate tests are made to determine if we are in "design mode" or not, and if any of them are true, it is considered that we are in design mode. One of these checks was if Application.Current == null. Of course, in the context of an MFC application, Application.Current is indeed null, so the determination (erroneously) was that we were in design mode, and therefore no regions were ever created.
Once I realized this, further internet searches showed a number of other people have run in to the same problem. Indeed, there is even an issue logged in the Prism section of CodePlex (work item #3552) relating to this exact issue dating back to January 2009. Contributors to this work item also suggests a work around of creating a dummy application just to pass the "are we in design mode?" check. Please reference the work item for more details. I implemented a similar workaround and was then able to successfully host my Prism-enabled shell and its contributing modules in an MFC CView in an MFC application.
Thank you to those who blazed this trail before me and identified and proposed a workaround. You have saved me a lot of time!

debugging a dynamically loaded executable

I've been handed an application to support, and I'm trying to figure out how to do it. I do have the source, and can make some changes, but I obviously don't want to completely change the architecture of the application.
The app is in a VS2010 solution composed of 9 different projects. The main one is a Windows Form application, but it spins off others in other threads.
Here's the difficulty. Even though the different projects are parts of the same solution, they are separate executables, not DLLs. When the main program starts one of the other projects, it does so by creating a new process, setting the filename of the executeable, the startup arguments and other assorted parameters into the process.StartInfo object, and then calls process.Start().
How can I set breakpoints and debug subordinate executables? I can't attach to them until they are loaded, but they don't get loaded until process.Start() is called, and by then it's too late. Is there a method call I can insert into the main program to get it to load the executable (so I can set breakpoints in it) before it actually begins execution?
Thanks.
Are you able to recompile the other executables? If so, have you tried putting DebugBreak in suitable places? (or _asm int 3).
You can't load the process (usefully), since by definition it will be run in a different addres space from the one you are debugging before it starts.
One simple solution could be adding a call to DebugActiveProcess function to the "main" function of every process which participates in your application.

Can I safely rename/move a dll that is already loaded by a process?

I have a dll that is loaded and file-locked by a process, and I would like to update it with a newer version. I'm looking for an alternative to terminating the process to release the file lock before updating the dll. It is okay that the existing live processes still uses the old version, as long as newly instantiated ones pick up the new logic.
It seems that I can simply rename/move the dll and the live process still seems to work well. Is it safe to do this? If the dll's code has already been loaded into the process then why does it still need to lock the dll?
It is not always ok to move all dll's used by any random application. Some applications, like asp.net, use a shadow copy concept where they actually copy the dll and use the copy leaving you free to modify the original. In the case of asp.net, if you modify the original asp.net will automatically spool up a new app domain using the new dll and gracefully shut down the old one.
If the application you're referring to has a lock on the dll, then you can't safely change it.
It depends on your dll/application. For example, dll may use shared memory, or implement inter-process communication. New dll version may implement it differently. So once new instance will start, you'll have two conflicting versions in memory.
So it's not safe in general case, though in your particular case it may be OK.

CComPtr CoCreateInstance() fails

I have a COM .dll registered successfully with regsvr32 but somehow CoCreateInstance() fails to create one of its interfaces. Is there a freeware tool which can determine the reason for the failure?
First of all, check the return value of the CoCreateInstance() call. Second, you can use a tool like Regmon or Process Monitor to see what registry lookup fails. This way, you can quickly determine what exactly wasn't registered the way you'd expect it to be.
If your com dll is implemented in C++ & has debug info, you could also try debugging with MSVC to step into CoCreateInstance.
My guess is that you missed associating your class with one of the interfaces properly. I've done that a number of times by mistake. If you're using ATL you need to make sure your implementing class derives from the interface & also you have added COM_INTERFACE_ENTRY(I____) for your interface in the COM_MAP:
BEGIN_COM_MAP(CFileHelper)
COM_INTERFACE_ENTRY(IFileHelper)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IStream)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()
Or maybe the GUID differs between the IDL file and what's in the implementing C++ file. MSVC6 has an annoying bug where if the wizard fails to create a new class because of file permissions (e.g. some of the files it wants to change are read-only) but it has already added a GUID to the IDL file, and you fix the file permissions and go to do it again, there will be an inconsistency in the GUIDs and it's a pain to catch this. In an ideal world, the GUID definitions would reside ONLY in one file and then you wouldn't have to worry about this.
If not that, sometimes there are weird errors regarding marshaling & apartments but that only pops up if you've got multiple threads and are sending interface pointers across thread or interface boundaries.

Resources