Monday, 08 September 2008
PicoSearch

Forms Revisited, and DLL Base Address

This column appeared in the July 1999 edition of EXE. I occasionally encounter relatively advanced Visual Basic programmers who still don't seem to grasp some of the concepts of manipulating forms. A quick explanation, and then a chat about the DLL Base Address setting in the Make DLL dialog box.


The first action that every new Visual Basic programmer performs is to paint a button control onto the default Form1, add a MsgBox statement to the Command1_Click event, and then run it. From this humble beginning the newbie developer then moves on to understand how to manipulate methods and properties on controls, and gradually learns the core language set. More advanced concepts such as writing ActiveX components eventually follow. It’s not unusual, however, for a developer to become so proficient with the basic usage of forms that the true nature of their architecture can be overlooked. To this end I want to start this months column by reviewing some information about Forms, one of the most fundamental building blocks of Visual Basic applications.

Forms are objects

Consider the following piece of code:

Load Form1
Form1.Show
Unload Form1

Once the Unload call has been made does all of the memory used by Form1 get freed up? The answer is not necesarily, which on the face of it is rather surprising. The reason is due to the way that forms are handled by Visual Basic. A form is actually an object that is manipulated behind the scenes by the Visual Basic engine. The best way to manipulate a form is to treat it as an object, thus:

Dim ofrmDataEntry As FrmDataEntry
Set ofrmDataEntry = New FrmDataEntry
Load ofrmDataEntry
ofrmDataEntry.Show vbModal

‘ and then...
Unload ofrmDataEntry
Set ofrmDataEntry = Nothing

When the Unload statement is executed the associated window resource is deleted but module-level variable data is still retained until all references to the form are set to Nothing. This is the way that MDI child forms are typically implemented in Visual Basic applications.

The behind the scenes manipulation that I mentioned is that Visual Basic automatically creates a global-scope object variable to cater for forms that are referenced directly by the programmer. For example if a project contains a form called Form1 and the programmer makes a call such as

Load Form1

then Visual Basic will quietly create a global variable along the lines of

Dim Form1 As Form1
Set Form1 = New Form1

so that the Form1 that the programmer is referencing is actually the object instance rather than the base class. Getting used to dealing with forms in this manner can lead to a more efficient use of memory. For example, an application that has a large number of forms will consume more memory as each of these forms are called into existence – even when they have been unloaded. By treating the forms as objects and subsequently setting the references to Nothing after use will destroy the forms and lead to less wasted memory.

Form lifecycle

The Form object exposes several important events during its lifetime:

  1. The first of these is the Initialize event which occurs when the new instance of the form object comes into existence. At this stage module-level declarations are accessible and Subs, Functions and Properties can be accessed. However the form itself will yet not be loaded into memory, and consequently nor will any child controls.

  2. The Load event occurs when the underlying window that is associated with the form is created. At this time all child controls are also created. If any preceding code makes a reference to any aspect of the form itself then the Load event will automatically trigger. Any subsequent calls to load a form are then ignored – a Load will only occur once between every invocation of the corresponding Unload call.

  3. The Activate event occurs whenever the form gains focus in relation to other forms within the same application. For example an MDI application might have a number of child windows open. As the user selects each child window in turn the Activate event for the newly-selected form will fire, as will the Deactivate event for the form that has just lost the focus. One possible gotcha here is that a form will not issue a Deactivate event if it is being unloaded from a status of having focus.

  4. The Unload event occurs just before the window is destroyed. It provides the opportunity to save any state data or to update another part of the application. After this event the form object will still be in existence, so it will revert to its post-Initialize Status. Consequently all declaration-level data will still be present, but any future reference to the actual form itself will cause a new Load event to take place which will initialise the properties of all controls.

This event comes with a parameter called Cancel which allows you to halt the unloading of the form, but this isn’t the best place to make this kind of decision. An event that occurs directly before Unload is QueryUnload which also provides information as to what originated the call to unload the form. For instance it might be appropriate to ask the users whether they really want to close a top-level window (and therefore the whole application) if they press the X in the top right-hand corner of the window, but on the other hand if Windows itself is closing then you might not feel that it is appropriate to ask.

Finally, the Terminate event is triggered just before the class instance is destroyed. After this happens all remaining module-level declarations disappear.

One final word to discuss with the lifecycle of forms is that calling the End command will immediately halt execution, forgoing any Unload or Terminate calls.

