Click here to Skip to main content
15,904,655 members
Articles / Programming Languages / C#
Article

An INI file enumerator class using C#

Rate me:
Please Sign up or sign in to vote.
3.67/5 (7 votes)
8 Dec 2008CPOL1 min read 68.8K   1.1K   32   11
An INI file enumerator class using C#.

Introduction

I found several snippets of code on the internet that uses the function GetPrivateProfileString from KERNEL32.DLL, but none of them offered the functionality I required. So, I created a C# class, INI, which exposes this function, utilizing the best elements from these snippets of code.

First, create a new C# Console Application. Then, add a new C# class. I called mine ReadINI.cs. Now, copy and paste the first section of code into this new class. Then, copy the second section of code into the C# Main Console Application class. Now, create a basic INI file on the root of C:\, called Test.ini.

[TestSectionHeader1]
TestKey11 = TestValue11
TestKey12 = TestValue12
TestKey13 = TestValue13
TestKey14 = TestValue14
TestKey15 = TestValue15

[TestSectionHeader2]
TestKey21 = TestValue21
TestKey22 = TestValue22
TestKey23 = TestValue23
TestKey24 = TestValue24
TestKey25 = TestValue25

[TestSectionHeader3]
TestKey31 = TestValue31
TestKey32 = TestValue32
TestKey33 = TestValue33
TestKey34 = TestValue34
TestKey35 = TestValue35

The Class

C#
using System;
using System.Text;
using System.Runtime.InteropServices;


namespace INI
{
    // Set the IniFileName class within the INI Namespace.
    public class IniFileName
    {
        //    Imports the Win32 Function "GetPrivateProfileString"
        ///   from the "Kernel32" class.
        //    I use 3 methods to gather the information. All have the same name
        //    as defind by the Win32 Function "GetPrivateProfileString"
        //
        //    First Method, Gathers the Value, as the SectionHeader and EntryKey are know.
        //
        //    Second Method, Gathers a list of EntryKey for the known SectionHeader 
        //
        //    Third Method, Gathers a list of SectionHeaders.
        

        // First Method
        [DllImport ("kernel32")]
        static extern int GetPrivateProfileString (string Section, string Key, 
               string Value, StringBuilder Result, int Size, string FileName);

        // Second Method
        [DllImport ("kernel32")]
        static extern int GetPrivateProfileString (string Section, int Key, 
               string Value, [MarshalAs (UnmanagedType.LPArray)] byte[] Result, 
               int Size, string FileName);

        // Third Method
        [DllImport ("kernel32")]
        static extern int GetPrivateProfileString (int Section, string Key, 
               string Value, [MarshalAs (UnmanagedType.LPArray)] byte[] Result, 
               int Size, string FileName);

        // Set the IniFileName passed from the Main Application.
        public string path;
        public IniFileName(string INIPath)
        {
            path = INIPath;
        }

        // The Function called to obtain the SectionHeaders,
        // and returns them in an Dynamic Array.
        public string[] GetSectionNames()
        {
            //    Sets the maxsize buffer to 500, if the more
            //    is required then doubles the size each time.
            for (int maxsize = 500; true; maxsize*=2)
            {
                //    Obtains the information in bytes and stores
                //    them in the maxsize buffer (Bytes array)
                byte[] bytes = new byte[maxsize];
                int size = GetPrivateProfileString(0,"","",bytes,maxsize,path);
                
                // Check the information obtained is not bigger
                // than the allocated maxsize buffer - 2 bytes.
                // if it is, then skip over the next section
                // so that the maxsize buffer can be doubled.
                if (size < maxsize -2)
                {
                    // Converts the bytes value into an ASCII char. This is one long string.
                    string Selected = Encoding.ASCII.GetString(bytes,0, 
                                               size - (size >0 ? 1:0));
                    // Splits the Long string into an array based on the "\0"
                    // or null (Newline) value and returns the value(s) in an array
                    return Selected.Split(new char[] {'\0'});
                }
            }
        }
        // The Function called to obtain the EntryKey's from the given
        // SectionHeader string passed and returns them in an Dynamic Array
        public string[] GetEntryNames(string section)
        {
            //    Sets the maxsize buffer to 500, if the more
            //    is required then doubles the size each time. 
            for (int maxsize = 500; true; maxsize*=2)
            {
                //    Obtains the EntryKey information in bytes
                //    and stores them in the maxsize buffer (Bytes array).
                //    Note that the SectionHeader value has been passed.
                byte[] bytes = new byte[maxsize];
                int size = GetPrivateProfileString(section,0,"",bytes,maxsize,path);

                // Check the information obtained is not bigger
                // than the allocated maxsize buffer - 2 bytes.
                // if it is, then skip over the next section
                // so that the maxsize buffer can be doubled.
                if (size < maxsize -2)
                {
                    // Converts the bytes value into an ASCII char.
                    // This is one long string.
                    string entries = Encoding.ASCII.GetString(bytes,0,
                                              size - (size >0 ? 1:0));
                    // Splits the Long string into an array based on the "\0"
                    // or null (Newline) value and returns the value(s) in an array
                    return entries.Split(new char[] {'\0'});
                }
            }
        }
        
