Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 19.10.2013, 01:31
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию Как и куда сохранить настройки

Есть форма, в которой заполняются настройки программы.
На этой же форме есть выпадающий список, в котором хранятся разные конфигурации этих настроек. Как сохранить несколько конфигураций и загружать их, если пользователь выберет определённые.

Я знаю о существовании iniFiles. Использовать несколько штук этих файлов? Может быть какой-то класс нужен?
Ответить с цитированием
  #2  
Старый 19.10.2013, 01:36
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Может заюзать секции в ини-файле под это?
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
  #3  
Старый 19.10.2013, 01:49
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию

Секции, насколько я понял, делят логически настройки внутри одной конфигурации.
Ваше решение нарушает идеологию. Наверное.
Ответить с цитированием
  #4  
Старый 19.10.2013, 02:00
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,025
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Цитата:
Сообщение от Uniq!
Секции, насколько я понял, делят логически настройки внутри одной конфигурации...

Цитата:
Почему иногда лучше использовать INI-файлы, а не реестр?
1. INI-файлы можно просмотреть и отредактировать в обычном блокноте.

2. Если INI-файл хранить в папке с программой, то при переносе папки на другой компьютер настройки сохраняются. (Я еще не написал ни одной программы, которая бы не поместилась на одну дискету

3. Новичку в реестре можно запросто запутаться или (боже упаси), чего-нибудь не то изменить.

Поэтому для хранения параметров настройки программы удобно использовать стандартные INI файлы Windows. Работа с INI файлами ведется при помощи объекта TIniFiles модуля IniFiles. Краткое описание методов объекта TIniFiles дано ниже.

Create('d:\test.INI');
Создать экземпляр объекта и связать его с файлом. Если такого файла нет, то он создается, но только тогда, когда произведете в него запись информации.

WriteBool(const Section, Ident: string; Value: Boolean);
Присвоить элементу с именем Ident раздела Section значение типа boolean

WriteInteger(const Section, Ident: string; Value: Longint);
Присвоить элементу с именем Ident раздела Section значение типа Longint

WriteString(const Section, Ident, Value: string);
Присвоить элементу с именем Ident раздела Section значение типа String

ReadSection (const Section: string; Strings: TStrings);
Прочитать имена всех корректно описанных переменных раздела Section (некорректно описанные опускаются)

ReadSectionValues(const Section: string; Strings: TStrings);
Прочитать имена и значения всех корректно описанных переменных раздела Section. Формат : имя_переменной = значение

EraseSection(const Section: string);
Удалить раздел Section со всем содержимым

ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
Прочитать значение переменной типа Boolean раздела Section с именем Ident, и если его нет, то вместо него подставить значение Default.

ReadInteger(const Section, Ident: string; Default: Longint): Longint;
Прочитать значение переменной типа Longint раздела Section с именем Ident, и если его нет, то вместо него подставить значение Default.

ReadString(const Section, Ident, Default: string): string;
Прочитать значение переменной типа String раздела Section с именем Ident, и если его нет, то вместо него подставить значение Default.

Free;
Закрыть и освободить ресурс. Необходимо вызвать при завершении работы с INI файлом

Property Values[const Name: string]: string;
Доступ к существующему параметру по имени Name

Пример :

Код:
Procedure TForm1.FormClose(Sender: TObject);
var
IniFile:TIniFile;
begin
IniFile := TIniFile.Create('d:\test.INI'); { Создали экземпляр объекта }
IniFile.WriteBool('Options', 'Sound', True); { Секция Options: Sound:=true }
IniFile.WriteInteger('Options', 'Level', 3); { Секция Options: Level:=3 }
IniFile.WriteString('Options' , 'Secret password', Pass); 
  { Секция Options: в Secret password записать значение переменной Pass }
IniFile.ReadSection('Options ', memo1.lines); { Читаем имена переменных}
IniFile.ReadSectionValues('Options ', memo2.lines); { Читаем имена и значения }
IniFile.Free; { Закрыли файл, уничтожили объект и освободили память }
end;
©Drkb::02306
Единственное замечание, INI файлы имеют ограничение на размер файла в 64 Kb...



Добавлено позже

Пример с комбобоксом (в нём названия конфигураций)

чтение:
Код:
procedure TForm1.ComboBox1Change(Sender: TObject);
var
IniFile:TIniFile;
begin
IniFile := TIniFile.Create('cnf.ini');
try
 with IniFile do begin
Label1.Caption:= ReadString(ComboBox1.Text, 'Label1', 'lab1');
Label2.Caption:= ReadString(ComboBox1.Text, 'Label2', 'lab2');
Label3.Caption:= ReadString(ComboBox1.Text, 'Label3', 'lab3');
end;
finally
IniFile.Free;
end;
end;
запись:
Код:
procedure TForm1.Button1Click(Sender: TObject);
var
IniFile:TIniFile;
begin
IniFile := TIniFile.Create('cnf.ini');
try
 with IniFile do begin
WriteString(ComboBox1.Text, 'Label1', Edit1.Text);
WriteString(ComboBox1.Text, 'Label2', Edit2.Text);
WriteString(ComboBox1.Text, 'Label3', Edit3.Text);
end;
finally
IniFile.Free;
end;
end;

Последний раз редактировалось Alegun, 19.10.2013 в 02:43.
Ответить с цитированием
  #5  
Старый 19.10.2013, 05:31
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,098
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Цитата:
Сообщение от Alegun
Единственное замечание, INI файлы имеют ограничение на размер файла в 64 Kb...

Ну, тут вы совершенно не правы.
Ini-файл уже давно устаревшая технология.
Единственное оправдание использованию ini-файла, это когда его действительно надо править "руками". Кстати, даже в этом случае, писать его рядом с программой больше не получится, по крайней мере начиная с Vista и 2003 (ну мы же не хотим всегда запускать программу от имени администратора?).

Реестр, в общем, тоже уже устаревающая технология, которая, кроме того, теперь имеет еще и особенности, в связи с появлением x64. Хотя именно она наиболее используемая в данное время.

Еще несколько лет назад MS предложила использовать xml (не помню как он там точно должен был называться, Application.configuration.xml что ли). Но проблемы запрета записи в Program Files это не решало.

В принципе, не столько важен формат хранения (ini, xml, binary, etc), как то, где хранить настройки и другие рабочие файлы данных программы (не пользователя, хотя в некоторых случаях даже их, если это всегда один файл). Начиная, если не ошибаюсь, с Windows XP, MS рекомендует использовать для этого "папку" Application Data, путь к которой получается с помощью вызова функции SHGetSpecialFolder. Таких папок для юзера 2: одна для конкретного юзера, вторая - для всех юзеров (у них разные CSIDL'ы).

Вот пример из моей программы, которая сохраняет данные пользователя в этом месте (пользовательская AppData) (собрано из разных модулей):
Код:
const
  {$EXTERNALSYM CSIDL_COMMON_APPDATA}
  CSIDL_COMMON_APPDATA = $0023;

function GetSpecialFolderPath(CSIDL : Integer) : String;
var
  Path : PChar;
begin
  Result := '';
  GetMem(Path,MAX_PATH);
  Try
    If Not SHGetSpecialFolderPath(0,Path,CSIDL,False) Then
      Raise Exception.Create('Shell function SHGetSpecialFolderPath fails.');
    Result := Trim(StrPas(Path));
    If Result = '' Then
      Raise Exception.Create('Shell function SHGetSpecialFolderPath return an empty string.');
    Result := IncludeTrailingPathDelimiter(Result);
  Finally
    FreeMem(Path,MAX_PATH);
  End;
end;

function GetUserAppDataFolderPath : String;
begin
  Result := GetSpecialFolderPath(CSIDL_APPDATA);
end;

function GetUserFileName(AForcePath : Boolean = False) : String;
var
  APath : String;
begin
  Try
    APath := GetUserAppDataFolderPath;
    APath := IncludeTrailingPathDelimiter(APath + 'Visual.Reminder');
    If Not DirectoryExists(APath) Then
      If AForcePath Then
        If Not ForceDirectories(APath) Then
          Raise Exception.Create('Can''t force user profile path.');
  Except
    APath := ExtractFilePath(Application.ExeName);
    APath := IncludeTrailingPathDelimiter(APath);
  End;
  Result := APath + 'Reminders.vr';
end;

procedure TMainForm.SaveModel;
var
  AFileName : String;
begin
  AFileName := GetUserFileName(True);
  Try
    FModel.SaveToFile(AFileName);
  Except
    // Hide all exceptions
  End;
end;
Ответить с цитированием
  #6  
Старый 19.10.2013, 09:20
Аватар для M.A.D.M.A.N.
M.A.D.M.A.N. M.A.D.M.A.N. вне форума
Sir Richard Abramson
 
Регистрация: 05.04.2008
Сообщения: 5,505
Версия Delphi: XE10
Репутация: выкл
По умолчанию

XML или JSON пользуй.
__________________
— Как тебя понимать?
— Понимать меня не обязательно. Обязательно меня любить и кормить вовремя.


На Delphi, увы, больше не программирую.
Рекомендуемая литература по программированию
Ответить с цитированием
  #7  
Старый 19.10.2013, 14:26
Аватар для Freeman
Freeman Freeman вне форума
Местный
 
Регистрация: 05.10.2012
Адрес: Санкт-Петербург
Сообщения: 577
Версия Delphi: 6
Репутация: выкл
По умолчанию

Цитата:
Сообщение от lmikle
Еще несколько лет назад MS предложила использовать xml (не помню как он там точно должен был называться, Application.configuration.xml что ли). Но проблемы запрета записи в Program Files это не решало.
Да-да-да, давайте всё писать на Си, а еще лучше на .NET, и обязательно 4.5, чтобы пользователям качать побольше было. Нехай не расслабляются.

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

Microsoft из года в год грозится запретить ini-файлы окончательно (интересно, как?), но миллионы программистов по всему миру не могут ошибаться. Для гарантии я бы использовал не системный, а самописный разборщик ini-файлов. Вроде TMemIniFile именно его и реализует, так что его должно быть достаточно. При желании, конечно, можно и в реестре хранить, если стоит задача досадить пользователям и техподдержке.

XML-формат -- ни нашим, ни вашим: вроде и текстовый, но его и вручную редактировать трудно, и программный доступ не в пример сложнее ini-файлов. JSON попроще, конечно, но плохо воспринимается именно как формат файла, -- переносы строк в нем факультативны.

Для хранения списков в ini-файлах обычно применяют два трюка -- раздел с оглавлением и точечные имена. Примеры можно увидеть в dof- и dsk-файлах, генерируемых средой Delphi.
__________________
Не стоит путать форумы с богадельнями. © Bargest
Ответить с цитированием
Эти 4 пользователя(ей) сказали Спасибо Freeman за это полезное сообщение:
Alegun (19.10.2013), Guaho (19.10.2024), PhoeniX (19.10.2013), Uniq! (19.10.2013)
  #8  
Старый 19.10.2013, 16:23
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,025
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Цитата:
Сообщение от lmikle
Ну, тут вы совершенно не правы.
Ini-файл уже давно устаревшая технология...
Вот только хотел поёрничать на эту тему, но Freeman меня опередил, добавить нечего
Ответить с цитированием
  #9  
Старый 19.10.2013, 22:34
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию

Цитата:
Сообщение от Freeman
раздел с оглавлением и точечные имена.
Про точечные имена гугл ничего не знает.

Раздел с оглавлением я так понимаю должен выглядеть так?
Код:
[Contents]
SettName1 = "первые настройки"
SettName2 = "вторые настройки"

[Первые настройки]
a = 1
b = true

[Вторые настройки]
a = 4
b = true

Да и ещё: если мне потребуется сохранить пароль, то в каком виде я должен буду его здесь хранить?
В этом случае ini-file с его редактированием руками мне играет не на руку.

Последний раз редактировалось Uniq!, 19.10.2013 в 22:39.
Ответить с цитированием
  #10  
Старый 19.10.2013, 22:56
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,098
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Цитата:
Сообщение от Alegun
Вот только хотел поёрничать на эту тему, но Freeman меня опередил, добавить нечего


Читать умеем, да?
Когда я говорил, что ini-файлы являются устаревшей технологией, то, естественно, имелось в виду именно то, где их сохранять. На это, кстати, в конце моего поста была отсылка. Сам формат является вполне себе современным, точнее принцип хранения информации в виде пар ключ-значение. Например, почитай о такой штуке, как hBase.

Хранение пароля, это отдельная тема. Открытый пароль прекрасно виден даже в бинарном файле.

В принципе, если вероятность, что ломать серьезно не будут, то достаточно просто зашифровать любым алгоритмом (да хоть XOR'ом) и поверху закрыть Base64 (для хранения в текстовом виде). Конечно, криптостойкость такого решения оставляет желать лучшего, но она, в общем, достаточна, если ключ недоступен.

Более надежная защита - это применение ассиметричного шифрования, да еще и с подписью. При этом приватный ключ надо хранить на каком-нить съемном носителе и/или шифровать симметричным алгоритмом с паролем. Т.е. у пользователя спрашиваем пароль, им расшифровываем приватный ключ, далее с помощью приватного ключа расшифровываем информацию.

В своей программе, куски которой я приводил выше, настройки (данные пользователя, сами настройки программы храняться в реестре, т.к. особой ценности не представляют) храняться в сжатом виде (встроенный компрессов на zlib в D7). Сам файл бинарный. Но это мое решение для конкретной задачи, для другой задачи, возможно, потребуется другое решение.
Ответить с цитированием
  #11  
Старый 19.10.2013, 23:42
Аватар для Freeman
Freeman Freeman вне форума
Местный
 
Регистрация: 05.10.2012
Адрес: Санкт-Петербург
Сообщения: 577
Версия Delphi: 6
Репутация: выкл
По умолчанию

Цитата:
Сообщение от Uniq!
Про точечные имена гугл ничего не знает.
Имел в виду, что для гарантии непересечения секций при случайной правке им можно давать точечные имена с предопределенным префиксом:
Код:
[Contents]
SettName1 = "настройки.1"
SettName2 = "настройки.2"
 
[настройки.1]
a = 1
b = true
 
[настройки.2]
a = 4
b = true
В этом случае и без оглавления можно обойтись, получается... Короче, это уже на усмотрение автора, я же не знаю всех случаев предполагаемого использования.
Цитата:
Сообщение от Uniq!
Да и ещё: если мне потребуется сохранить пароль, то в каком виде я должен буду его здесь хранить?
Пароль предлагаю вообще не хранить. Пусть логин и пароль пользователи каждый раз вводят собственноручно, морально и физически подтверждая начало работы с программой. Должна же быть у них хоть какая-то ответственность?

Для отладки можно реализовать фишку с чтением пароля из ini-файла, если он вписан туда вручную. Программно пароль, соответственно, не сохранять, только читать.
__________________
Не стоит путать форумы с богадельнями. © Bargest
Ответить с цитированием
  #12  
Старый 08.09.2014, 09:57
Аватар для Uniq!
Uniq! Uniq! вне форума
Местный
 
Регистрация: 29.09.2010
Сообщения: 539
Версия Delphi: Delphi XE3
Репутация: 374
По умолчанию

Подъём темы.

Собственно в старой версии всё хранится в ini-файле. Пароли каждый раз вводятся в ручную пользователями. Но настройки переносятся, да простит меня Freeman, через визуальные компоненты

Я думаю сделать так, в ОnCreate главной формы, считать настройки в TRecord (класс для настроек уже нарисовал) из файла, который хранится в AppData. А потом через полюбившуюся мне
Код:
function Execute(aDataSet: TDataSet; aUserID, aNo: integer) : boolean;

Передавать параметры настроек куда угодно.

Возник вопрос: в зависимости от активного пользователя настройки могут быть разными. Кому-то есть доступ к одной вкладке программы, кому-то к другой.

Каждый раз лезть с Select'ом в базу не хочу, чтоб узнать какой пользователь залогинился. Значит надо создать какой-то токен внутри программы, идентифицирующий пользователя

Сейчас это просто aUserID, который получает не "-1" при удачном вводе пароля. Такого подхода достаточно? или нужно читать информацию по поводу настоящих токенов в {} скобочках и кучей символов внутри (SSID что ли...)
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 09:16.


 

Сайт

Форум

FAQ

Соглашения

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2025