Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Fail


Автор: pascal 29.3.2004, 03:06
Может кто знает почему когда я вызываю Fail в конструкторе класса, которой создаёться вне try при завершении программы возникает ошибка...

Пример:

Код
constructor TDB.Create;
...
begin
...
 Fail;
...
end;


если я в dpr проекте попытаюсь создать объект и если Fail будет выполнена, то либо при создании проекта будет ошибка, либо при завершении работы возникнет ошибка, причём в модуле system в процедуре UnsetExceptionHandler...

Например:

Код
...
 Application.Initialize;
 DB := TDB.Create;
...

будет ошибка
а если вот так
Код
...
 Application.Initialize;
 try
   DB := TDB.Create;
 except
 end;
...

или так
Код
...
 Application.Initialize;
 try
   DB := TDB.Create;
 finally
 end;
...

то всё ок...

Но почему???

(для тех кто не знает: Fail может быть вызвана в функции типа constructor и она удаляет созданный объект из памяти и TObject.Create возврощает nil)

PS: Не надо предлагать перенсти этот фрагмент кода в try, как это в работоспособных примерах, меня суть ошибки интересует...

Автор: p0s0l 29.3.2004, 06:54
В D7 сразу видно, что компилится по-левому, т.к. esp не восстанавливается...
Пример:
Код
 TMy = class
 public
   constructor Create;
 end;

constructor TMy.Create;
begin
 inherited create;
 Fail;
end;

Компилится так:
Код
//begin
push ebx
push esi
test dl, dl
jz +$08
add esp, -$10 // !!!!!!
call @ClassCreate
mov ebx, eax
mov esi, eax
// inherited Create
xor edx, edx
mov eax, esi
call TObject.Create
// Fail
mov edx, ebx
mov eax, esi
call @Fail
jmp +$17 // прыжок на end
...
// end
pop esi
pop ebx
ret

Видно, что после add esp, -$10 надо бы его как-то восстановить, но Fail без раздумьев прыгает на pop esi, pop ebx, ret так и не восстановив esp... Остюда и вся лажа...
Для данного случая можно сделать свой Fail:
Код
var adr : pointer;
procedure MyFail;
begin
 asm
   mov [adr], eax
 end;
 FreeMem(adr);
 asm
   xor eax, eax
   add esp, $14
   pop esi
   pop ebx
   ret
 end;
end;

constructor TMy.Create;
begin
 inherited create;
 MyFail;
end;

add esp, $14 - это из-за того, что в конструкторе делали add esp, -$10 и вызывали MyFail (4 байта в стеке)...

Добавлено @ 06:56
pascal, тебе же может надо будет делать не $14, а побольше (но кратно 4). Посмотри у себя, насколько портится esp...


Добавлено @ 06:58
А, да, еще может не только pop esi, pop ebx делать надо, но и еще какой-нибудь pop - посмотри код твоего конструктора (в отладке View->Debug Windows->CPU window)...

Автор: pascal 29.3.2004, 15:08
нет, в чём то другом дело... делаю так
Код
 asm
   push esi;
 end;
   DB := TDB.Create;
 asm
   pop esi;
 end;


всё равно не работает, ошибка там же...

то есть вот так

Код
 asm
   push esp;
 end;
   DB := TDB.Create;
 asm
   pop esp;
 end;


всё равно там же ошибка

Автор: pascal 29.3.2004, 15:34
> DB := TDB.Create;

Код
mov dl, $01
mov eax, [$00466a28]
call TDB.Create


при выполнении последней строки изменяються регистры:
EAX - 00466A74 -> 00000000
EBX - 7FFDF000 -> 0012FFA0
ECX - 0012FF9C -> 00000000
EDX - 0012FF01 -> 00000000
ESI - 00000000 -> 004671C2
EDI - 00000000 -> 0012FF48



Автор: pascal 29.3.2004, 22:13
Код

изменения регистра ESP с try

begin  - +4 - FFAC -> FF9C
try    - +3 - FF9C -> FF90
except - -3 - FF90 -> FF9C
end    - -4 - FF9C -> FFAC

изменения регистра ESP без try

begin  - +1 - FFAC -> FFA8
end    - -1 - FFA8 -> FFAC

createDB с try

function CreateDB: TDB;
begin
 try
   Result := TDB.Create;
 except
 end;
end;

при вызове CreateDB изменяються регистры: EAX, ECX

createDB без try

function CreateDB: TDB;
begin
 Result := TDB.Create;
end;

при вызове CreateDB изменяються регистры: EAX, EBX, ECX, EDX, ESI, EDI

createDB с push и pop try

