![]() |
| ||
|
|
Новости |
Поиск |
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!
Знатоки, научите, как такое может быть, если вызывающий код один и тот же и результат возвращается корректно?
Anatoly Podgoretsky (2008-09-15 16:33:00) [61]> Сергей М. (15.09.2008 15:44:29) [29]
Классик сказал, напился, тут помню, а тут нет.
Сергей М. (2008-09-15 15:44:00) [62]
> но компилятор не знает
Та к не бывает - тут знаю, а тут не знаю)
oxffff (2008-09-15 14:33:00) [63]
> Slym © (15.09.08 11:20) [19]
Интерфейс - это просто указатель на абстрактный класс с самантикой подсчета ссылок и запроса других абстрактных классов.
oxffff (2008-09-15 14:20:00) [64]
> Либо они есть , либо их нет - разве компилятор об этом не
> знает ?
В данном случае их нет, но компилятор не знает.
:O)
Slym (2008-09-15 13:06:00) [65]попробуй так:
type
TClassID=packed record
a,b:LongWord;
end;
TClassDesc=packed record
IsPublic: function:LongBool;stdcall;
Create: function(const Loading:LongBool):pointer;stdcall;
ClassName:function :PChar;stdcall;
SuperClassID:function:TClassID;stdcall;
ClassID:function:TClassID;stdcall;
Category:function :PChar;stdcall;
end;
const m_id:TClassID=(a:$FF00FF;b:$AABBEE);
function ClassID:TClassID;stdcall;
begin
result:=m_id;
end;
function LibClassDesc(const i:integer):TClassDesc;
begin
ZeroMemory(@result,SizeOf(result));
result.ClassID:=@ClassID;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
ClassDesc:TClassDesc;
ClassID:TClassID;
begin
ClassDesc:=LibClassDesc(1);
ClassID:=ClassDesc.ClassID;
end;
Slym (2008-09-15 12:41:00) [66]Метод Передача параметров Кто чистит стек Где применяется
__cdecl В стек, справа налево Вызывающий По умолчанию в C и C++
__stdcall В стек, справа налево Сама функция Вызовы почти всех системных функций, а также по умолчанию внутренние функции Visual Basic
__fastcall Два первых параметра в регистрах ECX и EDX, остальные в стек, справа налево Сама функция По умолчанию в Borland Delphi
thiscall Указатель this передается в регистре ECX, параметры в стек, справа налево. Сама функция Функциями-методами классов без списка аргументов переменной длины. Не может быть задан явно.
похоже на то
Сергей М. (2008-09-15 12:37:00) [67]
> oxffff © (15.09.08 12:01) [23]
> проверки доступа к неинициализированным локальным переменным
Зачем их "проверять" ? Не понятно ..
Либо они есть , либо их нет - разве компилятор об этом не знает ?
oxffff (2008-09-15 12:01:00) [68]
> Сергей М. © (15.09.08 11:41) [21]
VС++ использует эту технику для проверки доступа к неинициализированным локальным переменным(те самые которые по отрицательным смещениям от ebp). В данном случае инициализация в 0CCCCCCCCh.
Но поскольку локальных переменных здесь нет, то следующий код - это шняга
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
Slym (2008-09-15 11:46:00) [69]например так может?
type
TMClass_ID=packed record
a,b:LongWord;
end;
IClassDesc = interface
['{9275252B-2D15-4C04-8317-158591B1EE85}']
function ClassID():TMClass_ID;cdecl;
end;
TClassDesc=class(TObject, IClassDesc);
function ClassID():TMClass_ID;cdecl;
end;
var ClassDesc:TClassDesc;
function LibClassDesc(const i:integer):TClassDesc
begin
ClassDesc.GetInterface(IClassDesc,result);
end;
Сергей М. (2008-09-15 11:41:00) [70]
> oxffff © (15.09.08 11:10) [18]
> Для проверки инициализированности
Инициализированности чего?
Anatoly Podgoretsky (2008-09-15 11:26:00) [71]
> Сергей М. © (15.09.08 11:01) [17]
Видимо так, поскольку в последней строчке используется ECX, а инициализации не видать, в связи с этим перед этим сохраняется ECX в последней строчке. Но для точного диагноза надо знать соглашения о вызовах.
Slym (2008-09-15 11:20:00) [72]А мне кажется что здесь не объекты, а интерфейсы... ибо как MAX находит точку входа в методы? по VMT чтоли? дак она у всех разная...
oxffff (2008-09-15 11:10:00) [73]
> Сергей М. © (15.09.08 11:01) [17]
>
> > Anatoly Podgoretsky © (15.09.08 10:09) [15]
>
>
> Бесспорно.
> Но если этот код не "шняга", значит, в ECX передается один
> из вх.параметров ..
Неа. Этот код
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
Эта муть. Вставляется в VS в debug версии.
Отношения к коду не имеет.
Для проверки инициализированности перед использованием __RTC_UninitUse
Сергей М. (2008-09-15 11:01:00) [74]
> Anatoly Podgoretsky © (15.09.08 10:09) [15]
Бесспорно.
Но если этот код не "шняга", значит, в ECX передается один из вх.параметров ..
Anatoly Podgoretsky (2008-09-15 10:10:00) [75]> Сергей М. (15.09.2008 9:01:12) [12]
esi, edi сохраняются в соответствии с соглашением по использованию регистров.
Anatoly Podgoretsky (2008-09-15 10:09:00) [76]> Сергей М. (15.09.2008 9:01:12) [12]
Из отмеченого жирным, регистр ECX сохраняется в стеке поскольку он нужен неиспорченым в последеней строке.
Сергей М. (2008-09-15 09:43:00) [77]
> oxffff © (15.09.08 09:09) [13]
Я тоже не понимаю, с какой луны он свалился)
Автор что-то напутал, наверно ..
oxffff (2008-09-15 09:09:00) [78]
> Сергей М. © (15.09.08 09:01) [12]
>
> > Anatoly Podgoretsky © (15.09.08 00:09) [11]
>
>
> > но не register
>
>
> Конечно, не register, но как объяснить
Это вообще код-шняга. Он делает ровным счетом ничего.
Рабочий код начинается с return sid;
Сергей М. (2008-09-15 09:01:00) [79]
> Anatoly Podgoretsky © (15.09.08 00:09) [11]
> но не register
Конечно, не register, но как объяснить
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
??
> После возврата указатель стека перебрасывается аж в $0012DD3C.
> А надо, чтобы был $0012DD38.
Это из-за self'а - он первым заталкивается в стек, следом заталкивается указатель на результат.
Соотв-но на выходе ты увидишь RET 8 вместо ожидаемых RET 4
Anatoly Podgoretsky (2008-09-15 00:09:00) [80]> oxffff (14.09.2008 23:23:10) [10]
По другому более точно - ret 4 говорит, что это не cdecl, а вот что одначно решить нельзя, толи это stdcall, толи pascal, толи safecal, но не register, тема в справке Calling conventions.