        // The Function called to obtain the EntryKey Value from
        // the given SectionHeader and EntryKey string passed, then returned
        public object GetEntryValue(string section, string entry)
        {
            //    Sets the maxsize buffer to 250, if the more
            //    is required then doubles the size each time. 
            for (int maxsize = 250; true;maxsize *=2)
            {
                //    Obtains the EntryValue information and uses the StringBuilder
                //    Function to and stores them in the maxsize buffers (result).
                //    Note that the SectionHeader and EntryKey values has been passed.
                StringBuilder result = new StringBuilder(maxsize);
                int size = GetPrivateProfileString(section,entry,"", 
                                                   result, maxsize,path);
                if (size < maxsize -1)
                {
                    // Returns the value gathered from the EntryKey
                    return result.ToString();
                }
            }
        }
    }
}

Now, for the Main C# Console Application class.

I have included two routines for gathering information. The first is a foreach loop which is in the main body of the code. The second is commented out at the end of the code, and uses the IEnumerator function. Both produce the same results.

C#
using System;
using System.Collections;
// Set the system to using the INI Namespace within the ReadINI Class
using INI;


namespace MainApp
{
    //    Basic Console Application that will read a Common Windows INI file
    //    and display Enumerate Section Headers, Section Entries and Entries Values.
    
    class Class1
    {
                    
        // The main Console routine.
        [STAThread]
        static void Main(string[] args)
        {
            
            //    Sets the INI filename and location to be used.
            //    remember to use a double slash "\\"
            //    instead of a single for folder levels.
            IniFileName INI = new INI.IniFileName("C:\\test.ini");


            // Use the Try comman to gather the information,
            // if an error occurs then throw an exception.
            // and display the error.
            try 
            {
                // set the SectionHeader into an Dynamic Singlelevel Array.
                // the Information is gathered from the GetSectionNames
                // function INI namespace of the ReadINI Class.
                string [] SectionHeader = INI.GetSectionNames();
                                
                // Checks to see if the SectionHeader has returned null.
                if (SectionHeader != null)
                {
                // Returns a list EntryKey's for each of the SectionHeader(s).
                    foreach (string  SecHead in SectionHeader)
                    {
                        // Write the SectionHeader to the display.
                        Console.WriteLine(SecHead);
                                    
                        // Sets the Entry Dynamic Array for the each
                        // of the EntryKeys within the Section Header.
                        // If you already know the SectionHeader
                        // and just need to list the Entry's
                        // then enter the name instead of the SecHead
                        // in the GetEntrNames function.
                        // ie.  INI.GetEntryNames("TestSecHead")
                        string [] Entry = INI.GetEntryNames(SecHead);
                        // Checks to see if there's an Entry under
                        // the SectionHeader and the Entry has not returned a null
                        if (Entry != null)
                        {
                            // Enumerates a list of Keys For each of the Entry.
                            foreach (string EntName in Entry)
                            {
                                // writes to display the value for the Entry key.
                                // If you already know the SectionHeader
                                // and the Entry Key values then, replace all the code
                                // after the INIFileName line, with the following
                                // Console.WriteLine(" {0} = {1}", "TestKey", 
                                //         INI.GetEntryValue("TestSecHead","TestKey"));
                                // this will display the Value for the Entry key
                                // under the given SectionHeader.
                                Console.WriteLine(" {0} = {1}", EntName, 
                                                  INI.GetEntryValue(SecHead,EntName));
                            }
                        }
                
                    }
                }
            }
            // If an error if thrown, catch the exception error
            catch (Exception ex)
            {
                // Write the exception error to the display.
                Console.WriteLine("Error:  "+ ex);
            }
            // Waits for a "Enter" key to be used.
            // I use this like a pause button.
            Console.ReadLine();
        }
    }
}



