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")
myIniFile.AddKey("MyKey","MyValue","MySection")
myIniFile.Sort()
myIniFile.ToXML("C:\Test.xml")
myIniFile.Save("C:\Test.ini")
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.
| You must Sign In to use this message board. |
|
|
 |
|
 |
Hi there, your code is really nice. I've modified it to work on my projects now. Am using VB.NET ... INI editor codes are so limited.
I need some help though ... my INI files have blanks and comments which I really need to keep. Your Save function doesn't support blanks and comments.
Do you have newer code that supports this? Can you provide me an overview on coding this ... maybe I can add it myself? Thanks.
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Thanks Tony. Sorry, I haven't touched this code in YEARS, so not, unfortunately it's not updated. But it's free and open source, so please feel free to modify it however you wish. I'm sure the functionality you are requesting would be fairly simple to implement. All I ask is that you leave my name somewhere if you distribute the code. Thanks!
-Todd Davis (toddhd@gmail.com)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Todd, Thanks for the great code. I used to write my applications using VC6, rather than VB6, because of the versatillity. VB was easier but not versatile. The ini class which I used to use was found right here in CodeProject. Now I tried the VB express and I found the NET Framework quite good. Now VB is still easy to code and yet versatile. I am definitely moving to VB. I would like to know if it is possible to use your class inside the project as a class or module so it will be compiled inside the final .exe file.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
I was going to have to write this class myself! Storing settings in plain text is a whole lot better for debugging purposes than another class I use, which stores serialized objects.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
The reason why using *.ini files begins and ends with accessing the Kernel32.dll is due to file contention. If you use the system.io namespace to access the file, when you try to write to the file if it is opened by another process your application will encounter an exception.
|
| Sign In·View Thread·PermaLink | 1.00/5 |
|
|
|
 |
|
|
 |
|
 |
Wanted to ask exactly the same 
The class is a great piece of code (much more flexible then using interop), but it does miss some features like adding and removing comments for keys and sections (commenting the keys themselves works however) which Todd has implemented in his CInifile class.
I've also fixed some bugs here and there and added generic lists instead of arraylists, but still I can't wait for the next code update 
Great work Todd!
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
 | Thanks!  TabathaLewis | 4:22 10 Nov '06 |
|
 |
I needed a way to read an ini file the .net way and this example provided above and beyond what was needed. Thanks for contributing your expertise!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi All,
I need some favor from you. I am using VB6.
How can I store only file xtension in a variable. Like if file name is rajeev.txt or rajeev.indd, then I want to store txt or indd in a variable.
How can I print a file. A file rajeev.indd is opened, then how can I print it to generate PS file.
Please send me an example so that I can use that.
Thanks Rajeev Kumar
Hi Everyone!!!!!!!!!!!
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
|
 |
|
 |
Great work with this class Todd. I haven't gotten the chance to try it out yet.
However, searching through the web, I came across the Nini .NET Configuration Library. Have you heard of it?
http://nini.sourceforge.net/
It looks like a good library implementation, has documentation & good user manual, offers examples in C# & VB.NET, also supports .NET XML, and Windows registry, and supports the Mono open source .NET platform too.
I'm going to try it out. It's a good alternative with more features.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
This code is great for what I need it for but I am having troubles getting the GetSections list into an indexed array. If I do succeed in getting anything at all, it is usually [workspace name].Section or [workspace name].Key
The following code produces the error: An unhandled exception of type 'System.InvalidCastException' occurred in microsoft.visualbasic.dll
Additional information: Cast from type 'Section' to type 'String' is not valid.
@ the aryGames(lngCounter) line
Dim iniGFile As New IniFile(GamesPath, False) Dim iniGSections As ArrayList = iniGFile.GetSections Dim aryGames() As String For lngSection As Long = 0 To iniGSections.Count - 1 aryGames(lngSection) = iniGSections(lngSection) Next
The File at GamesPath does have one section titled "Nightfire" successfully indexed at 0.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Sorry for the problems. This code is very old at this point. I have actually been rewriting this entire class quite extensively, and adding many new features. It should work more intutively and smoothly now. The new version should be out in a few weeks as time allows.
-Todd Davis (toddhd@gmail.com)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Sounds great! I did get this one to work using the enumerator but, it caused the code to clutter up and get complex. I'll definitely be looking forward to your new class.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
hi,
I have an INI file by name ABC.ini in system directory.
it consists of section and keyvalues.
if i open ini file it will like below
[ABC] Servername="DEF" Database="GHI"
when use the GetPrivateProfileString Kernel32
Private Declare Function GetPrivateProfileString Lib "kernel32" Alias _ "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal _ lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString _ As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
by using this i am unable to getservername(i.e; DEF).
So,please suggest me how sort this problem.
With Regards, IT SOLUTIONS FREE
|
| Sign In·View Thread·PermaLink | 4.50/5 |
|
|
|
 |
