Навигация
Главная »  Delphi 

Способы сохранения и загрузки параметров программного обеспечения (документация)


Источник: articles
Корнейчук Михаи
В этой статье речь пойдет о способах сохранения и загрузки параметров программного обеспечения. Из своего личного опыта я могу твердо сказать, что это не так просто, как кажется многим. Как Вы уже успели заметить, крупные программные продукты используют для хранения своих параметров исключительно системный реестр. Напротив, разработчики программного обеспечения, относящие его к freeware, предпочитают конфигурационные файлы с расширением "ini" (далее "ini-файлы"). Почему же дело обстоит именно так? Мы рассмотрим два этих способа более подробно, а так же поговорим о внедрении определенных средств защиты ini-файлов.

Системный реестр - один из самых надежных, но отнюдь не самый безопасный способов хранения параметров. Данные в нем располагаются в виде иерархической структуры, что облегчает поиск нужного раздела, но тем самым увеличивает риск удаления данных. Угрозой потери данных, находящихся в реестре, кроме неосторожных действий самих пользователей, могут являться сбои в операционной системе microsoft windows. Но это уже другая история.

Для работы с системным реестром в borland delphi предусмотрен модуль registry, который содержит класс tregistry.
Код:uses registry;
.....
var
r: tregistry;
begin
r := tregistry.create;
............
r.free;
end;

Рассмотрим принципы сохранения и загрузки данных, используя системный реестр, на небольшом примере, который будет содержать две процедуры getcaption и savecaption.
Код:
procedure getcaption;
var
r: tregistry;
begin
r := tregistry.create;
r.rootkey := hkey_local_machine;
{ Открытие ключа. Параметр true означает,
что при отсутствии ключа он автоматически создается }
r.openkey('softwaretest', true)
{Записываем параметр}
r.writestring('formcaption', form1.caption);
{Закрываем ключ}
r.closekey;
r.free;
end;

Код:
procedure savecaption;
var
r: tregistry;
begin
r := tregistry.create;
r.rootkey := hkey_local_machine;
r.openkey('softwaretest', true)
{ Устанавливаем заголовок формы, используя ранее сохраненную строчку }
form1.caption := r.readstring('formcaption');
r.closekey;
r.free;
end;
Процедура savecaption сохраняет заголовок формы, а процедура getcaption загружает из реестра ранее сохраненную строку, и устанавливает ее в качестве заголовка.

Рассмотрим фрагмент кода, который отвечает за открытие ключа. В комментариях уже оговорено, что при установке параметра true в процедуре openkey, ключ автоматически создается в случае его отсутствия. Следует всегда устанавливать значение true, так как при попытке открыть несуществующий ключ может произойти ошибка, которая повлечет за собой аварийное завершение программы.

Стоит сказать еще о двух процедурах модуля registry: getkeynames и getvaluenames. Они позволяют сканировать реестр, как обычные каталоги. При открытии ключа вы указываете начальный путь поиска, а далее процедура getkeynames создает список типа tstrings и записывает в него имена всех найденных ключей, а процедура getvaluenames составляет список из имен параметров, находящихся в заданном ключе.

До появления 32-х разрядных операционных систем, для хранения параметров программы использовали исключительно конфигурационные файлы с расширением "ini" (далее ini-файлы). Но вскоре, ini-файлы были забыты, и на смену им пришел системный реестр. Но до сих пор встречаются программы, которые активно используют такой способ хранения параметров. В чем же его преимущества? Прежде всего в стабильности. В отличие от системного реестра, при сбоях в операционной системе с ini-файлом ничего не случается, если только он не находится в системном каталоге. Стоит помнить, что ini-файл должен находиться в одном каталоге с программой. Кроме стабильности, важным преимуществом ini-файлов является мобильность программного кода, в чем вы можете убедиться, посмотрев пример, приведенный ниже. Что же касается недостатков, то это простота удаления. ini-файл - это обычный файл, который можно случайно удалить.

