Question

I've used the class from over here http://www.codeproject.com/Articles/1966/An-INI-file-handling-class-using-C and it works well, I tried to extend it to handle sections.

[DllImport("kernel32")]
private static extern long WritePrivateProfileSection(string section,
string val, string filePath);

public void IniWriteSection(string Section, string Value)
{
    WritePrivateProfileSection(Section, Value, this.path);
    WritePrivateProfileSection(null, null, null);
 }

I originally had a bit of trouble with data being written twice if the INI file was edited in quick succession, so added the

WritePrivateProfileSection(null, null, null);

section, and that seemed to have fixed the problem, but it's started happening again, so I guess it didn't work.

Does anyone have suggestions on what I've done wrong?

Was it helpful?

Solution

The problem is that what you're writing into the ini file is invalid. The values you write must be in the form key1=value1\0key2=value2\0. Based on your previous revisions of your original code, you're not writing anything in that form, just blobs of text.

The ini file parser will treat anything after the start of a section as part of the section. However it only considers key=value lines as valid key/value pairs. It will only replace anything up to the last valid key/value pair. Anything else beyond will be left as is.

At least... that's what I have observed... I don't know if that's the actual specification for the Windows implementation.


You have to write using the correct format. Every value you write must be paired with a key. Multiple values should be written as separate keys separated by null characters. If you have multiple lines to write, you have to write them to separate (unique) keys.

You should probably use something more like this:

public class IniFile
{
    private readonly string path;
    public IniFile(string path)
    {
        if (path == null)
            throw new ArgumentNullException("path");
        this.path = path;
    }
    public string Path { get { return path; } }

    public bool WriteSection(string section, string key, IEnumerable<string> lines)
    {
        if (String.IsNullOrWhiteSpace(section))
            return false;
        if (String.IsNullOrWhiteSpace(key))
            return false;
        return WritePrivateProfileSection(section, ToSectionValueString(key, lines));
    }

    private string ToSectionValueString(String key, IEnumerable<string> lines)
    {
        if (lines == null)
            return null;
        if (lines.Count() == 1)
            return key + "=" + lines.Single();
        return String.Join("\0",
            lines.Select((line, i) => (key + "." + i) + "=" + line)
        );
    }

    public bool WriteSection(string section, IEnumerable<KeyValuePair<string, string>> values)
    {
        if (String.IsNullOrWhiteSpace(section))
            return false;
        return WritePrivateProfileSection(section, ToSectionValueString(values));
    }

    private string ToSectionValueString(IEnumerable<KeyValuePair<string, string>> values)
    {
        if (values == null)
            return null;
        return String.Join("\0", values.Select(kvp => kvp.Key + "=" + kvp.Value));
    }

    public List<KeyValuePair<string, string>> ReadSection(string section)
    {
        var buff = new byte[SectionSizeMax];
        var count = GetPrivateProfileSection(section, buff);
        return ToSectionValueList(buff, (int)count);
    }

    private List<KeyValuePair<string, string>> ToSectionValueList(byte[] buff, int count)
    {
        return EnumerateValues(buff, count)
            .Select(v => v.Split('='))
            .Where(s => s.Length == 2)
            .Select(s => new KeyValuePair<string, string>(s[0], s[1]))
            .ToList();
    }

    private IEnumerable<string> EnumerateValues(byte[] buff, int count)
    {
        var value = new StringBuilder();
        foreach (var b in buff)
        {
            var c = (char)b;
            if (c != '\0')
            {
                value.Append(c);
            }
            else
            {
                yield return value.ToString();
                value.Clear();
            }
        }
    }

    [DllImport("kernel32")]
    private static extern bool WritePrivateProfileSection(string lpAppName, string lpString, string lpFileName);
    private bool WritePrivateProfileSection(string lpAppName, string lpString)
    {
        return WritePrivateProfileSection(lpAppName, lpString, path);
    }

    [DllImport("kernel32")]
    private static extern uint GetPrivateProfileSection(string lpAppName, byte[] lpReturnedString, uint nSize, string lpFileName);
    private uint GetPrivateProfileSection(string lpAppName, byte[] lpReturnedString)
    {
        return GetPrivateProfileSection(lpAppName, lpReturnedString, (uint)lpReturnedString.Length, path);
    }
    const uint SectionSizeMax = 0x7FFF;
}

Then write to it like this:

var lines = new List<string>();
lines.Add("...");
lines.Add("...");
// ...

ini.WriteSection("autoexec", "line", lines);

OTHER TIPS

I found a workaround, you need to remove the section you are writing to with

ini.IniWriteValue("autoexec", null, null);

and then write the your section

ini.IniWriteSection("autoexec", strAutoexecSection);

This line isn't required.

WritePrivateProfileSection(null, null, null);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top