Средство работы с файлами инициализации (*.ini)

Следующий набор функций обеспечивает простой интерфейс считывания и записи данных в файлы конфигурации формата ini.
Интерфейс представлен из набора функций:
GetExeDirPath Формирует путь к папке из которой была запущена вызывающая её программа.
IniCreateSection Создаёт группу параметров в ini файле.
IniDelParam Удаляет параметр
IniDelSection
Удаляет группу параметров
IniGetNum Извлекает числовое значение из ini файла.
IniGetStr Извлекает строковое значение из ini файла.
IniGetStr12
Извлекает строковое значение из ini файла, новая, более продвинутая IniGetStr
IniSetSection Выбирает группу в ini файле (для дальнейшего доступа к ней функциями чтения/записи значений)
IniSetNum Сохраняет числовое значение в ini файл.
IniSetStr Сохраняет строковое значение в ini файл.
А также вспомогательные, не рекомендуемые к употреблению
IniInit Считывает и подготавливает ini файла к извлечению и/или записи в него значений.
IniUninit Подготавливает и записывает сформированную или модифицированную таблицу значений в файл на диске.
Также замечу, в отличие от приведённых в таблице все функции имеют окончания A (для работы с ini файлами в однобайтной кодировке) и W (для работы с UNICODE ini файлами). Исключение составляют IniInit - IniWInit и IniUninit - IniUnWinit.
Также уточню что применение A или W функций никак не относится к кодировке непосредственно применяемой в файле. А зависит лишь от способа преобразования. Если в процессе чтения текста из файла символы были преобразованы к UNICODE виду - следует применять W функции.

ini файлы также могут содержать строки - комментарии, предваряемые символом ";", однако они обязательно должны располагаться на отдельных строках ini файла. (комментарии в хвостах в одной строке с чем-либо недопустимы)

Пример общей структуры ini файла:

;тут параметры без группы. Для доступа к ним вызови IniSetSection с пустым именем секции.
numval=X1234789ABCDEF
;тут "X" включает анализ значения как числа, записанного в шеснадцатиричном виде (хранение
чисел в HEX виде не предусматривается стандартом ini!).
[GENERAL]
;Это группа с именем "GENERAL". Заглавные буквы не обязательны, но являются следованием общему стилю ini формата. Также замечу что в имени раздела недопустимы "[" и "]".
strval=новая строка для записи
числовой параметр=1234

А также данный интерфейс не допускает применение в именах групп и параметров знаков "=", "[", "]", табуляций, перевода строки и возврата каретки.

Общая работа с ini файлом должна выглядеть следующим образом:

Производится по следующей схеме:
1)Открыть ini файл
2)Считать его содержимое в память, для некоторых, особенно мультибайтных форматов возможно выполнив преобразование из применённой кодировки в файле на диске в двубайтную UNICODE кодировку.
3)Проивести чтение и/или запись значений.
4)При необходимости - произвести обратное 1) преобразование и записать полученный в памяти ini файл на диск
5)Освободить задействованные переменные.
На примере кода:
#include <windows.h>
#include <stdio.h>
#include "2fapShare\ini.h"

int main(void)
{
    char path[MAX_PATH]; //путь к ini файлу
    HANDLE hIni; //указатель открытого файла

    INISECTION sect = {0}; //заполняется установщиком раздела, подаётся в функции чтения значений для выбора раздела
    size_t iniBufSz = 0; //размер памяти с загруженным образом ini файла
    char *iniBuf = 0; //буфер с образом ini файла

    char strVal[260]; //строковая переменная
    __int64 numVal; //числовая переменная

    //получаю путь к паке с exe-шникомпрограммы.
    GetExeDirPathA(path);
    //добавляю к нему имя ini-файла.
GetExeDirPath всегда завершает путь символом '\'
    strcat(path, "settings.ini");
    //открываю полученный файл (в зависимости от надобности можно открывать только на чтение или только на запись)
    hIni = CreateFile(path, GENERIC_READ | GENERIC_WRITE,  0, 0, OPEN_ALWAYS, 0, 0);
    //скармливаю парсеру для получения образа ini файла в памяти. Данный парсер - просто обёртка - помощник. Можно просто считать содержимое файла в память, однако с UNICODE файлами всё несколько сложнее т. к. там реализовано UTF-8 -> UNICODE -> UTF-8 преобразование
    iniBuf = IniInit(hIni, &iniBufSz);
    //выбираю группу параметров.
    IniSetSectionA(iniBuf, iniBufSz, "GENERAL", &sect);
    //Извлекаю строковый параметр. Тут вместо группы можно присваивать и 0, что даст чтение параметров, не относящихся к какой-либо группе
    IniGetStrA(iniBuf, iniBufSz, "strval", strVal, 260, &sect);
    //считываю числовой параметр. Да, допустимы названия параметров и с символами национальных кодировок и пробелами
    IniGetNumA(iniBuf, iniBufSz, "числовой параметр", &numVal, &sect);
   
    strcpy(strVal, "новая строка для записи");
    numVal = 1234789;
    //записываю новые значения строкового и числового параметра
    IniSetStrA(&iniBuf, &iniBufSz, "strval", strVal, strlen(strVal), &sect);
    IniSetNumA(&iniBuf, &iniBufSz, "числовой параметр", numVal, &sect);

    //суть - надо удалить старое содержимое ini-файла на диске, иначе
IniUninit не сработает как надо
    //теперь начиная с версии v1.2 это делать не обязательно, IniUninit сама затирает старый файл.
    SetFilePointer(hIni, 0, 0, FILE_BEGIN);
    SetEndOfFile(hIni);
    //выгружаю изменённый ini файл на диск. Тут заметим что в качестве размера следует подавать размер полезных данных, однако
iniBufSz содержит размер памяти (с учётом завершающего нуля)
    IniUninit(hIni, iniBuf, strlen(iniBuf));
    //закрытие ini файла и очистка буфера памяти
    CloseHandle(hIni);
    free(iniBuf);
    return 0;
}

Из примера кода хорошо заметно что IniInit и IniUninit далеки от совершенства, однако дополнительно уточню, что хоть и не рекомендую их к использованию формат их использования менять не планирую и возможные модификации будут касаться только внутренней логики, направленной на избавление от дополнительных технических манипуляций с указателями в формате приведённого примера.

Возвращаемые значения

Все функции извлечения и записи значений имеют следующую систему кодов возврата ошибок:
0  = Успех (секция определена, параметр найден, создан)
1  = успех с обрезкой (размера указанного буфера не хватило для сохранения значения указанного параметра ini)
2  = Отказ (параметр не найден, секция не найдена)
8  = Ошибка в структуре ini файла
12 = Не удалось расширить буфер (система отказала в выделении дополнительной ОЗУ)
17 = Отказ по попытке дублирования (уже есть такая секция)
22 = Аргументы функции заданы неверно
Однако функции GetExeDirPath, IniInit и IniUninit возвращают коды ошибок согласно таблице ошибок Win32, а также могут возвращать дополнительные коды ошибок Win32 через GetLastError()

Заметки

Внимание!

Требует линковки с msvcrt! Имеет проблемы с современными компиляторами от Microsoft.

Быстрая справка

ОС Наличие стандартной библиотеки Си, Поддержка WinAPI v4
DLL 2fapShare.dll
Библиотека 2fapShare.lib
Заголовок ini.h

Смотри также