Around the World with Visual Basic
Asynchronous Method Execution Using Delegates
Building a Progress Bar that Doesn't Progress
Calling All Operators
Create a Graphical Editor Using RichTextBox and GDI+
Creating A Breadcrumb Control
Creating a Five-Star Rating Control
Creating and Managing Secondary Threads
Data Binding Radio Buttons to a List
Deploying Assemblies
Designing With Custom Attributes
Digital Grandma
Doing Async the Easy Way
Extracting Data from .NET Assemblies
Implementing Callbacks with a Multicast Delegate
Naming and Building Assemblies in Visual Basic .NET
Programming Events of the Framework Class Libraries
Programming I/O with Streams in Visual Basic .NET
Reflection in Visual Basic .NET
Remembering User Information in Visual Basic .NET
Advanced Basics: Revisiting Operator Overloading
Scaling Up: The Very Busy Background Compiler
Synchronizing Multiple Windows Forms
Thread Synchronization
Updating the UI from a Secondary Thread
Using Inheritance in the .NET World
Using the ReaderWriterLock Class
Visual Basic: Simplify Common Tasks by Customizing the My Namespace
What's My IP Address?
Windows Forms Controls: Z-order and Copying Collections
Expand Minimize
4 out of 6 rated this helpful - Rate this topic

Visual Basic 6 and Visual Basic .NET Application Configuration Made Easy

Visual Studio .NET 2003
 

Scott Swigart
Swigart Consulting LLC.

March 2006

Applies to:

   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.


Contents

Introduction
Prerequisites
A Look at the Samples
Architecture
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?
Conclusion

Introduction

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.

Prerequisites

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.

A Look at the Samples

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.

Architecture

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.

Click here for larger image

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.

The Museum of Application Configuration

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 Magical Mystery XML Serializer

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.

xs.Serialize(configFile, settings) 

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.

configFile.Close()

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.

Hooking it Up

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.

Click here for larger image

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.

Settings, In and Out

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.

The Configurable Application

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.

What About App.Config? What about My.Settings?

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.

Conclusion

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.

Additional Resources

Calling a .NET Component from a COM Component

XmlSerializer Class

XML Serialization in the .NET Framework (Extreme XML)

 

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 scott@swigartconsulting.com.

Did you find this helpful?
(1500 characters remaining)
© 2013 Microsoft. All rights reserved.