Для работы с ini-файлами в borland delphi предусмотрен модуль inifiles.
Код:
uses inifiles;
….
var
inifile: tinifile;
begin
inifile := tinifile.create(‘имя файла’);
….
….
inifile.free;
end;

Принципы работы ini-файлов и системного реестра схожи между собой. Изменим предыдущую программу, которая сохраняла и изменяла заголовок формы, используя системный реестр.
Код:
procedure savecaption;
var
inifile: tinifile;
begin
inifile := tinifile.create(extractfilepath(application.exename) + 'test.ini');
inifile.writestring('mainoptions', 'formcaption', form1.caption);
inifile.free;
end;

Код:
procedure getcaption;
var
inifile: tinifile;
begin
inifile := tinifile.create(extractfilepath(application.exename) + 'test.ini');
form1.caption := inifile.readstring('mainoptions', 'formcaption', '');
inifile.free;
end;

Как видите, по сравнению с предыдущей программой, наблюдается уменьшение программного кода. Да и ошибок при работе с ini-файлами возникает так же намного меньше, чем с системным реестром.
А теперь, поговорим о средствах защиты. Операционная система предоставляет возможность закрытия доступа к определенным файлам. Но, к сожалению, закрытие доступа длится только во время работы программы. После того, как программа будет закрыта, доступ будет полностью открыт. Например, можно ограничить доступ к ini-файлу. Для реализации данной возможности, среди множества функций winapi существует функция openfile. Давайте попробуем написать программу, которая закрывала бы доступ к ini-файлу.
Код:
var
form1: tform1;
hinilockedfile: cardinal;
ofstruct : _ofstruct;
implementation
{$r *.dfm}
procedure savecaption;
var
inifile: tinifile;
begin
{Отключаем защиту файла}
closehandle(hinilockedfile);
{Резервное время для оключения}
sleep(1000);
inifile := tinifile.create(extractfilepath(application.exename) + 'test.ini');
inifile.writestring('mainoptions', 'formcaption', form1.caption);
inifile.free;
{После сохранения, заново ставим защиту}
hinilockedfile := openfile(pchar(extractfiledir(application.exename) + 'test.ini'), ofstruct, of_share_exclusive);
end;

Код:
procedure getcaption;
var
inifile: tinifile;
begin
inifile := tinifile.create(extractfilepath(application.exename) + 'test.ini');
form1.caption := inifile.readstring('mainoptions', 'formcaption', '');
inifile.free;
{Устанавливаем защиту}
hinilockedfile := openfile(pchar(extractfiledir(application.exename) + 'test.ini'), ofstruct, of_share_exclusive);
end;

При запуске программы, из ini-файла считывается информация, после чего он блокируется для последующих операций работы с ним: копирование, удаление, редактирование и т.д. При сохранении параметров, защита временно снимается, а вскоре заново возобновляется.

Ограничение доступа к файлу test.ini

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

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

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

Я написал две простейшие процедуры кодирования и декодирования текста. На их примере мы и рассмотрим работу нашей программы.
Код:
var
form1: tform1;
hinilockedfile: cardinal;
ofstruct : _ofstruct;
const
cscryptfirst = 20;
cscryptsecond = 230;
cscryptheader = 'crypted';

type
ecrypterror = class(exception);
implementation
{$r *.dfm}
function cryptstring(str:string):string;
var
i : integer;
clen : integer;
begin
clen := length(cscryptheader);
setlength(result, length(str)+clen);
move(cscryptheader[1], result[1], clen);
for i := 1 to length(str) do
begin
if i mod 2 = 0 then
result[i+clen] := chr(ord(str[i]) xor cscryptfirst)
else
result[i+clen] := chr(ord(str[i]) xor cscryptsecond);
end;
end;

function uncryptstring(str:string):string;
var
i : integer;
clen : integer;
begin
clen := length(cscryptheader);
setlength(result, length(str)-clen);
if copy(str, 1, clen) <> cscryptheader then
raise ecrypterror.create('Файл поврежден!');
for i := 1 to length(str)-clen do
begin
if (i) mod 2 = 0 then
result[i] := chr(ord(str[i+clen]) xor cscryptfirst)
else
result[i] := chr(ord(str[i+clen]) xor cscryptsecond);
end;
end;

