![]() |
|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
![]() |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
||||
|
||||
![]() Уважаемые форумчане, меня давно интересует один вопрос, надеюсь вы мне подскажите.. Сразу предупреждаю, что я новичок, поэтому не судите строго.
Вообщем дело в следующим. Допустим есть кнопка при нажатии на которую происходит следующее: 1. Создается файл 2. Файл переименовывается 3. Файл копируется куда-либо Как это делается я знаю. Вопрос в том, как грамотно, логично написать этот код. Можно ли просто написать три этих команды подряд или нужно ставить проверку перед каждым действием? Т.е. создать файл, проверить если он создался, переименовать, проверить если последнее было сделано (если нет, попытаться снова пока не получится) и только потом копировать его? Как правильно? |
#2
|
||||
|
||||
![]() Вообще, зависит от того, каким целям должен служить этот код.
В общем случае алгоритм выглядит как-то так 1) Проверить все ли данные готовы 2) Выполнить операцию 3) Проверить не возникла ли ошибка(проверить код возрвата функции, либо проверить исключение в try..except, либо проверить значение LastError) 4) Как-то обработать ошибку, если она произошла 5) Перейти к следующему этапу То есть, перед переименованием и копированием файла следует проверить, есть ли файл. Даже если предыдущая операция прошла без ошибок, файл, чисто теоритически, мог кто-то удалить. Перед созданием файла можно проверить, а есть ли каталог, в котором создается файл. Обрабатывать же ошибку можно различным образом: 1) Часть ошибок, с которыми вы знаете как бороться, можно победить программно(например, если файл с таким названием уже существует - удалить(переименовать) его и создать на его месте свой) 2) Те ошибки, с которыми вы не знаете как боротся можно выдать пользователю на экран в виде сообщения 3) Если пользователь недостаточно квалифицирован, чтобы разобратся с ошибкой, можно выдать пользователю общее сообщение, что-то вроде: "Произошла ошибка, операция не выполнена, свяжитесь с разработчиком" И сформировать отдельное сообщение\файл, содержащее подробное описание ошибки, которое пользователь направит вам, и которое вы сможете проанализировать. 4) Если операция выполняется в фоновом режиме, можно сохранять возникающие ошибки в лог. 5) Наконец, чисто теоритически, можно попробовать решить дело грубой силой - если не создался файл попытатся создать его еще раз и так до бесконечности(некоторое количество раз). Однако, логика подсказывает, что если файл не создался в первый раз, то он и в 10й не создасться, так что я бы не рекомендовал такой путь. Невозможно заточить карандаш тупым топором. Столь же тщетно пытаться сделать это десятком тупых топоров |
Этот пользователь сказал Спасибо madMonia за это полезное сообщение: | ||
Alloc (26.01.2015)
|
#3
|
||||
|
||||
![]() Большое Вам спасибо за ответ!
Цитата:
Сам по себе код в этом виде на моей машине работает как надо. А можно использовать для этого такую конструкцию? Код:
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
|
||||
|
||||
![]() Написанный цикл - просто абсурд.
99% функций возвращают управление в программу только после того, как выполнят все свои действия. И от скорости компьютера это никоим образом зависеть не может. Такой ЯП, как Delphi, описывает последовательность действий, и если бы какое-то действие могло начаться до того, как завершиться предыдущее, сама суть императивного языка исчезла бы и писать на таком языке было бы глупо и бессмысленно. jmp $ ; Happy End! The Cake Is A Lie. |
Этот пользователь сказал Спасибо Bargest за это полезное сообщение: | ||
Alloc (26.01.2015)
|
#5
|
||||
|
||||
![]() Как я говорил, я новичок и только начинаю познавать азы программирования
![]() Если я Вас правильно понял, то можно удалить цикл в моем коде и все команды выполняться поочередно? И мой код в принципе правильный? |
#6
|
||||
|
||||
![]() Может и правильный, но исходной задаче не соответствует.
Цитата:
Но этот бредовый цикл по-любому надо убирать, как минимум потому, что в нем RenameFile вызывается два или более раз. Если уж хочется выполнять переименование до тех пор, пока оно не вернет true, то делается это так Код:
while RenameFile(...) = false do sleep(10); //можно еще Application.ProcessMessages; jmp $ ; Happy End! The Cake Is A Lie. |
#7
|
||||
|
||||
![]() Цитата:
Эту задачу можно попытатся решить в лоб, просто проверяя ожидаемый результат выполнения задачи. Например, если задача создать файл - нужно проверить, что в каталоге появился соответствующий файл нужного размера. Однако, иногда проверить результат выполнения задачи довольно сложно, а то и вовсе не возможно. Кроме того, если произойдет какая-то ошибка, то вы не будете знать, что за ошибка произошла, и как вам ее исправить. Другими словами такой подход представляется крайне нежелательным. К счастью, есть другие варианты. Код, который непонятно когда закончит работу, и работает ли вообще, должен вызывать у вас дикую ярость и неистовое желание избавится от этого непредсказуемого фрагмента и заменить его на более предсказуемый и надежный вариант. Во-первых, желательно проверить, можно ли выполнить операции с помощью Делфи кода, не привлекая сторонние приложения. В частности, создать\переименовать\копировать файл можно. Поскольку я не знаю, какие у вас конкретно задачи, то не могу сказать определенне. Во-вторых, WinExec - это устаревшая функция, вместо нее можно использовать CreateProccess - она позволяет дождаться конца выполнения команды и проверить результат. Невозможно заточить карандаш тупым топором. Столь же тщетно пытаться сделать это десятком тупых топоров |
#8
|
||||
|
||||
![]() Давайте, чтобы не ходить вокруг да около, я выставлю код небольшой программы которую делаю, чтобы вы имели представление о чем идет речь.
Прошу поправить меня или посоветовать как сделать его (код) лучше. Буду очень признателен за помощь. Еще раз спасибо тем кто откликнулся! Как она выглядит Собственно сам код: Код:
//Процедура которая устанавливает совместимость игры ("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
|
||||
|
||||
![]() Разбираться в коде не хочу, но один вопрос возник. Речь шла о совместимости игры с разными ОС. Тогда почему в процедуре RegIt не обрабатывается входной параметр Param, который по логике автора и должен отвечать за совместимость?
Je venus de nulle part 55.026263 с.ш., 73.397636 в.д. |
#10
|
||||
|
||||
![]() В случае с 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
|
||||
|
||||
![]() madMonia, Bargest, angvelem, Спасибо вам большое за оказанную помощь и советы, премного вам благодарен. Думаю тему можно закрывать так как ответы на свои вопросы я получил.
Еще раз всем спасибо! |