/*    You can also use the Enumerator function to produce
 *    the same results. Here is the code if you wish to use that function.
 *    Replace the code from the "Try" at line 26 until
 *    the "console.readline()" at line 71 with this.
 * 
 *            try 
 *            {
 *                IEnumerator e = INI.GetSectionNames().GetEnumerator();
 *                while(e.MoveNext()) 
 *                {
 *                    string SectionHead = e.Current.ToString();
 *                    Console.WriteLine(e.Current);
 *                    IEnumerator d = INI.GetEntryNames(SectionHead).GetEnumerator();
 *                    while(d.MoveNext()) 
 *                    {
 *                        string EntryKey = d.Current.ToString();
 *                        Console.WriteLine("{0} = {1}", 
 *                           d.Current,INI.GetEntryValue(SectionHead,EntryKey));
 *                    }
 *
 *                }
 *            }
 *            catch (Exception ex)
 *            {
 *                Console.WriteLine( "Exception Error:  {0}", ex);
 *            }
 *            Console.ReadLine();
 * 
 * 
 *  
 * 
 */

And, that's it. Run the application to display your results.

Points of interest

I have also included the code in zip format for you. I hope this is of help to someone. This is my first posting, so please be gentle.. Can I also take this opportunity to thank all those other C# Guru's for their code snippets that helped me complete mine?

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Systems Engineer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionObservations on First Use: A Quirk, Probably of the Underlying API Pin
David A. Gray6-Sep-12 12:26
David A. Gray6-Sep-12 12:26 
GeneralMy vote of 5 Pin
bxyzst21-Mar-11 11:18
bxyzst21-Mar-11 11:18 
GeneralThe Reply Pin
Hilly_Billy22-Dec-08 0:32
Hilly_Billy22-Dec-08 0:32 
QuestionWhy use old Win32 functions when everything is built-in in a better way in .NET? Pin
Jacques Bourgeois16-Dec-08 16:31
Jacques Bourgeois16-Dec-08 16:31 
The recommended way to save initialization data in .NET is through an application configuration file, that is easily created through the settings tab in the project properties, and can be handled through the Settings class (My.Settings in VB).

It is a lot mot interesting to use for many reasons :

1a. A lot of programmers who have not deployed their application on other stations are not aware that under .NET, the Program Files directory is read-only for a user that is not administrator of the station. Since configuration files are typically stored in the same directory as the application, which is usually under Program Files, you will eventually end up with read only configuration files. The application configuration file of .NET has a built-in mechanism that copies the read-write portions of the configuration file in the users data directory which enables writes.

1b. Because of the way that feature is implemented, without any intervention from the programmer, you automatically have a mechanism where each individual user on the station has its own configuration. Try to do that wich an INI file.

2. A .NET configuration file is typed, so you are sure that a date data is a date and a numeric data is a number. You do not have that kind of data integrity with INI files.

3a. You should try not to use Windows API functions when there is something that does the same thing in .NET, and although a lot of things were missing in VS2002 and 2003, versions 2005 and 2008 have almost everything. I program professionnally on a lot of applications, some of them quite complex, and in the last 3 or 4 years, only once did I have to resort to the API to do something. .NET always have a way to do things, and it is most often better and easier to use than calling the API.

3b. Using the Windows API forces the system go get out of the managed .NET environment, using pointers and often needing type conversions since .NET and Windows are not built around the same basic types. The application is then more prone to bugs. And the application is more opened to security problems because the framework is not there to handle what happen in the call.

4. Seeing how Windows has evolved in the last few years compared to how .NET has evolved, I would feel more confident that my code will still run in future versions of .NET than it would with calls to the Windows API which might eventually be scrapped.

5. The registry is overused and one of the big reasons why Windows becomes very slow after a few months or use. It should not be used.

Jacques Bourgeois

AnswerRe: Why use old Win32 functions when everything is built-in in a better way in .NET? Pin
E. del Ayre5-Jan-09 14:40
E. del Ayre5-Jan-09 14:40 
AnswerRe: Why use old Win32 functions when everything is built-in in a better way in .NET? Pin
Dave Rock 24-Jan-12 5:54
Dave Rock 24-Jan-12 5:54 
GeneralRe: Why use old Win32 functions when everything is built-in in a better way in .NET? Pin
David A. Gray6-Sep-12 11:49
David A. Gray6-Sep-12 11:49 
QuestionA suggestion? Pin
Wendelius9-Dec-08 7:17
mentorWendelius9-Dec-08 7:17 
AnswerRe: A suggestion? Pin
Keith Vinson16-Dec-08 6:01
Keith Vinson16-Dec-08 6:01 
AnswerRe: A suggestion? Pin
TobiasP16-Dec-08 10:15
TobiasP16-Dec-08 10:15 
GeneralRe: A suggestion? Pin
David A. Gray6-Sep-12 12:05
David A. Gray6-Sep-12 12:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.