procedure cryptinifile;
var
s: tstringlist;
i: integer;
begin
s := tstringlist.create;
s.loadfromfile(extractfiledir(application.exename) + 'test.ini');
for i := 0 to s.count - 1 do
s.strings[i] := cryptstring(s.strings[i]);
s.savetofile(extractfiledir(application.exename) + 'test.ini');
end;

procedure decryptinifile;
var
s: tstringlist;
i: integer;
begin
if not fileexists(extractfiledir(application.exename) + 'test.ini') then exit;
s := tstringlist.create;
s.loadfromfile(extractfiledir(application.exename) + 'test.ini');
for i := 0 to s.count - 1 do
s.strings[i] := uncryptstring(s.strings[i]);
s.savetofile(extractfiledir(application.exename) + 'test.ini');
end;

procedure savecaption;
var
inifile: tinifile;
begin
{Отключаем защиту файла}
closehandle(hinilockedfile);
{Резервное время для оключения}
sleep(1000);
inifile := tinifile.create(extractfilepath(application.exename) + 'test.ini');
inifile.writestring('mainoptions', 'formcaption', form1.caption);
inifile.free;
{После сохранения, заново ставим защиту}
hinilockedfile := openfile(pchar(extractfiledir(application.exename) + 'test.ini'), ofstruct, of_share_exclusive);
end;

procedure getcaption;
var
inifile: tinifile;
begin
{Расширофка конфигурационного файла}
decryptinifile;
sleep(1000);
inifile := tinifile.create(extractfilepath(application.exename) + 'test.ini');
form1.caption := inifile.readstring('mainoptions', 'formcaption', '');
inifile.free;
{Устанавливаем защиту}
hinilockedfile := openfile(pchar(extractfiledir(application.exename) + 'test.ini'), ofstruct, of_share_exclusive);
end;

procedure tform1.formcreate(sender: tobject);
begin
getcaption;
end;

procedure tform1.formclose(sender: tobject; var action: tcloseaction);
begin
savecaption;
closehandle(hinilockedfile);
cryptinifile;
end;

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

Следует сказать пару слов о самих алгоритмах кодирования и декодирования текста. В качестве признака того, что файл уже был закодирован, используется обыкновенная текстовая строка «crypted», которой соответствует константа cscryptheader. Она следует вначале каждой зашифрованной строчки. Перед шифрованием, определяется длина строки cscryptheader, после чего увеличивается длина будущей строки, которая равна длине шифруемой строки и длине строки cscryptheader. Далее, простым перебором, происходит замена каждого символа шифруемой строки. Это осуществляется с помощью функции chr, которая по сгенерированному программой числу возвращает соответствующий символ. При замене символов, следует учитывать определенные параметры, которые определяет логическая операция. В нашем примере, в зависимости от того, делится ли порядковый номер строки на два без остатка, используются разные параметры, что усложняет последующую дешифровку текста.

При дешифровки текста определяется его реальная длина, что достигается вычитанием из длины дешифруемой строки длины строки cscryptheader. Для того, что бы определить, поврежден файл или нет, программа ищет в закодированной строчке заголовок, в нашем примере это «crypted», и если он отсутствует, то дешифровка прекращается и возникает ошибка. Если же все в порядке, далее идет расшифровка текста, алгоритм которой полностью противоположен алгоритму шифрования.

Итак, все способы, освещенные в статье, были подробно разобраны и оговорены. Теперь выбор за вами. Попробуйте описанные в этой статье способы на своей практике. Импровизируйте, пытаясь создать что-то совершенное, и решение в выборе между системным реестром и ini-файлами придет к вам.



 

 Вопросы и ответы по сертификации Delphi.
 Популярность Delphi продолжает расти.
 Программируем в Delphi.
 RadPHP XE2.
 Работа с файлами в Delphi.


Главная »  Delphi 

© 2017 Team.Furia.Ru.
Частичное копирование материалов разрешено.