Файлы инициализации

Аватар пользователя @mabu опубликовал

Практически любое программное обеспечение способно запоминать (сохранять) своё состояние. Например, внешний вид и размеры окон, язык, имя пользователя. Файлы инициализации — один из самых простых способов. Файлы с расширением «.ini» широко распространены не только в мире Windows, но и в других системах, например, php.ini.

Появились с самых первых версий Windows. В версии Windows 1.01 это был только файл WIN.INI. В Windows 3.0 добавился файл SYSTEM.INI. А затем их количество начало расти быстро и бесконтрольно.

Начиная с Windows 95, INI‐файлы считаются устаревшими и в качестве замены им корпорация Microsoft предлагает использовать системный реестр. Тем не менее INI‐файлы продолжают использоваться как приложениями других производителей, так и компонентами ОС от Microsoft. Например, файл boot.ini используется в Windows при загрузке.

Хотя INI‐файлы и приобрели популярность в Windows, они могут использоваться в любой ОС. Несложная структура этого формата позволяет легко обрабатывать их программно и имеет достаточно понятный вид для чтения и изменения людьми.

Формат файла

Не существует официальной спецификации формата, есть лишь общие принципы.

Формат INI‐файла очень прост: файл разделён на секции (или разделы), в каждой секции может находиться произвольное число записей вида «ключ=значение». Секции указываются в квадратных скобках. Имена ключей в разных секциях могут совпадать.

В INI‐файлах предусмотрены комментарии — это строки, начинающиеся с точки с запятой.

[секция_1]
параметр1=значение1
; Это комментарий
параметр2=значение2
; Иногда значения отсутствуют 
параметр3=

[секция_2]
параметр1=значение1
параметр2=значение2

Достоинства и недостатки

Достоинства:

Недостатки прямо вытекают из текстового формата и старых версий Windows и оставлены для совместимости:

Корпорация Microsoft рекомендует для хранения настроек в новых версиях программ использовать реестр.

Функции для работы с INI‐файлами

Существуют специальные функции, предназначенные для чтения и записи INI‐файлов, они лежат в библиотеке kernel32.dll:

Эти функции работают со строками и существуют в двух вариантах: в ANSI‐версии и в юникодной версии, однако запись и чтение INI‐файлов в юникоде будет работать только если файл уже был ранее создан в юникоде (кодировка UTF-16 LE). То есть создать юникодный INI‐файл функцией WritePrivateProfileString нельзя, но можно создать сторонними программами, например, блокнотом. Это поведение оставлено для совместимости со старыми версиями программ.

GetPrivateProfileString

Получает строковое значение ключа из INI‐файла.

Declare Function GetPrivateProfileString( _
    ByVal lpAppName As LPCTSTR, _
    ByVal lpKeyName As LPCTSTR, _
    ByVal lpDefault As LPCTSTR, _
    ByVal lpReturnedString As LPWSTR, _
    ByVal nSize As DWORD, _
    ByVal lpFileName As LPCTSTR _
)As DWORD

Параметры

lpAppName:
Имя секции для получения параметра. Если этот параметр равен NULL, то функция возвратит все секции в файле.
lpKeyName:
Имя ключа. Если этот параметр равен NULL, то функция возвратит все включи в данной секции.
lpDefault:
Значение по умолчанию, которое возвратит функция, если ключ не будет найден. Если параметр равен NULL, то значением по умолчанию считается пустая строка "". Не следует указывать здесь строку с завершающими пустыми символами.
lpReturnedString:
Указатель на буфер (строку), куда функция запишет значение ключа.
nSize:
Размер передаваемого в функцию буфера в символах.
lpFileName:
Имя INI‐файла. Если имя не содержит полный путь к файлу, то функция будет искать его в папке Windows.

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

Функция возвращает количество скопированных символов в буфер не включая нулевой символ.

Для получения дополнительных сведений об ошибке можно вызвать функцию GetLastError.

GetPrivateProfileInt

Если значение в файле инициализации представлено числом, то его проще получить функцией GetPrivateProfileInt.

Declare Function GetPrivateProfileInt( _
	ByVal lpAppName As LPCTSTR, _
	ByVal lpKeyName As LPCTSTR, _
	ByVal nDefault As INT_, _
	ByVal lpFileName As LPCTSTR _
)As UINT

Параметры

lpAppName:
Имя секции для получения параметра.
lpKeyName:
Имя ключа.
nDefault:
Значение по умолчанию, которое возвратит функция, если ключ не будет найден.
lpFileName:
Имя INI‐файла. Если имя не содержит полный путь к файлу, то функция будет искать его в папке Windows.

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

Функция возвращает число, представленное в INI‐файле. Если ключ не найден, будет возвращено значение по умолчанию.

WritePrivateProfileString

Сохраняет или удаляет значение в INI‐файле.

Declare Function WritePrivateProfileString( _
	ByVal lpAppName As LPCTSTR, _
	ByVal lpKeyName As LPCTSTR, _
	ByVal lpString As LPCTSTR, _
	ByVal lpFileName As LPCTSTR _
)As WINBOOL

Параметры

