
Работа с INI-файлами на Ассемблере

Работа с INI-файлами на Ассемблере
Конфигурационные ini-файлы появились в самых первых версиях Windows. Изначально в них хранились только настройки Windows, а затем они стали использоваться для хранения параметров других приложений. Начиная с Windows 95, Microsoft объявил ini-файлы устаревшими и с тех пор предлагает использовать системный реестр для хранения всех настроек и данных программ. Лично я считаю, что приложения должны быть легко переносимыми между компьютерами, а также легко и полностью деинсталлироваться, поэтому внедрение в систему должно быть минимальным. Хранение всех настроек в ini-файле или в xml-файле в папке с программой - это, на мой взгляд, самое правильное решение, а в реестр нужно залезать только в случае крайней необходимости.
Несложная структура формата ini-файлов позволяет легко обрабатывать их программно и имеет достаточно понятный вид для чтения и изменения человеком. Работать с ini-файлами из приложения одно удовольствие: чтобы читать параметры из файла конфигурации достаточно всего трех функций GetPrivateProfileString (строковые данные), GetPrivateProfileInt (числовые данные) и GetPrivateProfileStruct (двоичные данные), а вот для записи данных обратно есть только две функции WritePrivateProfileString (строковые данные) и WritePrivateProfileStruct (двоичные данные). Отдельной функции для записи числовых значений по какой-то причине не предусмотрено. Но это все хорошо, когда вы обрабатываете ini-файл с известной структурой, когда в приложении заранее определены имена секций и названия ключей.

Пример INI-файла
А вот случай посложнее, когда в связи с особенностью работы приложения, структура ini-файла неизвестна. Или если известны имена секций, но количество и список ключей и их значений могут меняться. Парсить ini-файл с неизвестным содержимым самостоятельно - задача не самая тривиальная. При кажущейся простоте структуры, в файле могут быть комментарии, строки могут быть отформатированы разными способами, ключи и значения могут быть разделены как пробелами, так и табуляциями, да мало ли еще может быть вариантов. К счастью, разработчики WinAPI позаботились об этом, и система способна полностью парсить ini-файлы самостоятельно. Сегодня я расскажу, как на Ассемблере прочитать структуру секций, все ключи и их значения из произвольного ini-файла.
Сперва определим в сегменте данных переменные, в которые будут записаны все нужные нам значения.
Code (Assembler) : Убрать нумерацию
- section '.data' data readable writeable
- ; Список секций
- sections rb 1024
- ; Список ключений и значений
- keys rb 1024*32
- ; Имя секции
- sname rb 100h
- ; Название ключа
- key rb 100h
- ; Значение ключа
- value rb 100h

Список имен секций
Содержимое секций можно прочитать другой функцией - GetPrivateProfileSection. Она вернет список ключей и их значений в виде ASCIIZ-строк в формате "ключ=значение". Окончание списка также определяется нулевым байтом. Все комментарии удаляются, а строки, которые не соответствуют формату ini-файла, игнорируются. При этом максимальный размер полезного содержимого секции, как сказано в описании функции, не должен превышать 32 килобайта. Может быть это утверждение справедливо для более старых версий Windows, а на современных системах, как показала практика, штатными средствами совершенно спокойно обрабатываются файлы с размером секций в несколько мегабайт.

Список ключей и их значений
И вот этот список нам все-таки придется парсить самостоятельно. Имя ключа и его значение всегда разделено знаком равенства "=" без пробелов, независимо от того, как эта строчка была записана в исходном файле. Разработчики WinAPI и тут нам очень помогли. Ведь по сути все значения в ini-файле являются строками и то, как будет представлено их значение, зависит исключительно от функции, которой это значение будет запрошено. Это надо будет учитывать при ручной обработке данных из файла. Полностью код для парсинга ini-файла выглядит примерно так:
Code (Assembler) : Убрать нумерацию
- ; Получить список секций ini-файла
- invoke GetPrivateProfileSectionNames,sections,1024,ini_file
- ; Файл пустой?
- or eax,eax
- jz loc_scan_sections_done
- ; Указатель на список секций
- mov esi,sections
- loc_scan_sections:
- ; Конец списка секций?
- cmp byte [esi],0
- je loc_scan_sections_done
- ; Имя обрабатываемой секции
- mov edi,sname
- @@:
- lodsb
- stosb
- or al,al
- jnz @b
- push esi
- ;----------------------------------------------
- ; sname = имя секции
- ;----------------------------------------------
- ; Прочитать содержимое секции
- invoke GetPrivateProfileSection,sname,keys,1024*32,ini_file
- ; Секция пустая?
- or eax,eax
- jz loc_scan_keys_done
- ; Указатель на список ключений и значений
- mov esi,keys
- loc_scan_keys:
- ; Конец списка ключей?
- cmp byte [esi],0
- je loc_scan_keys_done
- ; Название ключа
- mov edi,key
- @@:
- lodsb
- cmp al,'='
- je @f
- stosb
- jmp @b
- @@:
- xor eax,eax
- stosb
- ; Значение ключа
- mov edi,value
- @@:
- lodsb
- stosb
- or al,al
- jnz @b
- ;----------------------------------------------
- ; key = название ключа
- ; value = значение ключа
- ;----------------------------------------------
- ; Следующий ключ
- jmp loc_scan_keys
- loc_scan_keys_done:
- ; Следующая секция
- pop esi
- jmp loc_scan_sections
- loc_scan_sections_done:
- ; Разбор ini-файла завершен
Еще немного полезной информации. Для того, чтобы сбросить кешированные данные на диск и сразу задействовать изменения после записи в ini-файл, надо вызвать функцию WriteProfileString или WritePrivateProfileString, передав им в качестве имени секции, наименования ключа и строки значения NULL:
Code (Assembler) : Убрать нумерацию
- ; Сбросить кешированные данные на диск
- invoke WriteProfileString, NULL, NULL, NULL
- invoke WritePrivateProfileString, NULL, NULL, NULL, ini_file
Просмотров: 3688 | Комментариев: 3

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье

