All Windows applications need to persist some data. Here I can think of three different kinds of data. The first kind of data is normally kept into a database. An application has been created to handle that data (customers, invoices, books, authors, …). I won't talk about that kind of data in this article. The second kind of data is parameters for applications. Some of these parameters are better stored in a database (mail server, tax percentage, …) but others (like the connection string) are better kept elsewhere. The third kind of data is users settings. An application should persist some of the settings of users like forms position and size, preferred language, and so on. These could be stored into the database but I prefer to save them locally. There are advantages to persisting user's data to a central database (like the user's mobility) but in the client-server world, users do not switch from PC to PC quite often! The main disadvantage is the bad performance over slow network.
So my topic this month, as you may have already guessed, is the persistence of settings locally. I will give you some thoughts and code on four different ways of persisting that kind of data. The first method is the legacy .INI files. The second method is the Windows Registry database. The third method is XML files used with the Configuration namespace. The last, but not least, is XML used with its own library. All the code won't be listed through the article (for clarity reason). You will find a link at the bottom to download a complete project with demos of each method.
Method 1: The .INI files
This is probably the most used method as of today. These files are common since the beginning of Windows (do you remember system.ini and win.ini?). These files, very easy to edit, have a simple structure like this:
[Database]
Environment=Development
In this structure, Database is a section, Environment is a key and Development is a value. To use them from a VB.Net application, we still need to rely to the kernel32.dll and its GetPrivateProfileString and WritePrivateProfileString functions. If you have downloaded the source code for this article, you will find a small class (named INIAccess) at the bottom of the frmINI.vb file. This class contains most of the methods you need to read, write and delete sections, keys and values from a .INI file. At the top of this class, you will find the required API declarations like this one: Private Declare Unicode _
Function GetPrivateProfileString _
Lib "kernel32" _
Alias "GetPrivateProfileStringW" _
(ByVal lpApplicationName As String, _
ByVal lpKeyName As String, _
ByVal lpDefault As String, _
ByVal lpReturnedString As String, _
ByVal nSize As Int32, _
ByVal lpFileName As String) As Int32
This declaration should be familiar for anyone who used this in VB6. Things you need to take care are minimal like changing the Long of VB6 to Int32.
With the INIAccess class somewhere in your project, you can easily use it like in the following example that opens c:\test.ini and retrieves the value of the Environment key from the Database section. If this key does not exist, the message box will display a default value ("--- Not Found ---").
Dim x As New INIAccess()
MessageBox.Show(x.INIRead("c:\test.ini", _
"Database", _
"Environment", _
"--- Not Found ---"))
Method 2: The Registry
The main disadvantage of the registry is that it may not be as easy to distribute and edit as other methods. We also all have eared of corruption and problems caused by some hazardous manipulation of the registry. Another fact to consider is that values are user-based (because we are normally restricted to save values into HKEY_Current_User). If we logon as another user, we cannot have access to the values previously persisted by another user.
For example, this is the code required for you to write values into the registry (you need to Imports Microsoft.Win32 at the top of this module).
Dim k As RegistryKey
k = Registry.CurrentUser.OpenSubKey("Software\\Emoreau", True)
If k Is Nothing Then
k = Registry.CurrentUser.CreateSubKey("Software\\Emoreau")
End If
k.SetValue("Test1", "v1")
k.Close()
These lines will be saved the value into the HKEY_CURRENT_USER\Software\Emoreau name (as you can see in the screenshot). If the branch does not already exist, it will be created (because of the CreateSubKey method) . Retrieving values is as easy (download the code for the demo).
Figure 1: The values saved into the Registry.
Method 3: The Windows Forms configuration files
The .Net Framework gives us the System.Configuration namespace in which we can find the ConfigurationSettings class. This class exposes the AppSettings property that allows us to read the content of a particular section of a particular XML file. What is so particular with this file? It needs to respect a strict formatting of filename and section name. Here is an example of this file:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="env" value="test" />
<add key="cs" value="initial catalog=Pubs" />
</appSettings>
</configuration>
The first thing to know (that you cannot see from this example) is the filename. If your application is compiled to Demo.exe, the configuration file needs to be named Demo.exe.config. This file also needs to be saved into the same folder as the application (the bin folder while in development). After the filename and path constraints, you need to know that the configuration section and the appSettings must be there. The AppSettings property is looking for this exact path. The add structure must also be used (and using the proper casing as shown here). One last constraint (or complaint!) is that the file is cached during the execution. What I mean is that even if the file changes during the execution of the application, changes will be seen only when the application will be restarted.
The best part of it is the little code required to read this file. After you imported System.Configuration, you only need a simple line like this one:
MessageBox.Show(ConfigurationSettings.AppSettings("env"))
Oh sorry, have I told you that this namespace only allows you to read the file? But for application settings that are normally not modified by users, it is just perfect!
Method 4: XML files
The last method I want to talk about is the use of real XML files. If you download the demo project, you will find a complete class in it (named AppSettingsManager.vb). I cannot take credits for this class. I need to give the credits to A. Russell Jones (see How to Make Your .NET Windows Forms Configuration Files Dynamic in the references). The beauty of this class is that it includes methods such as GetValue, AddValue, KeyExists, Remove, Save to handle just about any operations on the file.
Here is a sample of the use of this class:
Dim cm = AppSettingsManager.GetManager()
' add single values
cm.AddValue("v5", "Value 5")
cm.AddValue("v6", "Value 6")
' remove single values
If cm.KeyExists("v4") Then
cm.Remove("v4")
End If
As you can see, it is really easy to use.
Conclusion
As you have seen in this article, persisting and using applications (or users) settings is really easy. I hope that you will consider using one of the methods described above to store your applications settings instead of hard coding them (because there are still people that hardcode the connection string!). Also, be sweet to your users and persist there settings like forms position and size. Why not create your own form that includes this feature and always inherits this base form so that you will write this code once for all? I am sure that you like when you open Word, Excel, Visual Studio or any other applications and find it at the same place you left it!
Here are some references related to this topic.
I hope you appreciated the topic and see you next month.