Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

IniFile Class using VB.NET

0.00/5 (No votes)
21 Jan 2004 5  
A .NET control to assist in the creation and manipualtion of *.ini files

Introduction

The February 2004 edition of MSDN magazine included some example code on how to build a custom class to save your winform settings to an *.ini file. This was a great idea, but the code presented was extremely limited, and basically only offered some Getval/Setval functions and a custom function to Get/Set your form settings quickly. Further hampering this code was its reliance on the Kernel32.dll *.ini file functions (WritePrivateProfileString, GetPrivateProfileString). I searched on Google for a better solution, but found nothing.

Many programmers choose to store their custom program settings in the registry, but this defeats the XCopy deployment paradigm of .NET programs. You should be able to copy settings along with the executable and go. Microsoft would have you store this information in an XML file, but the fact is that many end users (and even many programmers) still prefer the simple to open/edit format of the venerable old *.ini file. But Microsoft left out any kind of native support for this storage solution in .NET.

I decided to write my own class to handle this function. I'm not sure why every reference to *.ini files begins and ends with accessing the Kernel32.dll. I suppose people think that this would make things faster, or perhaps more accurate or secure. But the fact is that *.ini files are ultimately text files, and there is no reason they can't be handled as such. Furthermore, I don't want to be limited by the Get/Set nature of the Kernel32.dll functions. Controls designed for .NET are feature rich in nature, and this one should be no different.

Aside from getting and setting key values, I want to able to add, remove and edit sections. I want to be able to comment/uncomment key values, or even entire sections with ease. I want to be able to move a key from section to section. I want to be able to sort my sections and keys for easy reading. I want an easy way to store my form settings. And just to top it off, let's add the ability to dump an *.ini file out to XML should I decide to take that route in the future. In short, it should be a fully featured, simple to use control. Oh, and it should be free.

Using the code

Download the attached source code, which contains both the class file, and demo project - an Ini File Editor (of course). The code for the class is heavily commented, and should be easy to understand. There is also a detailed help file included, produced by NDoc and the VB.DOC Visual Studio addin.

A simple example of the class file would look something like this:

Imports IniFile
Dim myIniFile As New IniFile("C:\Test.ini") 
    'Create an IniFile object and load the *.ini file

myIniFile.AddKey("MyKey","MyValue","MySection") 
    'Create a section, and add new key/value to it

myIniFile.Sort() 'Sort all the sections and keys

myIniFile.ToXML("C:\Test.xml") 'Save the file to XML

myIniFile.Save("C:\Test.ini") 'Save the file

Points of Interest

  • Add, Delete, Edit, Comment and UnComment Sections
  • Add, Delete, Edit, Comment, UnComment and Move Keys/Values
  • Save form settings easily
  • List all Sections
  • Dump to XML
  • Sort file

The IndexOf() and Sort() Conundrums

While speed is not of the essence in this class (I simply cannot imagine a scenario where an *.ini file is being referenced thousands of times a second, or put under any kind of heavy load situation), I did try to make things as simple and fast as possible.

My first draft of this class was scrapped completely. It was based on a single ArrayList - the contents of the file were read line-by-line into the ArrayList, and then had to meticulously examined and manipulated in order to perform the necessary functions. It worked, but the code was ugly, and every time I tried to add even the most simple feature, I found myself re-writing huge hunks of code, and introducing new bugs.

It was then that I remembered I was dealing with an object oriented based language, and I should re-think my approach. I tried to think like Microsoft. Ok, so we have an *.ini file, that's an object. That file is made up of sections, those are objects. And each section has keys/values. Those are objects too. Suddenly, this made a lot sense, and everything fell into place from there.

The main component, the IniFile, is an ArrayList. I chose this, because the ArrayList class has some powerful features that make life easy, such as Add(), RemoveAt(), and IndexOf(), as well as sorting and searching features. This made adding and manipulating sections easy. The Section object is also based on an ArrayList, since it too needs to deal with child objects, namely, the keys. And keys are simple classes, with simple name/value strings.

This posed an interesting problem for me. Consider the following example:

 Dim myAL As New ArrayList()
 myAL.Add("The")
 myAL.Add("quick")
 myAL.Add("brown")
 Dim Quick_Index = myAL.IndexOf("quick")

In the above code, Quick_Index would contain a value of 1, which is the index of the string object called "quick". Simple enough.

But now consider this - the main ArrayList of IniFile (called Sections in the source code) contains not strings, but other ArrayLists (Sections). How do I use IndexOf() when the object I'm attempting to locate isn't a string object? And can I still sort the ArrayList? This same problem continues in the Sections Object, as it also contains custom classes (keys), and not string objects.

The sorting problem was solved by creating a custom comparer. The custom comparer implements IComparer, and allows me to direct the code as to how to compare one section to another.

Public Class SectionComparer
 Implements IComparer
 Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer  
 Implements System.Collections.IComparer.Compare
   Dim s1 As String = LCase(x.Name)
   Dim s2 As String = LCase(y.Name)
   Return s1.CompareTo(s2)
   End Function
 End Class

As you can see above, the custom comparer takes the x and y objects (does x = y?) and tells the computer how to go about comparing them. Since my Section objects each have a name value, I instructed the Compare function to compare those names (in lower case, just to be safe) and returned the result of that comparison. Then in the Sort() Function, I specify which comparer to use:

 Dim mySC As SectionComparer = New SectionComparer
 Sections.Sort(mySC)

That leaves me with the IndexOf() problem. IndexOf() doesn't take a comparer object. So now what? Well, it turns out that IndexOf() uses the Equals() method of the objects it is comparing to determine if we have a match. When you are comparing custom classes however, .NET simply uses the default Equals() method, which is to say, it compares the memory address of the object. Since two objects will never share the same memory address, they will never be equal. The fix is to override the Equals() method of the class, and tell the computer how to compare equal values of our custom class, much in the same way that we did with the custom comparer. An excellent example of this is here.

The problem is, this still isn't working. Take a look at the commented code in the GetKeyIndex() function to give this a try. If you find an answer, please email me so that I can update the code, and this article.

A final note

This is the first release of this code. It needs lots of testing, and has lots of room for improvement. If you find any bugs, or have any suggestions for improvements, they are welcomed. Thanks, and I hope you find this code to be of value to you.

History

  • Initial release of source code and documentation.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here