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

Delphi Sources



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

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 26.01.2015, 14:38
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию Как грамотно написать код

Уважаемые форумчане, меня давно интересует один вопрос, надеюсь вы мне подскажите.. Сразу предупреждаю, что я новичок, поэтому не судите строго.

Вообщем дело в следующим. Допустим есть кнопка при нажатии на которую происходит следующее:

1. Создается файл
2. Файл переименовывается
3. Файл копируется куда-либо

Как это делается я знаю. Вопрос в том, как грамотно, логично написать этот код.
Можно ли просто написать три этих команды подряд или нужно ставить проверку перед каждым действием?

Т.е. создать файл, проверить если он создался, переименовать, проверить если последнее было сделано (если нет, попытаться снова пока не получится) и только потом копировать его? Как правильно?
Ответить с цитированием
  #2  
Старый 26.01.2015, 17:23
Аватар для madMonia
madMonia madMonia вне форума
Новичок
 
Регистрация: 25.02.2014
Сообщения: 50
Версия Delphi: Delphi XE3
Репутация: 2545
По умолчанию

Вообще, зависит от того, каким целям должен служить этот код.

В общем случае алгоритм выглядит как-то так
1) Проверить все ли данные готовы
2) Выполнить операцию
3) Проверить не возникла ли ошибка(проверить код возрвата функции, либо проверить исключение в try..except, либо проверить значение LastError)
4) Как-то обработать ошибку, если она произошла
5) Перейти к следующему этапу

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

Обрабатывать же ошибку можно различным образом:
1) Часть ошибок, с которыми вы знаете как бороться, можно победить программно(например, если файл с таким названием уже существует - удалить(переименовать) его и создать на его месте свой)
2) Те ошибки, с которыми вы не знаете как боротся можно выдать пользователю на экран в виде сообщения
3) Если пользователь недостаточно квалифицирован, чтобы разобратся с ошибкой, можно выдать пользователю общее сообщение, что-то вроде: "Произошла ошибка, операция не выполнена, свяжитесь с разработчиком" И сформировать отдельное сообщение\файл, содержащее подробное описание ошибки, которое пользователь направит вам, и которое вы сможете проанализировать.
4) Если операция выполняется в фоновом режиме, можно сохранять возникающие ошибки в лог.
5) Наконец, чисто теоритически, можно попробовать решить дело грубой силой - если не создался файл попытатся создать его еще раз и так до бесконечности(некоторое количество раз). Однако, логика подсказывает, что если файл не создался в первый раз, то он и в 10й не создасться, так что я бы не рекомендовал такой путь.
__________________
Невозможно заточить карандаш тупым топором. Столь же тщетно пытаться сделать это десятком тупых топоров
Ответить с цитированием
Этот пользователь сказал Спасибо madMonia за это полезное сообщение:
Alloc (26.01.2015)
  #3  
Старый 26.01.2015, 18:12
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Большое Вам спасибо за ответ!


Цитата:
Однако, логика подсказывает, что если файл не создался в первый раз, то он и в 10й не создасться
Из всего этого меня больше всего интересует вопрос о том, успеет ли выполнить компьютер все команды о которых я говорил поочередно? Для этого собственно я и хочу как то проверить выполнение команд. Ведь я опасаюсь, что на медленных компьютерах, допустим, вторая команда начнет выполняться до того как первая закончилась.

Сам по себе код в этом виде на моей машине работает как надо.

А можно использовать для этого такую конструкцию?

Код:
procedure TForm1.btn1Click(Sender: TObject);
begin
 if chk2.Checked = True then
  begin
    repeat
      RenameFile('wdraw.dll','ddraw.dll');
      Application.ProcessMessages;
    until RenameFile('wddraw.dll','ddraw.dll') = True;
  end;

  if rb1.Checked = True then RegIt('WIN95 RunAsAdmin DisableNXShowUI', True);
  if rb2.Checked = True then RegIt('WIN98 RunAsAdmin DisableNXShowUI', True);
  if rb3.Checked = True then RegIt('WINXPSP3 RunAsAdmin DisableNXShowUI', True);

  if chk1.Checked = True then
     WinExec(Pchar('cmd.exe /c start /affinity 1 Revenant.exe'), SW_HIDE)
  else
     WinExec(Pchar('Revenant.exe'), SW_SHOW);
end;
Ответить с цитированием
  #4  
Старый 26.01.2015, 18:18
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Написанный цикл - просто абсурд.
99% функций возвращают управление в программу только после того, как выполнят все свои действия. И от скорости компьютера это никоим образом зависеть не может. Такой ЯП, как Delphi, описывает последовательность действий, и если бы какое-то действие могло начаться до того, как завершиться предыдущее, сама суть императивного языка исчезла бы и писать на таком языке было бы глупо и бессмысленно.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
Alloc (26.01.2015)
  #5  
