Visual Basic 6 and Visual Basic .NET Application Configuration Made Easy
Swigart Consulting LLC.
Microsoft Visual Basic 6
Microsoft Visual Basic .NET 2003
Microsoft Visual Basic 2005
Microsoft Visual Studio .NET 2003
Microsoft Visual Studio 2005
Summary: It's common to have applications that need to load and store configuration information. Applications may expose options to the users, track recently opened files, remember previous form sizes, or more. By writing some simple Visual Basic .NET code, you can easily add this kind of configuration capability to your existing Visual Basic 6 or Visual Basic .NET applications. (17 printed pages)
Click here to download the code sample for this article.
A Look at the Samples
The Museum of Application Configuration
The Magical Mystery XML Serializer
Hooking it Up
Settings, In and Out
The Configurable Application
What About App.Config? What about My.Settings?
Applications can be so forgetful. You would think that if you used your application to browse to a folder and open a file, it would start out in that same folder the next time you wanted to open a file. You would think that if you resize a form, the next time that form opens it would automatically default to the previous size and location. And what about Tools, Options? There should be an easy way to let the user configure the application, and have the application remember those configuration settings.
It turns out that the .NET framework has great facilities for storing and retrieving structured information from disk, allowing you to easily add configuration functionality to Visual Basic .NET or even existing Visual Basic 6 applications.
In this article, you will learn how to add configuration management to an application. Included are sample Visual Basic 6 and Visual Basic .NET applications that use the configuration management functionality.
To use the code in this article, you will need to install Visual Studio 2005, or the free Visual Basic Express. These products can be installed along side of Visual Studio 6.0, Visual Studio .NET, and/or Visual Studio 2003, without impacting those products.
The sample applications contain a logon form that, if desired, can remember the user's name and password:
Figure 1. Logon form populated with configuration information
Once the user logs in, another form is displayed. This form shows the last loaded picture, and the form appears on the screen at its previous location and size.
Figure 2. Form pre-loaded and size based on saved configuration
To get started with the samples, download and extract the associated code for this article. Then, navigate into the code folder and double-click "Install.bat."
In the code folder, you will find three other folders: ConfigLib, ConfigExample, and Visual Basic 6. The ConfigLib folder contains the classes to access, load, and save the configuration information. ConfigExample is a Visual Basic .NET project that loads and saves configuration information using ConfigLib. Visual Basic 6 is a Visual Basic 6 application that also loads and saves configuration information using ConfigLib. You can run either of these applications, enter some data, and close the application. When you run the application again, you will see that it remembers any information you previously entered.
It was important that it be programmatically simple for an application to access configuration settings. To facilitate this, the application uses properties of simple classes to store settings. So if, for example, the application wants to retrieve the setting for a form's height, the code is simply Settings.ImageForm.Height. Later in this article, the construction of this Settings class will be examined in more detail, but for now it's only important to understand that the settings are stored in properties of simple classes.
I also wanted it to be easy to load and save settings, so a SettingsManager class was created to handle this aspect of configuration management.
Figure 3. Application architecture (Click on the image for a larger picture)
All setting information is saved on disk by the SettingsManager as XML. "Why XML," you ask? To understand the advantages of XML configuration files, it's worth taking a trip through the various mechanisms that windows applications have used for configuration information.
In the beginning, there were .INI files. These files stored configurations settings as text, grouped into various sections.
Example 1. Example .INI file
[MainForm] height=600 width=800
But then, along came the registry, and suddenly .INI files were so passé. Instead, settings were supposed to go into (ominous sound) The Registry. The Registry would be the repository for every configuration setting ever imagined. However, the registry has issues. If you dump configuration settings into the registry, you can't examine those settings outside of the application without internal knowledge of the application, and knowledge about where, in the registry, it decided to write its settings. There's also only one registry. If you support having different versions of your application installed side-by-side, then you have to make sure that each version of your application writes to version specific registry locations. If not, v1.0 and v2.0 of your applications will sneak in when no one is looking, and goof with each others settings.
When the application stores settings in the registry, uninstalling the application is also more complicated. The uninstaller not only needs to delete the application files, but it needs to find and delete the registry settings as well. If your registry is like mine, it's full of configuration settings lost and forgotten from uninstalled applications. Even so, Visual Basic 6 and Visual Basic .NET make it easy to read and write settings to specific locations in the registry with the GetSetting and SaveSetting functions.
This brings us to today, where XML configuration files are completely in-vogue. They allow you to have a deep hierarchy for configuration settings, and they are stored as a simple file. This has a number of advantages. First, uninstalling the application is easy, as the uninstaller can just delete the file. Second, the file can be manually read or modified with any simple text or XML editor. Third, it's easy to store application wide settings in one file (database connection strings, for example), and individual user settings in different files. Finally, it's easy for different versions of an application to keep their settings separate, as the settings can be in application specific folders.
Example 2. Example XML configuration file
<?xml version="1.0"?> <Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <LogonForm> <UserName>Scott Swigart</UserName> <Password>password</Password> <Remember>true</Remember> </LogonForm> <ImageForm> <Width>6996</Width> <Height>6156</Height> <Top>3432</Top> <Left>5964</Left> <PicturePath>C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Winter.jpg</PicturePath> </ImageForm> </Settings>
The down side of XML configuration has traditionally been that XML files are somewhat cumbersome to create and parse, at least compared with simple-to-use functions like SaveSetting and GetSetting. However, the .NET Framework now makes it trivial to save the contents of a class as XML, and reload the class from XML.
The .NET Framework contains a class called the XmlSerializer that lives to turn classes into XML (called "serialization"), or re-create classes from XML (called deserialization). The first time I saw this class, I thought "Hmm. Magic." Let's use it to do some tricks.
Trick #1, XML Serialization
Imagine that you have a class that holds some configuration information for a form, such as the form's location and size:
Example 3. Class containing configuration information
Public Class FormInfo Public Top as Integer Public Left as Integer Public Width as Integer Public Height as Integer End Class
You might want to save this information out as a configuration file. With Visual Basic 6, you need code that explicitly writes each field out, and you have to know the names of the fields in advance. With Visual Basic .NET, however, you can just use the magic of the XmlSerializer. Uttering "Abra Kadabra" is optional.
Example 4. Serializing a class as XML
Public Sub Save(ByVal settings As FormInfo, ByVal fileName As String) Dim xs As New XmlSerializer(GetType(settings)) Dim configFile As Stream = File.Open(fileName, _ FileMode.Create, FileAccess.Write) xs.Serialize(configFile, settings) configFile.Close() End Sub
This function will take in a FormInfo class, like the one shown in Example 3, convert it to XML, and save the XML into an specific file. Let's examine this code one line at a time:
Dim xs As New XmlSerializer(GetType(settings))
The first line creates an instance of an XmlSerializer. An XmlSerializer can examine a class, extract the name and values of the class's properties, and emit the information as XML. Then, when XmlSerializer is created, it is provided the type of the object that it will be serializing.
Dim configFile As Stream = File.Open(fileName, FileMode.Create, _ FileAccess.Write)
The next line opens a file for writing. The contents of the serialized class will be written to this disk file.
The XmlSerializer is then used to convert the contents of a class to XML, and write the results to the file opened in the previous line.
Finally, the disk file is closed. Abra Kadabra, you have XML:
Example 5. FormInfo class serialized as XML
<FormInfo> <Width>800</Width> <Height>600</Height> <Top>34</Top> <Left>59</Left> </FormInfo>
Trick #2, Deserializing XML
Making the rabbit disappear into a string of angle brackets is interesting, but it's only half of the magic. For Trick #2, we'll pull the rabbit back out of the disk.
To recreate the FormInfo class from the saved XML, you again use the XmlSerializer class, but this time, you call the Deserialize method:
Example 6. Creating a class from XML.
Public Function Load(ByVal fileName As String) As FormInfo Dim xs As New XmlSerializer(GetType(FormInfo)) Dim configFile As Stream = File.Open(fileName, FileMode.Open, _ FileAccess.Read) Dim settings As FormInfo = xs.Deserialize(configFile) configFile.Close() Return settings End Function
Again, let's look at this one line at a time.
Dim xs As New XmlSerializer(GetType(Settings))
The first line creates an instance of the XmlSerializer, and lets it know what type of class it will be creating from the XML file.
Dim configFile As Stream = File.Open(fileName, FileMode.Open, _ FileAccess.Read)
The second line opens the disk file that contains the XML.
Dim settings As FormInfo = xs.Deserialize(configFile)
The third line is where the interesting stuff happens. Here, the XmlSerializer reads the XML file, creates the Destination class, populates it with data from the file, and returns the class instance. You now have a new class, recreated from the XML file.
configFile.Close() Return settings
The file is closed, and the class is returned.
To add configuration management to my applications, I began by creating a Visual Basic .NET class library project. This would hold the classes that store the application configuration information.
Figure 4. Creating a project for configuration management (Click on the image for a larger picture)
Once this project was created, I added classes to hold the actual configuration information. For the samples, I created a hierarchy of classes to hold the configuration settings. There's a root class called Settings. It contains instances of other classes that contain the settings for specific forms. These classes use the Visual Basic.NET COM Class template so that they can be used from Visual Basic.NET or Visual Basic 6. If you are using Visual Studio 2005, the COM Class template is included. If you are using Visual Basic Express, copy ComClass.zip from the code download for this article to "My Documents\Visual Studio 2005\Templates\ItemTemplates\Visual Basic."
Example 7. Root Settings class
<ComClass(Settings.ClassId, Settings.InterfaceId, Settings.EventsId) > _ Public Class Settings #Region "COM GUIDs" Public Const ClassId As String = "BFE852CB-EC87-4B67-AD4A- 213D8A25C50F" Public Const InterfaceId As String = "4CBC86FE-FB0B-47EF-ADE5- 7EDD8FD80B83" Public Const EventsId As String = "DE095353-B42B-433C-9A74- 8713DC6B8A3B" #End Region Public Sub New() MyBase.New() End Sub Private _logonForm As New LogonForm Public Property LogonForm() As LogonForm Get Return _logonForm End Get Set(ByVal Value As LogonForm) _logonForm = Value End Set End Property Private _imageForm As New ImageForm Public Property ImageForm() As ImageForm Get Return _imageForm End Get Set(ByVal Value As ImageForm) _imageForm = Value End Set End Property End Class
You can see that the Settings class contains instances of LogonForm and ImageForm classes. These classes hold the settings for the logon form, and the form with the picture.
Example 8. Class to hold logon form settings
<ComClass(LogonForm.ClassId, LogonForm.InterfaceId, LogonForm.EventsId) > _ Public Class LogonForm #Region "COM GUIDs" Public Const ClassId As String = "6EAC0114-F315-4CBB-8321- BCFB38A9F84D" Public Const InterfaceId As String = "F111DA82-C3D0-4481-A994- C254B209F228" Public Const EventsId As String = "EBFCD422-0A76-45F8-9813- 77435949918B" #End Region Public Sub New() MyBase.New() End Sub Private _userName As String = "" Public Property UserName() As String Get Return _userName End Get Set(ByVal Value As String) _userName = Value End Set End Property Private _password As String = "" Public Property Password() As String Get Return _password End Get Set(ByVal Value As String) _password = Value End Set End Property Private _remember As Boolean = False Public Property Remember() As Boolean Get Return _remember End Get Set(ByVal Value As Boolean) _remember = Value End Set End Property End Class
If you look at these classes, you can see that they just contain simple properties. There's no application logic in these classes. They're just a place where the application can store configuration information. The last class for configuration settings holds the information for the image form.
Example 9. Class to hold image form settings
<ComClass(ImageForm.ClassId, ImageForm.InterfaceId, ImageForm.EventsId)> _ Public Class ImageForm #Region "COM GUIDs" Public Const ClassId As String = "547A8B7E-FB9E-434F-91B4- 40E15635AA17" Public Const InterfaceId As String = "5AFBD593-9076-4804-9D79- 652062C12245" Public Const EventsId As String = "AE995B5F-5325-4071-A55C- 06DFF4F0A8A7" #End Region Public Sub New() MyBase.New() End Sub Private _width As Integer Public Property Width() As Integer Get Return _width End Get Set(ByVal Value As Integer) _width = Value End Set End Property Private _height As Integer Public Property Height() As Integer Get Return _height End Get Set(ByVal Value As Integer) _height = Value End Set End Property Private _top As Integer Public Property Top() As Integer Get Return _top End Get Set(ByVal Value As Integer) _top = Value End Set End Property Private _left As Integer Public Property Left() As Integer Get Return _left End Get Set(ByVal Value As Integer) _left = Value End Set End Property Private _picturePath As String Public Property PicturePath() As String Get Return _picturePath End Get Set(ByVal Value As String) _picturePath = Value End Set End Property End Class
This completes the classes for the configuration information that this application needs to store.
Next, the application needs a way to load and save these configuration classes as XML. For this task, I created a SettingsManager class.
Example 10. The SettingsManager class
Imports System.Environment Imports System.Xml.Serialization Imports System.IO <ComClass(SettingsManager.ClassId, SettingsManager.InterfaceId, SettingsManager.EventsId) > _ Public Class SettingsManager #Region "COM GUIDs" Public Const ClassId As String = "6FD6B8D1-87E8-4A1B-ABE9- 20984CF41C11" Public Const InterfaceId As String = "2454F7C6-C5A8-4AB2-AB5F- 6C6D01815337" Public Const EventsId As String = "00542DC3-7670-4E77-B0A5- 08B9582A13F9" #End Region Public Sub New() MyBase.New() End Sub Public Sub Save(ByVal settings As Settings, ByVal fileName As String) Dim xs As New XmlSerializer(GetType(Settings)) Dim configFile As Stream = File.Open(fileName, FileMode.Create, _ FileAccess.Write) xs.Serialize(configFile, settings) configFile.Close() End Sub Public Function Load(ByVal fileName As String) As Settings Dim xs As New XmlSerializer(GetType(Settings)) Dim configFile As Stream = File.Open(fileName, FileMode.Open, _ FileAccess.Read) Dim settings As Settings = xs.Deserialize(configFile) configFile.Close() Return settings End Function Public Function GetSpecialFolder(ByVal folderID As String) As String Dim specialFolder As SpecialFolder specialFolder = System.Enum.Parse(GetType(SpecialFolder), folderID) Return System.Environment.GetFolderPath(specialFolder) End Function End Class
With the SettingsManager class, you can save your settings out to an XML file, or load your settings in from an XML file. The SettingsManager also has a GetSpecialFolder function that you can use to get the path to special folders such as My Documents, Program Files, and so on. The SettingsManager class is also callable from Visual Basic .NET or Visual Basic 6 because it also exposes itself as a COM object.
At this point the configuration infrastructure is in place. The only thing left to do is use it from the application.
Example 11. Loading configuration information from Visual Basic 6
Private SettingsFile As String Private Sub Form_Load() On Error GoTo eh Dim sm As New ConfigLib.SettingsManager SettingsFile = sm.GetSpecialFolder("ApplicationData") & "\settings.xml" Set Settings = sm.Load(SettingsFile) With Settings.LogonForm txtUserName.Text = .UserName chkRemember.Value = IIf(.Remember, Checked, Unchecked) If .Remember Then txtPassword.Text = .Password End If End With Exit Sub eh: Set Settings = New ConfigLib.Settings End Sub
In the form load event, the application first determines the path to the settings file. In this case, the application is looking for the settings in the ApplicationData special folder. This is a good place to store user settings as the ApplicationData folder is a user specific folder. This will insure that each user's settings are separate. For a list of available special folders, see the Environment.SpecialFolder enumeration.
Once the path to the settings is determined, the application creates an instance of the SettingsManager, and uses it to load the settings. The settings are loaded into a global Settings variable so that they are available throughout the application. Then, the individual settings are used. For example, Settings.LogonForm.UserName returns the name that this user last used to log in.
If the settings file is not found, a blank Settings object is created. The application can store setting in this new object, and any settings are saved when the application exits.
Example 12. Saving settings when the Visual Basic 6 application exists.
Private Sub Form_Unload(Cancel As Integer) With Settings.LogonForm .UserName = txtUserName.Text .Remember = (chkRemember.Value = Checked) If .Remember Then .Password = txtPassword.Text End If End With Dim sm As New ConfigLib.SettingsManager sm.Save Settings, SettingsFile End Sub
When the main form is closed, data from the form's controls are copied into the Settings object. The SettingsManager class is then used to save these settings back out to disk. Other forms in the application retrieve their settings from the Settings class when they are loaded, and store their settings back in the Settings class when they unload.
If you're already programming in Visual Basic .NET, you may be familiar with the app.config file. This is a file that you can add to your project for storing application settings. On the surface, it may seem that app.config is all you need for configuration settings, at least for Visual Basic .NET applications. However, app.config is only intended for certain types of configuration information.
There are a number of different types of configuration data. At the lowest level, you have user configuration information. Although the same application is running, it may be configured differently depending on which user is using it. If one user sets specific fonts, colors, or form sizes and locations, those settings should be independent of what another user configures. User configuration data should be stored in a separate file for each user, and this file should be in the ApplicationData special folder.
There is also application configuration information. This is data that is global to the application, regardless of which user is running the application. For example, the application might connect to a particular SQL server database regardless of the current user. If the application configuration is expected to be changed by a user, then it should be stored in the CommonApplicationData special folder.
The app.config file is also a location where application wide settings can be stored. In fact, certain settings for .NET applications must go into app.config. For example, if you want your application to bind to a specific version of the .NET framework, or use a newer version of a DLL than what it was compiled against, that information must go into app.config. The problem with app.config is that it must be in the same folder as the application executable. This means, it's probably under the Program Files folder. Settings that the user can change should not be stored under the Program Files folder, as this requires the user to run the application with administrative privileges. Settings in app.config are therefore reserved for settings that only an administrator would be expected to change.
Finally, Visual Studio 2005 introduces My.Setting for Visual Basic applications, which gives you another location to store configuration information. My.Settings is great for .NET applications, but it's not COM callable. This means that you would end up needing to wrap every setting which was stored in My.Settings. In the end, creating your own setting class, as shown in this article, provides the same benefits.
Applications frequently need to store and retrieve application configuration information or user specific settings. In the past, application settings were typically stored in INI files or the system registry. Today, XML files are the most popular format for storing configuration information. The .NET framework makes it simple to store and retrieve configuration data from XML files due to its ability to serialize an object hierarchy to XML, or recreate an object hierarchy from XML. This means that even though the settings are saved as XML, your application need never deal with the XML directly, and can instead work with simple objects and their properties. Since Visual Basic .NET is capable of creating COM objects, this configuration system can be used equally well from Visual Basic 6 applications.
About the author
Scott Swigart www.swigartconsulting.net, spends his time consulting with companies on how to best use today's technology and prepare for tomorrows. Along this theme, Scott is a proud contributor to the Visual Basic Fusion site, as it offers information and tactics of real use for Visual Basic developers who want to build the most functionality with the least effort. Scott is also a Microsoft MVP, and co-author of numerous books and articles. If you have any question or comments about this article, feel free to send e-mail to email@example.com.