DLL Base Addresses

Moving on to a different topic, there is a phenomenon known as synchronicity in which the same event occurs at the same time or in unison. As far as I can recall at no time in my life previously has anybody ever asked me to explain what the DLL Base Address option in the compile dialog is for, and then three separate people have asked me this very question during the past few weeks. I know a sign when I’m given one.

In order to understand what the DLL Base Address is for, it is worth briefly reviewing the architecture of a Win32 process first. In both Windows NT and Windows 9x an application resides within a process, which is defined as a 4GB address space within which it and any dependent components reside. Although the specifics of this implementation vary for each platform the basic idea is that the lower 2GB is specifically used by the application and its resources, while the upper 2GB is used by the operating system. The main application executable loads first at a point close to the bottom of the 2GB map, although this initial offset varies depending on the platform. This initial address is generally assigned by the operating system. However, the various components that are called upon by the application – DLLs, OCXs, and so on – can nominate their own preferred location address. If nothing else is occupying that address at the time the component is loaded then Windows is usually happy to oblige, otherwise the memory location must be altered – an operation known as rebasing.

The options dialog that accompanies the Make Project form contains a text box in which you can enter a preferred address. The default value provided by Visual Basic is always &H11000000, so unless you change it the likelihood is that every component you produce will have the same load address. If a component doesn’t get in at the address that it requested then of course its not the end of the world, it just gets loaded into a different location. There are a couple of good reasons why developers take the time to produce a set of components with different load addresses. First of all it is to avoid the overhead that is incurred by Windows having to perform the rebasing operation. The other reason is to do with the way that Windows manages its internal memory. Within a Win32 environment there can be a sharing of code pages for in-process components among different processes when the component has been able to load at its base address. However if the code has been rebased then this cannot happen and a fresh load must take place for each new invocation by another application. In practical terms this can make the difference between the same component physically being loaded once or five times.

Base addresses are calculated on 64K boundaries, starting at 16MB (&H1000000) and going up to 2GB (&H80000000). This means that there can theoretically be up to 32,512 separate 64K addresses available to choose from. Microsoft generally recommend that you choose any of these possible values at random, and therefore avoid the default base address whenever possible. Owners of the Bruce McKinney book Hardcore Visual Basic (2nd edition, Microsoft Press) will find on the accompanying CD a utility called Address-o-matic that will randomly generate a suitable address. Corporate development teams might find it worthwhile to maintain a central register of components that have been developed and which are still in use throughout the organisation. Through this database they would be able to keep track of all base addresses currently allocated and could assign new ones accordingly.

VSVIEW Review

I've recently received a review copy of VideoSoft VSVIEW 6.0 which replaces the Printer object in Visual Basic. Support for printing has never been particularly rich in Visual Basic although the situation was improved somewhat with the inclusion of the Data Reports engine into the current version.

The VSVIEW product is shipped in the form of three components: vsPrinter, vsDraw, and vsViewPort. The most significant component is vsPrinter which "allows you to quickly and easily create documents for printing and previewing". In essence this means that you can create a document that includes columns, graphics, headers and footers, and the sort of formatting that is defined by underlying RTF or HTML descriptions. Having defined this document you can then display multiple page print previews that include zooming, panning, and thumbnails. This particular component offers a rich set of supporting methods and functions. Also in the package, the vsDraw component provides a means of producing images such as charts, diagrams, and maps, while vsViewPort is concerned with creating scrollable areas within forms, for example to implement forms that are larger than the actual window.

As third-party toolkits go I must say that I am impressed with the quality of the documentation. A 279-page manual accompanies the CD and provides a full description of all of the properties, methods, and so on. One touch that I also like it that the first page of each component description includes a list of useful information such as the component file name, the GUID, the description (as it will appear in the Components dialog in Visual Basic), the icon that will appear in the toolbox, and a general descriptive text. It's clear that a lot of effort was put into this. There are also four separate tutorial chapters, and a fair amount of sample code is scattered throughout the whole book.

VSVIEW 6.0 is priced at £199, and is available from most software resellers. My thanks to Contemporary plc for the review copy. Further details can be found at http://www.contemporary.co.uk or by phone on 01344 873434.

Copyright ©2002 Jon Perkins I, Jon Michael Perkins, hereby assert and give notice of my right under section 77 of the Copyright, Designs, and Patents Act 1988 to be identified as the author of the foregoing article.