using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using System.Linq; namespace Common { public class IniAPI { #region INI文件操作 /* * 針對INI文件的API操作方法,其中的節點(Section)、鍵(KEY)都不區分大小寫 * 如果指定的INI文件不存在,會自動創建該文件。 * * CharSet定義的時候使用了什么類型,在使用相關方法時必須要使用相應的類型 * 例如 GetPrivateProfileSectionNames聲明為CharSet.Auto,那么就應該使用 Marshal.PtrToStringAuto來讀取相關內容 * 如果使用的是CharSet.Ansi,就應該使用Marshal.PtrToStringAnsi來讀取內容 * */ #region API聲明 /// <summary> /// 獲取所有節點名稱(Section) /// </summary> /// <param name="lpszReturnBuffer">存放節點名稱的內存地址,每個節點之間用\0分隔</param> /// <param name="nSize">內存大小(characters)</param> /// <param name="lpFileName">Ini文件</param> /// <returns>內容的實際長度,為0表示沒有內容,為nSize-2表示內存大小不夠</returns> [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern uint GetPrivateProfileSectionNames(IntPtr lpszReturnBuffer, uint nSize, string lpFileName); /// <summary> /// 獲取某個指定節點(Section)中所有KEY和Value /// </summary> /// <param name="lpAppName">節點名稱</param> /// <param name="lpReturnedString">返回值的內存地址,每個之間用\0分隔</param> /// <param name="nSize">內存大小(characters)</param> /// <param name="lpFileName">Ini文件</param> /// <returns>內容的實際長度,為0表示沒有內容,為nSize-2表示內存大小不夠</returns> [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern uint GetPrivateProfileSection(string lpAppName, IntPtr lpReturnedString, uint nSize, string lpFileName); /// <summary> /// 讀取INI文件中指定的Key的值 /// </summary> /// <param name="lpAppName">節點名稱。如果為null,則讀取INI中所有節點名稱,每個節點名稱之間用\0分隔</param> /// <param name="lpKeyName">Key名稱。如果為null,則讀取INI中指定節點中的所有KEY,每個KEY之間用\0分隔</param> /// <param name="lpDefault">讀取失敗時的默認值</param> /// <param name="lpReturnedString">讀取的內容緩沖區,讀取之后,多余的地方使用\0填充</param> /// <param name="nSize">內容緩沖區的長度</param> /// <param name="lpFileName">INI文件名</param> /// <returns>實際讀取到的長度</returns> [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, [In, Out] char[] lpReturnedString, uint nSize, string lpFileName); //另一種聲明方式,使用 StringBuilder 作為緩沖區類型的缺點是不能接受\0字符,會將\0及其后的字符截斷, //所以對於lpAppName或lpKeyName為null的情況就不適用 [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, uint nSize, string lpFileName); //再一種聲明,使用string作為緩沖區的類型同char[] [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, string lpReturnedString, uint nSize, string lpFileName); /// <summary> /// 將指定的鍵值對寫到指定的節點,如果已經存在則替換。 /// </summary> /// <param name="lpAppName">節點,如果不存在此節點,則創建此節點</param> /// <param name="lpString">Item鍵值對,多個用\0分隔,形如key1=value1\0key2=value2 /// <para>如果為string.Empty,則刪除指定節點下的所有內容,保留節點</para> /// <para>如果為null,則刪除指定節點下的所有內容,並且刪除該節點</para> /// </param> /// <param name="lpFileName">INI文件</param> /// <returns>是否成功寫入</returns> [DllImport("kernel32.dll", CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] //可以沒有此行 private static extern bool WritePrivateProfileSection(string lpAppName, string lpString, string lpFileName); /// <summary> /// 將指定的鍵和值寫到指定的節點,如果已經存在則替換 /// </summary> /// <param name="lpAppName">節點名稱</param> /// <param name="lpKeyName">鍵名稱。如果為null,則刪除指定的節點及其所有的項目</param> /// <param name="lpString">值內容。如果為null,則刪除指定節點中指定的鍵。</param> /// <param name="lpFileName">INI文件</param> /// <returns>操作是否成功</returns> [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool WritePrivateProfileString(string lpAppName, string lpKeyName, string lpString, string lpFileName); #endregion #region 封裝 /// <summary> /// 讀取INI文件中指定INI文件中的所有節點名稱(Section) /// </summary> /// <returns>所有節點,沒有內容返回string[0]</returns> public static string[] ReadSectionNames(string iniPath) { uint MAX_BUFFER = 32767; //默認為32767 string[] sections = new string[0]; //返回值 //申請內存 IntPtr pReturnedString = Marshal.AllocCoTaskMem((int)MAX_BUFFER * sizeof(char)); uint bytesReturned = IniAPI.GetPrivateProfileSectionNames(pReturnedString, MAX_BUFFER, iniPath); if (bytesReturned != 0) { //讀取指定內存的內容 string local = Marshal.PtrToStringAuto(pReturnedString, (int)bytesReturned).ToString(); //每個節點之間用\0分隔,末尾有一個\0 sections = local.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries); } //釋放內存 Marshal.FreeCoTaskMem(pReturnedString); return sections; } /// <summary> /// 獲取INI文件中指定節點(Section)中的所有條目(key=value形式) /// </summary> /// <param name="section">節點名稱</param> /// <returns>指定節點中的所有項目,沒有內容返回string[0]</returns> public static string[] ReadAllItems(string iniPath, string section) { //返回值形式為 key=value,例如 Color=Red uint MAX_BUFFER = 32767; //默認為32767 string[] items = new string[0]; //返回值 //分配內存 IntPtr pReturnedString = Marshal.AllocCoTaskMem((int)MAX_BUFFER * sizeof(char)); uint bytesReturned = IniAPI.GetPrivateProfileSection(section, pReturnedString, MAX_BUFFER, iniPath); if (!(bytesReturned == MAX_BUFFER - 2) || (bytesReturned == 0)) { string returnedString = Marshal.PtrToStringAuto(pReturnedString, (int)bytesReturned); // v1.2 modify 2016-10-21 support # remark. items = (from item in returnedString.Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries) where item.StartsWith("#") == false select item).ToArray(); } Marshal.FreeCoTaskMem(pReturnedString); //釋放內存 return items; } /// <summary> /// 獲取INI文件中指定節點(Section)中的所有條目的Key列表 /// </summary> /// <param name="section">節點名稱</param> /// <returns>如果沒有內容,反回string[0]</returns> public static string[] ReadAllItemKeys(string iniPath, string section) { string[] keys = new string[0]; const int SIZE = 1024 * 200; if (string.IsNullOrEmpty(section)) { throw new ArgumentException("必須指定節點名稱", "section"); } char[] chars = new char[SIZE]; uint bytesReturned = IniAPI.GetPrivateProfileString(section, null, null, chars, SIZE, iniPath); if (bytesReturned != 0) { // v1.2 modify 2016-10-21 support # remark. keys = (from key in new string(chars).Split(new char[] { '\0' }, StringSplitOptions.RemoveEmptyEntries) where key.StartsWith("#") == false select key).ToArray(); } chars = null; return keys; } /// <summary> /// 讀取INI文件中指定KEY的字符串型值 /// </summary> /// <param name="section">節點名稱</param> /// <param name="key">鍵名稱</param> /// <param name="defaultValue">讀取為空時返回的值</param> /// <returns>讀取到的值</returns> public static string ReadItemValue(string iniPath, string section, string key, string defaultValue = "") { string value = defaultValue; const int SIZE = 1024 * 10; if (string.IsNullOrEmpty(section)) { throw new ArgumentException("必須指定節點名稱", "section"); } if (string.IsNullOrEmpty(key)) { throw new ArgumentException("必須指定鍵名稱(key)", "key"); } StringBuilder sb = new StringBuilder(SIZE); uint bytesReturned = IniAPI.GetPrivateProfileString(section, key, value, sb, SIZE, iniPath); if (bytesReturned != 0) { value = sb.ToString(); } sb = null; return value; } /// <summary> /// 在INI文件中,將指定的鍵值對寫到指定的節點,如果已經存在則替換 /// </summary> /// <param name="section">節點,如果不存在此節點,則創建此節點</param> /// <param name="items">鍵值對,多個用\0分隔,形如key1=value1\0key2=value2</param> /// <returns></returns> public static bool WriteItems(string iniPath, string section, string items) { if (string.IsNullOrEmpty(section)) { throw new ArgumentException("必須指定節點名稱", "section"); } if (string.IsNullOrEmpty(items)) { throw new ArgumentException("必須指定鍵值對", "items"); } return IniAPI.WritePrivateProfileSection(section, items, iniPath); } /// <summary> /// 在INI文件中,指定節點寫入指定的鍵及值。如果已經存在,則替換。如果沒有則創建。 /// </summary> /// <param name="section">節點</param> /// <param name="key">鍵</param> /// <param name="value">值</param> /// <returns>操作是否成功</returns> public static bool WriteValue(string iniPath, string section, string key, string value) { if (string.IsNullOrEmpty(section)) { throw new ArgumentException("必須指定節點名稱", "section"); } if (string.IsNullOrEmpty(key)) { throw new ArgumentException("必須指定鍵名稱", "key"); } if (value == null) { throw new ArgumentException("值不能為null", "value"); } return IniAPI.WritePrivateProfileString(section, key, value, iniPath); } /// <summary> /// 在INI文件中,刪除指定節點中的指定的鍵。 /// </summary> /// <param name="section">節點</param> /// <param name="key">鍵</param> /// <returns>操作是否成功</returns> public static bool DeleteKey(string iniPath, string section, string key) { if (string.IsNullOrEmpty(section)) { throw new ArgumentException("必須指定節點名稱", "section"); } if (string.IsNullOrEmpty(key)) { throw new ArgumentException("必須指定鍵名稱", "key"); } return IniAPI.WritePrivateProfileString(section, key, null, iniPath); } /// <summary> /// 在INI文件中,刪除指定的節點。 /// </summary> /// <param name="section">節點</param> /// <returns>操作是否成功</returns> public static bool DeleteSection(string iniPath, string section) { if (string.IsNullOrEmpty(section)) { throw new ArgumentException("必須指定節點名稱", "section"); } return IniAPI.WritePrivateProfileString(section, null, null, iniPath); } /// <summary> /// 在INI文件中,刪除指定節點中的所有內容。 /// </summary> /// <param name="section">節點</param> /// <returns>操作是否成功</returns> public static bool EmptySection(string iniPath, string section) { if (string.IsNullOrEmpty(section)) { throw new ArgumentException("必須指定節點名稱", "section"); } return IniAPI.WritePrivateProfileSection(section, string.Empty, iniPath); } #endregion public static Dictionary<string, string> ToDictionary(string iniPath, string section, char split = '=') { Dictionary<string, string> dic = new Dictionary<string, string>(); foreach (var item in ReadAllItems(iniPath, section)) { int index = item.IndexOf(split); if (index > 0) // 確保Key長度大於0 { string key = item.Substring(0, index); if (dic.ContainsKey(key) == false) { dic.Add(key, item.Substring(index + 1)); } } } return dic; } #endregion } }