using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; using System.Text; namespace INIUtilities { /// /// Provides methods for reading and writing to an INI file. /// public class IniFile { /// /// The maximum size of a section in an ini file. /// /// /// This property defines the maximum size of the buffers /// used to retreive data from an ini file. This value is /// the maximum allowed by the win32 functions /// GetPrivateProfileSectionNames() or /// GetPrivateProfileString(). /// public const int MaxSectionSize = 32767; // 32 KB #region P/Invoke declares /// /// A static class that provides the win32 P/Invoke signatures /// used by this class. /// /// /// Note: In each of the declarations below, we explicitly set CharSet to /// Auto. By default in C#, CharSet is set to Ansi, which reduces /// performance on windows 2000 and above due to needing to convert strings /// from Unicode (the native format for all .Net strings) to Ansi before /// marshalling. Using Auto lets the marshaller select the Unicode version of /// these functions when available. /// [System.Security.SuppressUnmanagedCodeSecurity] private static class NativeMethods { [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern int GetPrivateProfileSectionNames(IntPtr lpszReturnBuffer, uint nSize, string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, [In, Out] char[] lpReturnedString, int nSize, string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern int GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, IntPtr lpReturnedString, uint nSize, string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern int GetPrivateProfileInt(string lpAppName, string lpKeyName, int lpDefault, string lpFileName); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern int GetPrivateProfileSection(string lpAppName, IntPtr lpReturnedString, uint nSize, string lpFileName); //We explicitly enable the SetLastError attribute here because // WritePrivateProfileString returns errors via SetLastError. // Failure to set this can result in errors being lost during // the marshal back to managed code. [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool WritePrivateProfileString(string lpAppName, string lpKeyName, string lpString, string lpFileName); } #endregion //The path of the file we are operating on. private readonly string m_path; /// /// Initializes a new instance of the class. /// /// The ini file to read and write from. public IniFile(string path) { //Convert to the full path. Because of backward compatibility, // the win32 functions tend to assume the path should be the // root Windows directory if it is not specified. By calling // GetFullPath, we make sure we are always passing the full path // the win32 functions. m_path = System.IO.Path.GetFullPath(path); } /// /// Gets the full path of ini file this object instance is operating on. /// /// A file path. public string Path { get { return m_path; } } #region Get Value Methods /// /// Gets the value of a setting in an ini file as a . /// /// The name of the section to read from. /// The name of the key in section to read. /// The default value to return if the key /// cannot be found. /// The value of the key, if found. Otherwise, returns /// /// /// The retreived value must be less than 32KB in length. /// /// /// or are /// a null reference (Nothing in VB) /// public string GetString(string sectionName, string keyName, string defaultValue) { if (sectionName == null) throw new ArgumentNullException("sectionName"); if (keyName == null) throw new ArgumentNullException("keyName"); StringBuilder retval = new StringBuilder(IniFile.MaxSectionSize); NativeMethods.GetPrivateProfileString(sectionName, keyName, defaultValue, retval, IniFile.MaxSectionSize, m_path); return retval.ToString(); } /// /// Gets the value of a setting in an ini file as a . /// /// The name of the section to read from. /// The name of the key in section to read. /// The default value to return if the key /// cannot be found. /// The value of the key, if found. Otherwise, returns /// . /// /// or are /// a null reference (Nothing in VB) /// public short GetInt16(string sectionName, string keyName, short defaultValue) { int retval = GetInt32(sectionName, keyName, defaultValue); return Convert.ToInt16(retval); } /// /// Gets the value of a setting in an ini file as a . /// /// The name of the section to read from. /// The name of the key in section to read. /// The default value to return if the key /// cannot be found. /// The value of the key, if found. Otherwise, returns /// /// /// or are /// a null reference (Nothing in VB) /// public int GetInt32(string sectionName, string keyName, int defaultValue) { if (sectionName == null) throw new ArgumentNullException("sectionName"); if (keyName == null) throw new ArgumentNullException("keyName"); return NativeMethods.GetPrivateProfileInt(sectionName, keyName, defaultValue, m_path); } /// /// Gets the value of a setting in an ini file as a . /// /// The name of the section to read from. /// The name of the key in section to read. /// The default value to return if the key /// cannot be found. /// The value of the key, if found. Otherwise, returns /// /// /// or are /// a null reference (Nothing in VB) /// public double GetDouble(string sectionName, string keyName, double defaultValue) { string str = GetString(sectionName, keyName, ""); if (String.IsNullOrEmpty(str)) return defaultValue; double retval; if (!Double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out retval)) retval = defaultValue; return retval; } /// /// Gets the value of a setting in an ini file as a . /// /// The name of the section to read from. /// The name of the key in section to read. /// The default value to return if the key /// cannot be found. /// The value of the key, if found. Otherwise, returns /// /// /// or are /// a null reference (Nothing in VB) /// public bool GetBoolean(string sectionName, string keyName, bool defaultValue) { string str = GetString(sectionName, keyName, ""); if (String.IsNullOrEmpty(str)) return defaultValue; bool retval; if (!Boolean.TryParse(str, out retval)) { retval = defaultValue; } return retval; } #endregion #region GetSectionValues Methods /// /// Gets all of the values in a section as a list. /// /// /// Name of the section to retrieve values from. /// /// /// A containing objects /// that describe this section. Use this verison if a section may contain /// multiple items with the same key value. If you know that a section /// cannot contain multiple values with the same key name or you don't /// care about the duplicates, use the more convenient /// function. /// /// /// is a null reference (Nothing in VB) /// public List> GetSectionValuesAsList(string sectionName) { List> retval; string[] keyValuePairs; string key, value; int equalSignPos; if (sectionName == null) throw new ArgumentNullException("sectionName"); //Allocate a buffer for the returned section names. IntPtr ptr = Marshal.AllocCoTaskMem(IniFile.MaxSectionSize); try { //Get the section key/value pairs into the buffer. int len = NativeMethods.GetPrivateProfileSection(sectionName, ptr, IniFile.MaxSectionSize, m_path); keyValuePairs = ConvertNullSeperatedStringToStringArray(ptr, len); } finally { //Free the buffer Marshal.FreeCoTaskMem(ptr); } //Parse keyValue pairs and add them to the list. retval = new List>(keyValuePairs.Length); for (int i = 0; i < keyValuePairs.Length; ++i) { //Parse the "key=value" string into its constituent parts equalSignPos = keyValuePairs[i].IndexOf('='); key = keyValuePairs[i].Substring(0, equalSignPos); value = keyValuePairs[i].Substring(equalSignPos + 1, keyValuePairs[i].Length - equalSignPos - 1); retval.Add( new KeyValuePair(key, value) ); } return retval; } /// /// Gets all of the values in a section as a dictionary. /// /// /// Name of the section to retrieve values from. /// /// /// A containing the key/value /// pairs found in this section. /// /// /// If a section contains more than one key with the same name, /// this function only returns the first instance. If you need to /// get all key/value pairs within a section even when keys have the /// same name, use . /// /// /// is a null reference (Nothing in VB) /// public Dictionary GetSectionValues(string sectionName) { List> keyValuePairs; Dictionary retval; keyValuePairs = GetSectionValuesAsList(sectionName); //Convert list into a dictionary. retval = new Dictionary(keyValuePairs.Count); foreach (KeyValuePair keyValuePair in keyValuePairs) { //Skip any key we have already seen. if (!retval.ContainsKey(keyValuePair.Key)) { retval.Add(keyValuePair.Key, keyValuePair.Value); } } return retval; } #endregion #region Get Key/Section Names /// /// Gets the names of all keys under a specific section in the ini file. /// /// /// The name of the section to read key names from. /// /// An array of key names. /// /// The total length of all key names in the section must be /// less than 32KB in length. /// /// /// is a null reference (Nothing in VB) /// public string[] GetKeyNames(string sectionName) { int len; string[] retval; if (sectionName == null) throw new ArgumentNullException("sectionName"); //Allocate a buffer for the returned section names. IntPtr ptr = Marshal.AllocCoTaskMem(IniFile.MaxSectionSize); try { //Get the section names into the buffer. len = NativeMethods.GetPrivateProfileString(sectionName, null, null, ptr, IniFile.MaxSectionSize, m_path); retval = ConvertNullSeperatedStringToStringArray(ptr, len); } finally { //Free the buffer Marshal.FreeCoTaskMem(ptr); } return retval; } /// /// Gets the names of all sections in the ini file. /// /// An array of section names. /// /// The total length of all section names in the section must be /// less than 32KB in length. /// public string[] GetSectionNames() { string[] retval; int len; //Allocate a buffer for the returned section names. IntPtr ptr = Marshal.AllocCoTaskMem(IniFile.MaxSectionSize); try { //Get the section names into the buffer. len = NativeMethods.GetPrivateProfileSectionNames(ptr, IniFile.MaxSectionSize, m_path); retval = ConvertNullSeperatedStringToStringArray(ptr, len); } finally { //Free the buffer Marshal.FreeCoTaskMem(ptr); } return retval; } /// /// Converts the null seperated pointer to a string into a string array. /// /// A pointer to string data. /// /// Length of the data pointed to by . /// /// /// An array of strings; one for each null found in the array of characters pointed /// at by . /// private static string[] ConvertNullSeperatedStringToStringArray(IntPtr ptr, int valLength) { string[] retval; if (valLength == 0) { //Return an empty array. retval = new string[0]; } else { //Convert the buffer into a string. Decrease the length //by 1 so that we remove the second null off the end. string buff = Marshal.PtrToStringAuto(ptr, valLength - 1); //Parse the buffer into an array of strings by searching for nulls. retval = buff.Split('\0'); } return retval; } #endregion #region Write Methods /// /// Writes a value to the ini file. /// /// The name of the section to write to . /// The name of the key to write to. /// The string value to write /// /// The write failed. /// private void WriteValueInternal(string sectionName, string keyName, string value) { if (!NativeMethods.WritePrivateProfileString(sectionName, keyName, value, m_path)) { throw new System.ComponentModel.Win32Exception(); } } /// /// Writes a value to the ini file. /// /// The name of the section to write to . /// The name of the key to write to. /// The string value to write /// /// The write failed. /// /// /// or or /// are a null reference (Nothing in VB) /// public void WriteValue(string sectionName, string keyName, string value) { if (sectionName == null) throw new ArgumentNullException("sectionName"); if (keyName == null) throw new ArgumentNullException("keyName"); if (value == null) throw new ArgumentNullException("value"); WriteValueInternal(sectionName, keyName, value); } /// /// Writes an value to the ini file. /// /// The name of the section to write to . /// The name of the key to write to. /// The value to write /// /// The write failed. /// public void WriteValue(string sectionName, string keyName, short value) { WriteValue(sectionName, keyName, (int)value); } /// /// Writes an value to the ini file. /// /// The name of the section to write to . /// The name of the key to write to. /// The value to write /// /// The write failed. /// /// /// or are /// a null reference (Nothing in VB) /// public void WriteValue(string sectionName, string keyName, int value) { WriteValue(sectionName, keyName, value.ToString(CultureInfo.InvariantCulture)); } /// /// Writes an value to the ini file. /// /// The name of the section to write to . /// The name of the key to write to. /// The value to write /// /// The write failed. /// /// /// or are /// a null reference (Nothing in VB) /// public void WriteValue(string sectionName, string keyName, float value) { WriteValue(sectionName, keyName, value.ToString(CultureInfo.InvariantCulture)); } /// /// Writes a value to the ini file. /// /// The name of the section to write to . /// The name of the key to write to. /// The value to write /// /// The write failed. /// /// /// or are /// a null reference (Nothing in VB) /// public void WriteValue(string sectionName, string keyName, double value) { WriteValue(sectionName, keyName, value.ToString(CultureInfo.InvariantCulture)); } /// /// Writes a value to the ini file. /// /// The name of the section to write to . /// The name of the key to write to. /// The value to write /// /// The write failed. /// /// /// or are /// a null reference (Nothing in VB) /// public void WriteValue(string sectionName, string keyName, bool value) { WriteValue(sectionName, keyName, value.ToString(CultureInfo.InvariantCulture)); } #endregion #region Delete Methods /// /// Deletes the specified key from the specified section. /// /// /// Name of the section to remove the key from. /// /// /// Name of the key to remove. /// /// /// or are /// a null reference (Nothing in VB) /// public void DeleteKey(string sectionName, string keyName) { if (sectionName == null) throw new ArgumentNullException("sectionName"); if (keyName == null) throw new ArgumentNullException("keyName"); WriteValueInternal(sectionName, keyName, null); } /// /// Deletes a section from the ini file. /// /// /// Name of the section to delete. /// /// /// is a null reference (Nothing in VB) /// public void DeleteSection(string sectionName) { if (sectionName == null) throw new ArgumentNullException("sectionName"); WriteValueInternal(sectionName, null, null); } #endregion } }