![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Классы в DLL на Delphi и MSVC++ (бой продолжается)...
ПЗ © (2008-09-13 21:58:00)Продолжаю исследовать возможности написания плагинов для 3d Studio MAX на Delphi.
Плагин сидит в DLL в виде класса заданной структуры. 3ds загружает DLL, получает
ссылку на экземпляр класса (не COM) и вызывает его методы. Когда класс написан на MSVC
все ОК, когда на Delphi - есть сбои. Путем сравнения процессов вызова в отладчиках
установил аномалию.
Есть метод класса.
На С++:
class MyUtilityClassDesc
{
...
virtual SClassID ClassID()
{
return sid;
}
}
где
struct SClassID
{
ULONG a,b;
}sid;
...
sid.a=0xFF00FF;
sid.b=0xAABBEE;
То же на Delphi:
ClassDesc=class
...
function ClassID():MClass_ID;virtual;cdecl;
end;
function ClassDesc.ClassID():MClass_ID;cdecl;
begin
Result:=m_id;
end;
где
MClass_ID=record
a,b:LongWord;
end;
...
m_id:MClass_ID;
m_id.a:=$FF00FF;
m_id.b:=$AABBEE;
Аномалия в том, что после возврата из ClassID() на С++, указатель стека смщается, а на Delphi - нет!
На С++:
Перед вызовом
ESP = 0х0012DD34
После вызова
ESP = 0х0012DD38
На Delphi
Перед вызовом
ESP = $0012DD34
После вызова
ESP = $0012DD34 <=!!!
Получается, что на С++ вершина стека оказывается непосредственно на возвращенном результате, а на Delphi - ПОСЛЕ результата функции(метода), что потом приводит к AV!
Знатоки, научите, как такое может быть, если вызывающий код один и тот же и результат возвращается корректно?
oxffff (2008-09-14 23:23:00) [81]
> Anatoly Podgoretsky © (14.09.08 23:09) [9]
-1.
:)
И cdecl и stdcall использует указатель на слот для результата размером >4
The following conventions are used for returning function result values.
Ordinal results are returned, when possible, in a CPU register. Bytes are returned in AL, words are returned in AX, and double-words are returned in EAX.
Real results are returned in the floating-point coprocessor's top-of-stack register (ST(0)). For function results of type Currency, the value in ST(0) is scaled by 10000. For example, the Currency value 1.234 is returned in ST(0) as 12340.
For a string, dynamic array, method pointer, or variant result, the effects are the same as if the function result were declared as an additional var parameter following the declared parameters. In other words, the caller passes an additional 32-bit pointer that points to a variable in which to return the function result.
Int64 is returned in EDX:EAX.
Pointer, class, class-reference, and procedure-pointer results are returned in EAX.
For static-array, record, and set results, if the value occupies one byte it is returned in AL; if the value occupies two bytes it is returned in AX; and if the value occupies four bytes it is returned in EAX. Otherwise, the result is returned in an additional var parameter that is passed to the function after the declared parameters..
Anatoly Podgoretsky (2008-09-14 23:09:00) [82]> Сергей М. (14.09.2008 22:14:06) [6]
Вообще то об этом говорит "mov eax,dword ptr [ebp+8]", а не "ret 4"
oxffff (2008-09-14 22:44:00) [83]
> либо бросить затею скрестить ежа с ужом - об этом тебе,
> кстати, уже неоднократоно говорили.
The cdecl convention is useful when you call functions from shared libraries written in C or C++.
Скрещивать можно все. Нужно только немного знаний. Самую малость.
oxffff (2008-09-14 22:42:00) [84]
> Сергей М. © (14.09.08 22:14) [6]
Уже лучше.
Сергей М. (2008-09-14 22:14:00) [85]
> return sid;
> mov eax,dword ptr [ebp+8]
> mov ecx,dword ptr [sid (0D2CF734h)]
> mov dword ptr [eax],ecx
> mov edx,dword ptr [sid+4 (0D2CF738h)]
> mov dword ptr [eax+4],edx
> mov eax,dword ptr [ebp+8]
> }
> pop edi
> pop esi
> pop ebx
> mov esp,ebp
> pop ebp
> ret 4
>
Это говорит о том, что на вершину стека перед вызовом ф-ции компилятором был положен параметр - адрес структуры, которую ф-ция должна заполнить, фактически скопировать.
Он же, этот адрес, возвращается в кач-ве рез-та в EAX.
RET 4 говорит об stdcall - стек балансируется вызываемой ф-цией.
> Как быть?
Либо извращаться на BASM'е либо бросить затею скрестить ежа с ужом - об этом тебе, кстати, уже неоднократоно говорили.
Сергей М. (2008-09-14 21:18:00) [86]
> после возврата из ClassID()
Ну а где у тебя реализация этой функции ?
Сергей М. (2008-09-14 21:16:00) [87]
> нет у него параметров. self разве что
У кого "у него" ?
Покажи конкретную строчку дельфийского кода, при исполнении которой происходит эта самая "аномалия" ..
ПЗ (2008-09-14 20:29:00) [88]Да вроде и нет у него параметров. self разве что....
На всякий случай, попробовал stdcallв Delphi-реализации.
После возврата указатель стека перебрасывается аж в $0012DD3C. А надо, чтобы был $0012DD38.
safecall и pascal дают AV. Как быть?
Сергей М. (2008-09-13 22:49:00) [89]
> Перед вызовом
> ESP = 0х0012DD34
> После вызова
> ESP = 0х0012DD38
Это говорит о том, что соглашение о вызове однозначноне cdecl.
> На Delphi
> Перед вызовом
> ESP = $0012DD34
> После вызова
> ESP = $0012DD34 <=!!!
Это говорит о том, что вызываемой cdecl-подпрограмме небыл передан ни один входной параметр.
ПЗ (2008-09-13 22:00:00) [90]На всякий случай, скомпилированный вид данного метода на обоих языках:
С++
virtual SClassID ClassID()
{
push ebp
mov ebp,esp
sub esp,0CCh
push ebx
push esi
push edi
push ecx
lea edi,[ebp-0CCh]
mov ecx,33h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
pop ecx
mov dword ptr [ebp-8],ecx
return sid;
mov eax,dword ptr [ebp+8]
mov ecx,dword ptr [sid (0D2CF734h)]
mov dword ptr [eax],ecx
mov edx,dword ptr [sid+4 (0D2CF738h)]
mov dword ptr [eax+4],edx
mov eax,dword ptr [ebp+8]
}
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 4
Delphi
ClassDesc.pas.244: begin
push ebp
mov ebp,esp
mov eax,[ebp+$08]
mov edx,[m_id] <=== адрес возвращаемой структуры (m_id.a)
mov [eax],edx
mov edx,[m_id+$4] <=== m_id.b
mov [eax+$04],edx
ClassDesc.pas.244: end;
pop ebp
ret
Настораживают разночтения у скомпилированных функций. Что означает sub esp,0CCh в С++ - варианте?