user
(20.06.2016 в 19:42):
--Добавлено--
.. когда-то сделал собственную библиотечку на Си (для DOS) с реализацией аналогов Get/Wirite-PrivateProfileInt и Get/Write**String, но, как это часто бывало, попользовался ею сам всего пару раз и забросил - слишком громоздкая оказалась штука, хотя работала вполне нормально ..
С WinAPI всё гораздо проще.
.. когда-то сделал собственную библиотечку на Си (для DOS) с реализацией аналогов Get/Wirite-PrivateProfileInt и Get/Write**String, но, как это часто бывало, попользовался ею сам всего пару раз и забросил - слишком громоздкая оказалась штука, хотя работала вполне нормально ..
С WinAPI всё гораздо проще.

user
(20.06.2016 в 19:29):
Немного поразглагольствую на тему, с позволенья.
Действительно, использование собственного файла настроек выглядит предпочтительным во всех случаях, кроме, разве что, случая запуска программы с защищённого от записи носителя.
Но и в этом случае существует популярный стандартный метод работы со своим файлом настроек - его часто размещали в каталоге %WINDIR%. Особенно это было популярно во времена WIN16. Там (в %WINDIR%) обычно накапливались эти самые INI-файлы в приличных количествах от разных программ.
Такой способ тоже имеет недостаток - при запуске разных версий софта он будет писать/читать INI-файл не своей версии.
Примером может служить популярный wave-редактор CoolEdit. У этого редактора в INI-файле хранились регистрационные данные, так что головняк был постоянный.
Решалось запуском CoolEdit'а из пакетного файла, который прежде копировал свой INI из каталога программы в %WINDIR%.
Неудобство заключалось в том, что вновь сохранённые настройки перезаписывались старым файлом при следующем запуске.
Еще один неплохой способ хранения настроек в популярном файловом менеджере FAR - настройки хранятся в реестре, но их можно оттуда экспортировать в reg-файл c помощью самой программы. Для сохранения/переноса на другую машину.
В общем, идеального способа нет. Вернее, идеальным можно считать, если в программе предусмотрен выбор способа хранения своих настроек несколькими (двумя) из перечисленных способов.
Симпатичным представляется вариант, когда программа использует свой INI-файл в каталоге запуска, иначе INI-файл в каталоге %WINDIR%, иначе ключи в реестре. И есть возможность выбрать способ при сохренении настроек.
Ну, как-то так.
Действительно, использование собственного файла настроек выглядит предпочтительным во всех случаях, кроме, разве что, случая запуска программы с защищённого от записи носителя.
Но и в этом случае существует популярный стандартный метод работы со своим файлом настроек - его часто размещали в каталоге %WINDIR%. Особенно это было популярно во времена WIN16. Там (в %WINDIR%) обычно накапливались эти самые INI-файлы в приличных количествах от разных программ.
Такой способ тоже имеет недостаток - при запуске разных версий софта он будет писать/читать INI-файл не своей версии.
Примером может служить популярный wave-редактор CoolEdit. У этого редактора в INI-файле хранились регистрационные данные, так что головняк был постоянный.
Решалось запуском CoolEdit'а из пакетного файла, который прежде копировал свой INI из каталога программы в %WINDIR%.
Неудобство заключалось в том, что вновь сохранённые настройки перезаписывались старым файлом при следующем запуске.
Еще один неплохой способ хранения настроек в популярном файловом менеджере FAR - настройки хранятся в реестре, но их можно оттуда экспортировать в reg-файл c помощью самой программы. Для сохранения/переноса на другую машину.
В общем, идеального способа нет. Вернее, идеальным можно считать, если в программе предусмотрен выбор способа хранения своих настроек несколькими (двумя) из перечисленных способов.
Симпатичным представляется вариант, когда программа использует свой INI-файл в каталоге запуска, иначе INI-файл в каталоге %WINDIR%, иначе ключи в реестре. И есть возможность выбрать способ при сохренении настроек.
Ну, как-то так.

Добавить комментарий
Заполните форму для добавления комментария

Если запись в папку с программой по какой-то причине закрыта, то можно хранить настройки в пользовательской папке %USERPROFILE% или %LOCALAPPDATA%, не обязательно же сразу лезть в каталог винды. Экспорт-импорт из/в реестр тоже не панацея, при внезапно рухнувшей системе доступ к реестру может оказаться если не невозможен, то по крайней мере сильно затруднен. А если программа хранит настройки у себя в файле, то после переустановки системы или при переносе программы на другой комп достаточно будет только заново создать ярлычки для ее запуска. При хранении настроек в пользовательской папке легко решается вопрос с персональными настройками для разных учетных записей, если программа подразумевает для них какой-то индивидуальный режим работы. Резервное копирование в автоматическом режиме тоже гораздо легче делать поиском и архивированием *.ini, чем прописыванием в бэкапилке и поддержанием в актуальном состоянии списка из 100500 веток реестра. Короче, сплошные плюсы :)
Реестр must die!