function CreateDB: TDB;
begin
   asm
     push ebx
     push edx
     push esi
     push edi
   end;
   Result := TDB.Create;
   asm
     pop  edi
     pop  esi
     pop  edx
     pop  ebx
   end;
end;

при вызове CreateDB изменяються регистры: EAX, ECX

но оказалось что не в регистрах то дело...

Есть другай мысль... Надо отследить сам код это try...

> код TDB.Create
mov dl, 1
mov eax, *
call TDB.Create

> try
xor eax, eax
push ebp
push *
push dword ptr fs:[eax]

> try (после кода)
xor eax, eax
pop edx
pop ecx
pop ecx
mov fs:[eax], edx
jmp [end]

и так я сделал альтернативу

 try
   Result := TDB.Create;
 except
 end;

вот так

 asm
   xor eax, eax
   push dword ptr fs:[eax]
 end;
   Result := TDB.Create;
 asm
   mov ecx, eax
   xor eax, eax
   pop edx
   mov fs:[eax], edx
   mov eax, ecx
 end;

вроде работает, но как всётаки без этого обойтись???

Автор: p0s0l 30.3.2004, 13:24
Цитата(pascal @ 29.3.2004, 15:08)
нет, в чём то другом дело... делаю так
Код
 asm
   push esi;
 end;
   DB := TDB.Create;
 asm
   pop esi;
 end;


всё равно не работает, ошибка там же...

то есть вот так

Код
 asm
   push esp;
 end;
   DB := TDB.Create;
 asm
   pop esp;
 end;


всё равно там же ошибка

pascal, ошибка-то возникает в конструкторе, зачем push и pop делать где-то там, в далеке ? Что, каждый раз так будешь делать ? Я же привел пример, как исправить сам конструктор (вместо Fail делать MyFail)... Приведи асм-листинг самого конструктора (начиная с begin) - я тебе напишу, как у тебя будет выглядеть MyFail.
Ошибка возникает, как я сказал, из-за того, что esp при Fail не восстанавливается, а значит pop esi и pop ebx (в приведенном примере) восстанавливаются совсем по-левому, и, самое главное, ret происходит неизвестно куда, может в космос, из-за этого и появляется исключение...

Добавлено @ 13:30
Цитата
try
  Result := TDB.Create;
except
end;

вот так

asm
  xor eax, eax
  push dword ptr fs:[eax]
end;
  Result := TDB.Create;
asm
  mov ecx, eax
  xor eax, eax
  pop edx
  mov fs:[eax], edx
  mov eax, ecx
end;


вроде работает, но как всётаки без этого обойтись?

Ага, зачем try..except заменять на asm-аналог ? Быстрее try-except написать, чем всё это, и будет то же самое...

Автор: pascal 30.3.2004, 13:49
Я придумал, вот так работает

Код
asm
 add esp, $0C
 pop dword ptr fs:[0]
end;
Fail;


тока в теле try слишком сильно сбиваеться esp и восстановить его весьма сложно

то есть вот так уже работать не будет

Код

try
 asm
   add esp, $0C
   pop dword ptr fs:[0]
 end;
 Fail;
except
end;

или
Код

try
...
except
 asm
   add esp, $0C
   pop dword ptr fs:[0]
 end;
 Fail;
end;

Автор: p0s0l 30.3.2004, 15:52
А зачем pop dword ptr fs:[0] ? Это же в случае try..except...
Просто add esp, $10 не работает ?

Автор: pascal 30.3.2004, 23:27
Цитата
Просто add esp, $10 не работает ?

Неа, не работает...

В FS:[0] Delphi переодически esp сохраняет... а сам esp и с "add esp, $10" и без него не меняеться в результате...

Автор: p0s0l 31.3.2004, 06:46
Код
В FS:[0] Delphi переодически esp сохраняет...

Если ты пользуешь try - тогда и сохраняет...
Код
сам esp и с "add esp, $10" и без него не меняеться в результате...
Ты поди понял так, что add esp,$10 надо вместо pop делать ? Я имел в виду, что надо вместо add esp, $0c + pop делать add esp,$10...
Esp в обоих случаях будет одинаковым, к твоему сведениюsmile.gif:
1. add esp, $10
2. add esp, $0c; pop dword ptr fs:[0]
Так что что-то ты там намудрил, или неправильно понял...
pop dword ptr fs:[0] не восстанавливает esp, а заносит по этому адресу fs:[0] dword из стека.

Вот способ Fail, работающий хоть в try..except, хоть без него:
Код
var  old_esp : dword;

constructor TMy.Create;
begin
asm
  mov [old_esp], esp
end;

inherited create;

try

asm
  mov esp, [old_esp]
  add esp, $10
end;
Fail;

except
end;
end;

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)