Старый 26.01.2015, 19:11
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Как я говорил, я новичок и только начинаю познавать азы программирования

Если я Вас правильно понял, то можно удалить цикл в моем коде и все команды выполняться поочередно? И мой код в принципе правильный?
Ответить с цитированием
  #6  
Старый 26.01.2015, 19:53
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Может и правильный, но исходной задаче не соответствует.
Цитата:
1. Создается файл
2. Файл переименовывается
3. Файл копируется куда-либо
А в написанном коде выполняется переименование, потом вызывается некая функция RegIt (причем лучше было использовать else'ы, раз в качестве переключателей используются взаимоисключающие RadioButton'ы), после чего происходит запуск через WinExec.
Но этот бредовый цикл по-любому надо убирать, как минимум потому, что в нем RenameFile вызывается два или более раз. Если уж хочется выполнять переименование до тех пор, пока оно не вернет true, то делается это так
Код:
while RenameFile(...) = false do
   sleep(10); //можно еще Application.ProcessMessages;
Только вот смысла в таком решении мало: если файл не переименовался с первого раза, то скорее всего и с десятого не переименуется.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.
Ответить с цитированием
  #7  
Старый 26.01.2015, 20:36
Аватар для madMonia
madMonia madMonia вне форума
Новичок
 
Регистрация: 25.02.2014
Сообщения: 50
Версия Delphi: Delphi XE3
Репутация: 2545
По умолчанию

Цитата:
Сообщение от Alloc
Из всего этого меня больше всего интересует вопрос о том, успеет ли выполнить компьютер все команды о которых я говорил поочередно?
Аа вот в чем дело. Конечно, если вы используете вызов сторонних приложений через WinExec, то вы не будете знать, завершила ли запрошенная вами команда работу или нет, а так же вы не будете знать, успешно она завершила работу или нет.

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

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

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

Во-вторых, WinExec - это устаревшая функция, вместо нее можно использовать CreateProccess - она позволяет дождаться конца выполнения команды и проверить результат.
__________________
Невозможно заточить карандаш тупым топором. Столь же тщетно пытаться сделать это десятком тупых топоров
Ответить с цитированием
  #8  
Старый 27.01.2015, 00:48
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

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

Прошу поправить меня или посоветовать как сделать его (код) лучше. Буду очень признателен за помощь. Еще раз спасибо тем кто откликнулся!

Как она выглядит

Собственно сам код:

Код:
//Процедура которая устанавливает совместимость игры ("Revenant.exe") с Win95,98,XP (rb1, rb2, rb3)
procedure RegIt(Param: string; AddDel: Boolean);
var
  Registry: TRegistry;
begin
  Registry := TRegistry.Create;
  Registry.RootKey := HKEY_CURRENT_USER;
  Registry.OpenKey('Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers', True);
  if AddDel = True then Registry.WriteString(ExtractFilePath(ParamStr(0))+'Revenant.exe', Param);
  if AddDel = False then Registry.DeleteValue(ExtractFilePath(ParamStr(0))+'Revenant.exe');
  Registry.CloseKey;
  Registry.Free;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  Ini: Tinifile;
begin
  RenameFile('wdraw.dll','ddraw.dll'); // Переименовываем файл при выходе.

  if rb1.Checked = True then RegIt('', False); //Убираем совместимость с Win95
  if rb2.Checked = True then RegIt('', False); //Убираем совместимость с Win98
  if rb3.Checked = True then RegIt('', False); //Убираем совместимость с WinXP

//Сохраняем в .ini отмеченные опции
  Ini:=TiniFile.Create(extractfilepath(paramstr(0))+'Revenant.ini');
  Ini.WriteBool('Launcher','SingleCPU',chk1.Checked); //Run Game on single CPU
  Ini.WriteBool('Launcher','UseWineD3D',chk2.Checked); //Use WineD3D (override system ddraw library)
  Ini.WriteBool('Launcher','CompMode95',rb1.Checked); //Win95
  Ini.WriteBool('Launcher','CompMode98',rb2.Checked); //Win98
  Ini.WriteBool('Launcher','CompModeXP',rb3.Checked); //WinXP
  Ini.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Ini: Tinifile;
begin
  RenameFile('ddraw.dll','wdraw.dll'); // Переименовываем файл при входе.