lpAppName:
Имя секции для записи.
lpKeyName:
Имя ключа. Если этот параметр будет равен NULL, то вся секция вместе со всеми ключами будет удалена.
lpString:
Строка, которая будет записана как значение ключа. Если этот параметр равен NULL, то ключ будет удалён.
lpFileName:
Имя INI‐файла. Если имя не содержит полный путь к файлу, то функция будет искать его в папке Windows. Если файл не существует, то он будет создан в кодировке ANSI. Значение будет записано в юникодной кодировке только если файл уже существует в юникодной кодировке, иначе значени ключа перед записью будет сконвертировано в кодировку ANSI.

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

Если функция выполнилась успешно, то возвращаемое значение не ноль.

При ошибке функция возвращает ноль. Для получения дополнительных сведений об ошибке можно вызвать функцию GetLastError.

Примеры

Далее будут примеры использования с учётом юникода, а значит, программа будет корректно читать и записывать русские буквы.

Получение значения ключа

Создай в блокноте текстовый файл «configuration.ini» и сохрани его в кодировке Unicode (UTF-16):

; Параметры пользователя
[Пользователь]
Имя=Алексей

[Окно]
; Ширина и высота окна
Width=640
Height=480

Исходный код для чтения файла тоже необходимо сохранить в юникодной кодировке, например, в UTF-8 или UTF-16.

#define unicode
#include "windows.bi"

Const SectionWindow = "Окно"
Const KeyWidth = "Width"
Const KeyHeight = "Height"
Const DefaultValue = "0"
Const BufferSize As Integer = 255

' Полный путь к файлу
Dim FileName As WString * (MAX_PATH + 1) = Any
FileName = ExePath() & "\configuration.ini"

' Значение из INI‐файла, которое мы получим
Dim Value As WString * (BufferSize + 1) = Any ' + 1 под завершающий нулевой символ

' Получить значение ключа Width
GetPrivateProfileString(SectionWindow, KeyWidth, DefaultValue, @Value, BufferSize, FileName)

Print KeyWidth; " = "; Value

' Получить значение ключа Height 
GetPrivateProfileString(SectionWindow, KeyHeight, DefaultValue, @Value, BufferSize, FileName)

Print KeyHeight; " = "; Value

Получение всех секций и ключей

Чтобы получить список всех секций, нужно в функцию GetPrivateProfileString вместо имени секции передать NULL. Функция заполнит буфер списком из всех секций, разделённых символом с кодом 0. Аналогично для списка всех ключей в разделе.

#define unicode
#include "windows.bi"

Const SectionWindow = "Окно"
Const KeyWidth = "Width"
Const KeyHeight = "Height"
Const DefaultValue = NULL
Const BufferSize As Integer = 32000 - 1

' Печать результата
Sub PrintResult(ByRef ValueList As WString)
	Dim pItem As WString Ptr = @ValueList
	Dim ItemLength As Integer = Len(*pItem)
	
	' До тех пор, пока элемент больше нуля
	Do While ItemLength > 0
		Print *pItem
		pItem = @pItem[ItemLength + 1]
		ItemLength = Len(*pItem)
	Loop
End Sub


' Полный путь к файлу
Dim FileName As WString * (MAX_PATH + 1) = Any
FileName = ExePath() & "\configuration.ini"

' Все секции INI‐файла
Dim AllSections As WString * (BufferSize + 1) = Any ' + 1 под завершающий нулевой символ

GetPrivateProfileString(Null, Null, DefaultValue, @AllSections, BufferSize, FileName)
PrintResult(AllSections)

' Получение всех ключей в секции Окно
Dim AllKeys As WString * (BufferSize + 1) = Any ' + 1 под завершающий нулевой символ

GetPrivateProfileString(SectionWindow, Null, DefaultValue, @AllKeys, BufferSize, FileName)
PrintResult(AllKeys)

Запись в файл

Попробуем сменить имя пользователя:

#define unicode
#include "windows.bi"

Const SectionUser = "Пользователь"
Const KeyUser = "Имя"
Const NewUserName = "Саша"

' Полный путь к файлу
Dim FileName As WString * (MAX_PATH + 1) = Any
FileName = ExePath() & "\configuration.ini"

' Функция возращает значение WinBool
Dim Result As WinBool = WritePrivateProfileString(SectionUser, KeyUser, NewUserName, FileName)
If Result Then
    Print "Запись удалась"
End If

Удаление ключа

Чтобы удалить ключ из секции, необходимо записать NULL в значение параметра.

Удалим ключ «Имя» из секции «Пользователь»:

#define unicode
#include "windows.bi"

Const SectionUser = "Пользователь"
Const KeyUser = "Имя"

' Полный путь к файлу
Dim FileName As WString * (MAX_PATH + 1) = Any
FileName = ExePath() & "\configuration.ini"

' Функция возращает значение WinBool
Dim Result As WinBool = WritePrivateProfileString(SectionUser, KeyUser, NULL, FileName)
If Result Then
    Print "Параметр «Имя» удалён"
End If

Удаление секции

Аналогично удалению ключа из файла конфигурации удаляются целые секции. Записываем NULL в имя ключа:

#define unicode
#include "windows.bi"

Const SectionUser = "Пользователь"

' Полный путь к файлу
Dim FileName As WString * (MAX_PATH + 1) = Any
FileName = ExePath() & "\configuration.ini"

' Функция возращает значение WinBool
Dim Result As WinBool = WritePrivateProfileString(SectionUser, NULL, NULL, FileName)
If Result Then
    Print "Секция «Пользователь» удалёна"
End If

Не забываем сохранять исходный код в кодировке UTF-8 или UTF-16, а сам INI‐файл в юникоде (UTF-16).

Литература