|
 |
check out this thread http://www.codeguru.com/forum/archive/index.php/t-189920.html it will so you how to use the API's in VB.Net
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
You had a good and useful idea but even if the article seems to have a good feeling, the coding style is really poor and can lead to a lot of problems: - It catches any exception and shows them in a dialog box - uses a lot of VB-specific features such as the runtime methods checking, this is not a good practice. - uses a lot of VB-specific functions that have their counterparts in the framework, this is not good too.
Try to convert it to C#...
paco
|
| Sign In·View Thread·PermaLink | 1.50/5 |
|
|
|
 |
|
|
 |
|
 |
I'm having trouble getting this to work. I can't create a new INI file at all. I select New and then try to create a section, an error is thrown and I'm done..
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Hello,
i am not sure if i could find it or if its really not possible, but how can i get the values of a specified key in a given section?
greetz from germany
Harry
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I wrote a short function to do that. Just add it at the bottom of the IniFile.vb file, right above the line that says "End Class". Here's the function:
'Added by GM to simply get Value, given a Section and a Key - 9.16.2005 Public Function GetValue(ByVal SectionName As String, ByVal KeyName As String) As String Dim ThisSection As Section = GetSection(SectionName) If ThisSection Is Nothing Then Return False Dim ThisKey As Key = GetKey(KeyName, SectionName) If Not (ThisKey Is Nothing) Then Return ThisKey.Value Else Return "" End If End Function
Then you can simply get your value like this:
Dim myIni As New IniFile.IniFile("C:\Work\myIniFile.ini") myString = myIni.GetValue("Section Name", "Key Name")
|
| Sign In·View Thread·PermaLink | 3.50/5 |
|
|
|
 |
|
 |
Dear gentlemen, I'm writing a small software in VB.Net for my O2 Handheld (using Visual Basic)and I have problems as follows: 1- Input Data into a table (e.g. 7 rows and 7 columns) 2- Save the information (inputed into the cells) into a file (may be text file) 3- If I want I can read the information (in any cells)and put it in a Control (e.g. ComboBox) Note: I don't want to use Ms Access, SQL, or the same database. I want to write my database myself. Can I use the controls of VB.Net as DataGrid? Can I use the DataTable and other objects? Pls. help me, Thank You, Ta Phuong Ky
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Hi Ta Phuong Ky,
This is probably not the best place to ask this question, and I don't think the INI class is going to help you. It sounds like the best idea for you would be to serialize the data from the datagrid (or whatever source you are using). I would suggest that you ask this same question at this website: http://www.experts-exchange.com
There you will find some of the greatest minds in programming who can attempt to help you with this or any other programming questions you may have.
Good luck on your quest. In the meantime, you can Google on "ISERIALIZEABLE" to learn more about serialization. -Todd
-Todd Davis (toddhd@gmail.com)
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
I noticed that the key value is not being set when I save a form using SaveFormSettings, and the section already exists in the ini file.
In the AddKey method, the key value is being passed in, but it only gets set if the key does not exist. Here is a revised AddKey method that fixes the problem. Revised part is between the asterisks (*).
Thanks for an otherwise fine utility class! Made my life easier!
Public Function AddKey(ByVal KeyName As String, ByVal KeyValue as ...... ... ... ...
If Not GetKey(KeyName, SectionName) Is Nothing Then
'**Added by kuhnsmadhouse ******************************* Dim MyKey As Key MyKey = GetKey(KeyName, SectionName) MyKey.Value = KeyValue '******************************************************** Return False 'verify that the key does *not* exist End If
Dim ThisKey As New Key(KeyName, KeyValue, IsCommented) ...... ... ... End Function
kuhnsmadhouse
|
| Sign In·View Thread·PermaLink | 3.33/5 |
|
|
|
 |
|
|