//Загружаем из .ini отмеченные опции
  Ini:=TiniFile.Create(extractfilepath(paramstr(0))+'Revenant.ini');
  chk1.Checked:=Ini.ReadBool('Launcher','SingleCPU',False); //Run Game on single CPU
  chk2.Checked:=Ini.ReadBool('Launcher','UseWineD3D',False); //Use WineD3D (override system ddraw library)
  rb1.Checked:=Ini.ReadBool('Launcher','CompMode95',False); //Win95
  rb2.Checked:=Ini.ReadBool('Launcher','CompMode98',True); //Win98
  rb3.Checked:=Ini.ReadBool('Launcher','CompModeXP',False); //WinXP
  Ini.Free;
end;

//Кнопка запуска Игры (Run Game)
procedure TForm1.btn1Click(Sender: TObject);
begin
//Если отпечена опция "Use WineD3D (override system ddraw library)" то переименовываем файл
  if chk2.Checked = True then
     RenameFile('wdraw.dll','ddraw.dll');

//Если отмечена одна из опций "Win95,98,XP" тогда ставим соответствующую совместимость игре
  if rb1.Checked = True then RegIt('WIN95 RunAsAdmin DisableNXShowUI', True);
  if rb2.Checked = True then RegIt('WIN98 RunAsAdmin DisableNXShowUI', True);
  if rb3.Checked = True then RegIt('WINXPSP3 RunAsAdmin DisableNXShowUI', True);

//Если отмечена опция "Run Game on single CPU" запускаем игру на одном Ядре процессора.
  if chk1.Checked = True then
     WinExec(Pchar('cmd.exe /c start /affinity 1 Revenant.exe'), SW_HIDE)
  else //Иначе просто запускаем
     WinExec(Pchar('Revenant.exe'), SW_SHOW);
//Я выбрал именно WinExec потому, что только с ней у меня получилось запустить скрытно cmd.exe и задать параметры (cmd.exe /c start /affinity 1 Revenant.exe) для запуска игры
end;

Последний раз редактировалось Alloc, 27.01.2015 в 00:58.
Ответить с цитированием
  #9  
Старый 27.01.2015, 02:30
Аватар для angvelem
angvelem angvelem вне форума
.
 
Регистрация: 18.05.2011
Адрес: Омск
Сообщения: 3,970
Версия Delphi: 3,5,7,10,12,XE2
Репутация: выкл
По умолчанию

Разбираться в коде не хочу, но один вопрос возник. Речь шла о совместимости игры с разными ОС. Тогда почему в процедуре RegIt не обрабатывается входной параметр Param, который по логике автора и должен отвечать за совместимость?
__________________
Je venus de nulle part
55.026263 с.ш., 73.397636 в.д.
Ответить с цитированием
  #10  
Старый 27.01.2015, 12:20
Аватар для madMonia
madMonia madMonia вне форума
Новичок
 
Регистрация: 25.02.2014
Сообщения: 50
Версия Delphi: Delphi XE3
Репутация: 2545
По умолчанию

В случае с RenameFile нужно обрабать результат функции

Код:
if not RenameFile('ddraw.dll','wdraw.dll') then
begin
  ShowMessage('Произошла ошибка при переименовании файла' + IntToStr(GetLastError));
  exit;
end;

Работа с реестром зависит от версии Delphi. В DelphiXE начиная со 2й есть свойства LastError, LastErrorMsg, которые позволяют проверить, что за ошибка произошла. А до этого не помню, может быть никак нельзя было проверить.

Другими словами код как-то так выглядит
Код:
if not   Registry.OpenKey('Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers', True) then
begin
  ShowMessage('Не удалось открыть раздел реестра' + IntToStr(Registry.GetLastError));
  exit;
end;

Методы WriteString и DeleteValue выбрасывают эксепшионы
Код:
try
  if AddDel = True then Registry.WriteString(ExtractFilePath(ParamStr(0))+'Revenant.exe', Param);
  if AddDel = False then Registry.DeleteValue(ExtractFilePath(ParamStr(0))+'Revenant.exe');
except 
  on E: ERegistryException do
  begin
     ShowMessage('При изменении реестра произошла ошибка:' +e.message);
     exit;
  end;
end;

Ваше опасение, что какая-то из операций начнет выполнятся до того, как предыдущая завершит выполнение, безпочвенно. Операции выполняются строго последовательно.
__________________
Невозможно заточить карандаш тупым топором. Столь же тщетно пытаться сделать это десятком тупых топоров
Ответить с цитированием
  #11  
Старый 27.01.2015, 12:49
Аватар для Alloc
Alloc Alloc вне форума
Начинающий
 
Регистрация: 17.09.2014
Сообщения: 104
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

madMonia, Bargest, angvelem, Спасибо вам большое за оказанную помощь и советы, премного вам благодарен. Думаю тему можно закрывать так как ответы на свои вопросы я получил.

Еще раз всем спасибо!
Ответить с цитированием
Ответ


Delphi Sources

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

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

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

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


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


 

Сайт

Форум

FAQ

Соглашения

Прочее

 

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