Форум: "Основная";
Текущий архив: 2009.10.18;
Скачать: [xml.tar.bz2];
ВнизВ продолжение темы про DLL-классы на Delphi и MSVC. Нужен совет! Найти похожие ветки
← →
ПЗ (2008-08-06 21:23) [0]Имею плагин для 3d Studio MAX, написанный на С. Хочу написать его на Delphi. Плагин сидит в DLL, экспортирующей функцию, которая возвращает указатель на следующий класс (MessageBox вставлены мною для отладки):
// класс-описатель для вашего plug-in"а
class MyUtilityClassDesc
{
private:
Tab<FPInterface*> interfaces; // the FnPub interfaces published by this plugin
// maxutil.lib needed!
public:
virtual void AAA() {}
virtual void BBB() {}
virtual ~MyUtilityClassDesc() {MessageBox(0,"ClassDesc.~ClassDesc","",MB_OK);}
virtual int IsPublic()
{
MessageBox(0,"ClassDesc.IsPublic","",MB_OK);
return 1;
}
virtual void * Create(BOOL loading = FALSE)
{
MessageBox(0,"ClassDesc.Create","",MB_OK);
return NULL;//&theMyUtility;
}
int BeginCreate(Interface *i) {MessageBox(0,"ClassDesc.BeginCreate","",MB_OK); return 0;}
int EndCreate(Interface *i) {MessageBox(0,"ClassDesc.EndCreate","",MB_OK); return 0;};
virtual const TCHAR * ClassName()
{
MessageBox(0,"ClassDesc.ClassName","",MB_OK);
return "MyUtility";
}
virtual SClass_ID SuperClassID()
{
MessageBox(0,"ClassDesc.SuperClassID","",MB_OK);
return UTILITY_CLASS_ID;
}
virtual CClassID ClassID()
{
MessageBox(0,"ClassDesc.ClassID","",MB_OK);
return cid;//MYUTILITY_CLASS_ID;
}
virtual const TCHAR * Category()
{
MessageBox(0,"ClassDesc.Category","",MB_OK);
return "Utility";
}
virtual BOOL OkToCreate(Interface *i) {MessageBox(0,"ClassDesc.OkToCreate","",MB_OK); return TRUE; }
virtual BOOL HasClassParams() {MessageBox(0,"ClassDesc.HasClassParams","",MB_OK); return FALSE;}
virtual void EditClassParams(HWND hParent) {MessageBox(0,"ClassDesc.EditClassParams","",MB_OK);}
virtual void ResetClassParams(BOOL fileReset=FALSE) {MessageBox(0,"ClassDesc.ResetClassParams","",MB_OK);}
virtual int NumActionTables() {MessageBox(0,"ClassDesc.NumActionTables","",MB_OK); return 0; }
virtual ActionTable* GetActionTable(int i) {MessageBox(0,"ClassDesc.GetActionTable","",MB_OK); return NULL; }
virtual BOOL IsManipulator() {MessageBox(0,"ClassDesc.IsManipulator","",MB_OK); return FALSE; }
virtual BOOL CanManipulate(ReferenceTarget* hTarget) {MessageBox(0,"ClassDesc.CanManipulate","",MB_OK); return FALSE; }
virtual BOOL CanManipulateNode(INode* pNode) {MessageBox(0,"ClassDesc.CanManipulateNode","",MB_OK); return FALSE; }
virtual Manipulator* CreateManipulator(
ReferenceTarget* hTarget,
INode* pNode) {MessageBox(0,"ClassDesc.CreateManipulator","",MB_OK); return NULL; }
virtual Manipulator* CreateManipulator(INode* pNode) {MessageBox(0,"ClassDesc.CreateManipulator2","",MB_OK); return NULL;}
virtual BOOL NeedsToSave() {MessageBox(0,"ClassDesc.NeedsToSave","",MB_OK); return FALSE; }
virtual IOResult Save(ISave *isave) {MessageBox(0,"ClassDesc.Save","",MB_OK); return IO_OK; }
virtual IOResult Load(ILoad *iload) {MessageBox(0,"ClassDesc.Load","",MB_OK); return IO_OK; }
virtual DWORD InitialRollupPageState() {MessageBox(0,"ClassDesc.InitialRollupPageState","",MB_OK); return 0x7fffffff;}
// возвращает имя для MaxScript
virtual const TCHAR * InternalName()
{
MessageBox(0,"ClassDesc.InternalName","",MB_OK);
return _T("MyUtility");
}
// возвращает hInstance модуля
virtual HINSTANCE HInstance()
{
MessageBox(0,"ClassDesc.HInstance","",MB_OK);
return hInstance;
}
virtual int NumParamBlockDescs() {MessageBox(0,"ClassDesc.NumParamBlockDescs","",MB_OK); return 0; }
virtual ParamBlockDesc2* GetParamBlockDesc(int i) {MessageBox(0,"ClassDesc.GetParamBlockDesc","",MB_OK); return NULL; }
virtual ParamBlockDesc2* GetParamBlockDescByID(BlockID id) {MessageBox(0,"ClassDesc.GetParamBlockDescByID","",MB_OK); return NULL; }
virtual void AddParamBlockDesc(ParamBlockDesc2* pbd) {MessageBox(0,"ClassDesc.AddParamBlockDesc","",MB_OK); }
virtual void BeginEditParams(IObjParam *ip, ReferenceMaker* obj, ULONG flags, Animatable *prev) {MessageBox(0,"ClassDesc.BeginEditParams","",MB_OK); }
virtual void EndEditParams(IObjParam *ip, ReferenceMaker* obj, ULONG flags, Animatable *prev) {MessageBox(0,"ClassDesc.EndEditParams","",MB_OK); }
virtual void InvalidateUI(ParamBlockDesc2* pbd) {MessageBox(0,"ClassDesc.InvalidateUI","",MB_OK); }
virtual MCHAR* GetRsrcString(INT_PTR id){MessageBox(0,"ClassDesc.GetRsrcString","",MB_OK); return NULL;};
virtual void MakeAutoParamBlocks(ReferenceMaker* owner) {MessageBox(0,"ClassDesc.MakeAutoParamBlocks","",MB_OK); }
virtual int NumParamMaps() {MessageBox(0,"ClassDesc.NumParamMaps","",MB_OK); return 0; }
virtual IParamMap2* GetParamMap(int i) {MessageBox(0,"ClassDesc.GetParamMap","",MB_OK); return NULL; }
virtual IParamMap2* GetParamMap(ParamBlockDesc2* pbd) {MessageBox(0,"ClassDesc.GetParamMap2","",MB_OK); return NULL; }
virtual void SetUserDlgProc(ParamBlockDesc2* pbd, ParamMap2UserDlgProc* proc=NULL) {MessageBox(0,"ClassDesc.SetUserDlgProc","",MB_OK); }
virtual ParamMap2UserDlgProc* GetUserDlgProc(ParamBlockDesc2* pbd) {MessageBox(0,"ClassDesc.GetUserDlgProc","",MB_OK); return NULL; }
virtual bool DrawRepresentation(COLORREF bkColor, HDC hDC, Rect &rect) {MessageBox(0,"ClassDesc.DrawRepresentation","",MB_OK); return FALSE; }
virtual int NumInterfaces() {MessageBox(0,"ClassDesc.NumInterfaces","",MB_OK); return 0; }
virtual FPInterface* GetInterfaceAt(int i) {MessageBox(0,"ClassDesc.GetInterfaceAt","",MB_OK); return NULL; }
virtual FPInterface* GetInterface(Interface_ID id){MessageBox(0,"ClassDesc.GetInterface","",MB_OK); return NULL; }
virtual FPInterface* GetInterface(MCHAR* name){MessageBox(0,"ClassDesc.GetInterface2","",MB_OK); return NULL; }
virtual void AddInterface(FPInterface* fpi){MessageBox(0,"ClassDesc.AddInterface","",MB_OK); };
virtual void ClearInterfaces() { MessageBox(0,"ClassDesc.ClearInterfaces","",MB_OK); }
virtual Class_ID SubClassID() {MessageBox(0,"ClassDesc.SubClassID","",MB_OK); return Class_ID(); }
virtual INT_PTR Execute(int cmd, ULONG_PTR arg1=0, ULONG_PTR arg2=0, ULONG_PTR arg3=0) {MessageBox(0,"ClassDesc.Execute","",MB_OK); return 0; }
};
Два виртуальных метода вначале - пустышки. Зачем нужны - ХЗ, но без них последовательность вызовов методов нарушается. Последовательность следующая:
1. MyUtilityClassDesc.SuperClassID()
2. MyUtilityClassDesc.ClassID()
3. MyUtilityClassDesc.SuperClassID()
4. MyUtilityClassDesc.IsPublic()
5. MyUtilityClassDesc.ClassID()
6. MyUtilityClassDesc.ClassName()
7. MyUtilityClassDesc.Category()
8. MyUtilityClassDesc.InitialPageRollupState()
9. MyUtilityClassDesc.NemActionTables()
....
Написал аналог на Delphi (cм. далее). Есть проблема, тебуется опытный глаз!
← →
ПЗ (2008-08-06 21:25) [1]
ClassDesc=class {MaxHeapOperators}
private
interfaces:Tab;
public
destructor ClassDesc();virtual;
function IsPublic():integer;virtual;cdecl; // Show this in create branch?
function Creat(loading:Boolean=FALSE):Pointer;virtual;cdecl; // return a pointer to an instance of the class.
function BeginCreate(i:IUnknown):Integer;virtual;cdecl; {return 0;}
function EndCreate(i:IUnknown):Integer;virtual;cdecl; {return 0;}
function ClassName():PAnsiChar;virtual;cdecl;
function SuperClassID():SClass_ID;virtual;cdecl;
function ClassID():TClass_ID;virtual;cdecl;
function Category():PAnsiChar;virtual;cdecl; // primitive/spline/loft/ etc
function OkToCreate(i:IUnknown):Boolean;virtual;cdecl; { return TRUE; } // return FALSE to disable create button
function HasClassParams():Boolean;virtual;cdecl; {return FALSE;}
procedure EditClassParams(hParent:HWND);virtual;cdecl; {}
procedure ResetClassParams(fileReset:Boolean=FALSE);virtual;cdecl; {}
function NumActionTables():Integer;virtual;cdecl; { return 0; }
function GetActionTable(i:Integer):TActionTable;virtual;cdecl; { return NULL; }
function IsManipulator():Boolean;virtual;cdecl; { return FALSE; }
function CanManipulate(hTarget:TReferenceTarget):Boolean;virtual;cdecl; { return FALSE; }
function CanManipulateNode(pNode:INode):Boolean;virtual;cdecl; { return FALSE; }
function CreateManipulator(hTarget:TReferenceTarget;pNode:INode):TManipulator;overload;vi rtual;cdecl; { return NULL; }
function CreateManipulator(pNode:INode):TManipulator;overload;virtual;cdecl; {return NULL;}
function NeedsToSave():Boolean;virtual;cdecl; { return FALSE; }
function Save(isave:ISave):TIOResult;virtual;cdecl; { return IO_OK; }
function Load(iload:ILoad):TIOResult;virtual;cdecl; { return IO_OK; }
function InitialRollupPageState():DWord;virtual;cdecl; { return 0x7fffffff; }
function InternalName():PAnsiChar;virtual;cdecl; { return NULL; }
function HInstance():Cardinal;virtual;cdecl; { return NULL; }
function NumParamBlockDescs():Integer;virtual;cdecl; { return 0; }
function GetParamBlockDesc(i:Integer):TParamBlockDesc2;virtual;cdecl; { return NULL; }
function GetParamBlockDescByID(id:BlockID):TParamBlockDesc2;virtual;cdecl; { return NULL; }
procedure AddParamBlockDesc(pbd:TParamBlockDesc2);virtual;cdecl; { }
procedure BeginEditParams(ip:IObjParam; obj:TReferenceMaker; flags:Longword; prev:TAnimatable);virtual;cdecl; { }
procedure EndEditParams(ip:IObjParam; obj:TReferenceMaker; flags:Longword; prev:TAnimatable);virtual;cdecl; { }
procedure InvalidateUI(pbd:TParamBlockDesc2);virtual;cdecl; { }
{CoreExport} function GetRsrcString(id:INT_PTR):PAnsiChar;virtual;cdecl;
procedure MakeAutoParamBlocks(owner:TReferenceMaker);virtual;cdecl; { }
function NumParamMaps():Integer;virtual;cdecl; { return 0; }
function GetParamMap(i:Integer):IParamMap2;overload;virtual;cdecl; { return NULL; }
function GetParamMap(pbd:TParamBlockDesc2):IParamMap2;overload;virtual;cdecl; { return NULL; }
procedure SetUserDlgProc(pbd:TParamBlockDesc2; proc:TParamMap2UserDlgProc=nil);virtual;cdecl; { }
function GetUserDlgProc(pbd:TParamBlockDesc2):TParamMap2UserDlgProc;virtual;cdecl; { return NULL; }
function DrawRepresentation(bkColor:COLORREF; DC:HDC; Rect:TRect):Boolean;virtual;cdecl; { return FALSE; }
function NumInterfaces():integer;virtual;cdecl; { return interfaces.Count(); }
function GetInterfaceAt(i:Integer):FPInterface;virtual;cdecl; { return interfaces[i]; }
{CoreExport} function GetInterface(id:TInterface_ID):FPInterface;overload;virtual;cdecl;
{CoreExport} function GetInterface(name:PAnsiChar):FPInterface;overload;virtual;cdecl;
{CoreExport} procedure AddInterface(fpi:FPInterface);virtual;cdecl;
procedure ClearInterfaces();virtual;cdecl; { interfaces.ZeroCount(); }
function SubClassID():TClass_ID;virtual;cdecl; { return Class_ID(); }
function Execute(cmd:Integer; arg1:ULONG_PTR=0; arg2:ULONG_PTR=0; arg3:ULONG_PTR=0):INT_PTR;virtual;cdecl; { return 0; }
end;
Реализацию сделал аналогично сишному аналогу, и тоже вставил MessageBox для контроля правильности вызовов методов. Здесь все вызывается правильно и никаких левых методов-пустышек не требуется (см. ниже).
← →
ПЗ (2008-08-06 21:28) [2]//ClassDesc
destructor ClassDesc.ClassDesc();
begin
inherited;
end;
function ClassDesc.IsPublic():integer;cdecl; // Show this in create branch?
begin
MessageBox(0,"IsPublic","ClassDesc",MB_OK);
Result:=1;
end;
function ClassDesc.Creat(loading:Boolean=FALSE):Pointer;cdecl; // return a pointer to an instance of the class.
begin
MessageBox(0,"Creat","ClassDesc",MB_OK);
Result:=nil;
end;
function ClassDesc.BeginCreate(i:IUnknown):Integer;cdecl; {return 0;}
begin
MessageBox(0,"BeginCreate","ClassDesc",MB_OK);
Result:=0;
end;
function ClassDesc.EndCreate(i:IUnknown):Integer;cdecl; {return 0;}
begin
MessageBox(0,"EndCreate","ClassDesc",MB_OK);
Result:=0;
end;
function ClassDesc.ClassName():PAnsiChar;cdecl;
begin
MessageBox(0,"ClassName","ClassDesc",MB_OK);
Result:=pchClassName;
Result:=nil;
end;
function ClassDesc.SuperClassID():SClass_ID;cdecl;
begin
MessageBox(0,"SuperClassID","ClassDesc",MB_OK);
Result:=UTILITY_CLASS_ID;
end;
function ClassDesc.ClassID():TClass_ID;cdecl;
begin
MessageBox(0,"ClassID","ClassDesc",MB_OK);
Result:=c_id;
end;
function ClassDesc.Category():PAnsiChar;cdecl; // primitive/spline/loft/ etc
begin
MessageBox(0,"Category","ClassDesc",MB_OK);
Result:=nil;
Result:=pchCategory;
end;
function ClassDesc.OkToCreate(i:IUnknown):Boolean;cdecl; { return TRUE; } // return FALSE to disable create button
begin
MessageBox(0,"OKCreate","ClassDesc",MB_OK);
Result:=True;
end;
function ClassDesc.HasClassParams():Boolean;cdecl; {return FALSE;}
begin
MessageBox(0,"HasClassParams","ClassDesc",MB_OK);
Result:=False;
end;
procedure ClassDesc.EditClassParams(hParent:HWND);cdecl; {}
begin
MessageBox(0,"EditClassParams","ClassDesc",MB_OK);
end;
procedure ClassDesc.ResetClassParams(fileReset:Boolean=FALSE);cdecl; {}
begin
MessageBox(0,"ResetClassParams","ClassDesc",MB_OK);
end;
function ClassDesc.NumActionTables():Integer;cdecl; { return 0; }
begin
MessageBox(0,"NewActTable","ClassDesc",MB_OK);
Result:=0;
end;
function ClassDesc.GetActionTable(i:Integer):TActionTable;cdecl; { return NULL; }
begin
MessageBox(0,"GetActTable","ClassDesc",MB_OK);
Result:=nil;
end;
function ClassDesc.IsManipulator():Boolean;cdecl; { return FALSE; }
begin
MessageBox(0,"IsManip","ClassDesc",MB_OK);
Result:=False;
end;
function ClassDesc.CanManipulate(hTarget:TReferenceTarget):Boolean;cdecl; { return FALSE; }
begin
MessageBox(0,"CanManipulate","ClassDesc",MB_OK);
Result:=False;
end;
function ClassDesc.CanManipulateNode(pNode:INode):Boolean;cdecl; { return FALSE; }
begin
MessageBox(0,"CanManipulateNode","ClassDesc",MB_OK);
Result:=False;
end;
function ClassDesc.CreateManipulator(hTarget:TReferenceTarget;pNode:INode):TManipulator;c decl; { return NULL; }
begin
MessageBox(0,"CreateManipulator","ClassDesc",MB_OK);
Result:=nil;
end;
function ClassDesc.CreateManipulator(pNode:INode):TManipulator;cdecl; {return NULL;}
begin
MessageBox(0,"CreateManipulator","ClassDesc",MB_OK);
Result:=nil;
end;
function ClassDesc.NeedsToSave():Boolean;cdecl; { return FALSE; }
begin
MessageBox(0,"NeedsToSave","ClassDesc",MB_OK);
Result:=False;
end;
function ClassDesc.Save(isave:ISave):TIOResult;cdecl; { return IO_OK; }
begin
MessageBox(0,"Save","ClassDesc",MB_OK);
result:=IO_OK;
end;
function ClassDesc.Load(iload:ILoad):TIOResult;cdecl; { return IO_OK; }
begin
MessageBox(0,"Load","ClassDesc",MB_OK);
result:=IO_OK;
end;
function ClassDesc.InitialRollupPageState():Longword;cdecl; { return 0x7fffffff; }
begin
MessageBox(0,"InitialRollupPageState","ClassDesc",MB_OK);
result:=$7fffffff;
end;
function ClassDesc.InternalName():PAnsiChar;cdecl; { return NULL; }
begin
MessageBox(0,"IntName","ClassDesc",MB_OK);
Result:=pchClassName;
end;
function ClassDesc.HInstance():Cardinal;cdecl; { return NULL; }
begin
MessageBox(0,"hInstance","ClassDesc",MB_OK);
Result:=hInstance;
end;
function ClassDesc.NumParamBlockDescs():Integer;cdecl; { return 0; }
begin
MessageBox(0,"NumParamBlockDescs","ClassDesc",MB_OK);
Result:=0;
end;
function ClassDesc.GetParamBlockDesc(i:Integer):TParamBlockDesc2;cdecl; { return NULL; }
begin
MessageBox(0,"GetParamBlockDesc","ClassDesc",MB_OK);
result:=nil;
end;
function ClassDesc.GetParamBlockDescByID(id:BlockID):TParamBlockDesc2;cdecl; { return NULL; }
begin
MessageBox(0,"GetParamBlockDescByID","ClassDesc",MB_OK);
result:=nil;
end;
procedure ClassDesc.AddParamBlockDesc(pbd:TParamBlockDesc2);cdecl; { }
begin
MessageBox(0,"AddParamBlockDesc","ClassDesc",MB_OK);
end;
procedure ClassDesc.BeginEditParams(ip:IObjParam; obj:TReferenceMaker; flags:Longword; prev:TAnimatable);cdecl; { }
begin
MessageBox(0,"BeginEditParams","ClassDesc",MB_OK);
end;
procedure ClassDesc.EndEditParams(ip:IObjParam; obj:TReferenceMaker; flags:Longword; prev:TAnimatable);cdecl; { }
begin
MessageBox(0,"EndEditParams","ClassDesc",MB_OK);
end;
procedure ClassDesc.InvalidateUI(pbd:TParamBlockDesc2);cdecl; { }
begin
MessageBox(0,"InvalidateUI","ClassDesc",MB_OK);
end;
function ClassDesc.GetRsrcString(id:INT_PTR):PAnsiChar;cdecl;
begin
MessageBox(0,"GetRsrcStr","ClassDesc",MB_OK);
Result:=nil;
end;
procedure ClassDesc.MakeAutoParamBlocks(owner:TReferenceMaker);cdecl; { }
begin
MessageBox(0,"MakeAutoParamBlocks","ClassDesc",MB_OK);
end;
function ClassDesc.NumParamMaps():Integer;cdecl; { return 0; }
begin
MessageBox(0,"NumParamMaps","ClassDesc",MB_OK);
Result:=0;
end;
function ClassDesc.GetParamMap(i:Integer):IParamMap2;cdecl; { return NULL; }
begin
MessageBox(0,"GetParamMap","ClassDesc",MB_OK);
Result:=nil;
end;
function ClassDesc.GetParamMap(pbd:TParamBlockDesc2):IParamMap2;cdecl; { return NULL; }
begin
MessageBox(0,"GetParamMap1","ClassDesc",MB_OK);
Result:=nil;
end;
procedure ClassDesc.SetUserDlgProc(pbd:TParamBlockDesc2; proc:TParamMap2UserDlgProc=nil);cdecl; { }
begin
MessageBox(0,"SetupDlgProc","ClassDesc",MB_OK);
end;
function ClassDesc.GetUserDlgProc(pbd:TParamBlockDesc2):TParamMap2UserDlgProc;cdecl; { return NULL; }
begin
MessageBox(0,"GetUserDlgProc","ClassDesc",MB_OK);
Result:=nil;
end;
...
...
function ClassDesc.SubClassID():TClass_ID;cdecl; { return Class_ID(); }
begin
MessageBox(0,"SubClassId","ClassDesc",MB_OK);
Result:=c_id;//self.ClassID();
end;
function ClassDesc.Execute(cmd:Integer; arg1:ULONG_PTR=0; arg2:ULONG_PTR=0; arg3:ULONG_PTR=0):INT_PTR;cdecl; { return 0; }
begin
MessageBox(0,"Execute","ClassDesc",MB_OK);
Result:=0;
end;
Все бы хорошо, но после вызова InitialPageRollupState происходит крэш! Как мне найти ошибку? Не наврал ли я с возвращением Pchar`ов и ClaccID()? Сам класс TClassID - простейший - два поля и несколько методов для доступа к ним. Если надо - приведу отдельно. Help!
← →
Сергей М. © (2008-08-06 21:58) [3]С чего бы везде cdecl понатыкано ?
Не понятно ..
И вообще - есть же, наверно, документация по API для плагинов к этому продукту, почему бы ее не проштудировать, прежде чем лепить невесть что ?
← →
tesseract © (2008-08-06 22:32) [4]Классы в DLL.... ИМХО там Com-server.
← →
ПЗ (2008-08-07 21:05) [5]Нет, там не COM-сервер, а к сожалению, именно классы в DLL :-( Документация есть, но не на API, а на эти самые классы. И заголовочники есть на С++. Ровно по ним все и делалось. Отсюда и cdecl везде – все методы должны иметь сишные правила вызова. Я извиняюсь за большой объем кода выше, там 90% методов для мебели сделаны. Значение имеют только то, что не в строчку записаны :-) ClassName, Category, ClassID, SuperClassID, InitialPageRollupState.
Имею следующее подозрение: Метод ClassDesc::ClassID() согласно документации и сишному заголовочнику, возвращает непосредственно экземпляр класса Class_ID. Не указатель на него, а по значению. Вопрос: а на делфи тот же метод будет у меня возвращать объект моего аналога данного класса TClass_ID по значению?
Сюда же – согласно документации, сам класс ClassDesc не имеет конструктора! Деструктор (виртуальный) есть, а конструктора нет. На делфи он есть по-умолчанию (от TObject). Это может сыграть?
← →
Сергей М. © (2008-08-08 08:13) [6]
> на делфи тот же метод будет у меня возвращать объект моего
> аналога данного класса TClass_ID по значению?
Как бы ни осуществлялся возврат (а осуществляется он в делфи всегда по ссылке), дельфийские классы никаким боком не пересекаются с сишными.
← →
oxffff © (2008-08-08 12:12) [7]
>
> Все бы хорошо, но после вызова InitialPageRollupState происходит
> крэш! Как мне найти ошибку? Не наврал ли я с возвращением
> Pchar`ов и ClaccID()? Сам класс TClassID - простейший -
> два поля и несколько методов для доступа к ним. Если надо
> - приведу отдельно. Help!
О, я как раз из отпуска приехал.
Вроде же разобрали как делать месяц назад.
Не мог бы ты сократить код до минимума с сохранением ошибки?
← →
ПЗ (2008-08-08 20:13) [8]ОК, попробую. Что в прошлый раз обсудили, работает. В чем сейчас проблема – ХЗ:
Итак, имеем:class MyUtilityClassDesc
{
public:
virtual void AAA() {}// – ХЗ №1
virtual void BBB() {}// – ХЗ №2
virtual ~MyUtilityClassDesc() { }//Деструктор. Конструктора нет!
virtual int IsPublic() { return 1; }
…
virtual const TCHAR * ClassName() { return _T("MyUtility"); }
virtual SClass_ID SuperClassID() { return 0x1020; }
virtual CClassID ClassID() { return CClass_ID(); }
virtual const TCHAR * Category() { return _T("Utility"); }
…
virtual int NumActionTables() { return 0; }
…
virtual DWORD InitialRollupPageState() { return 0x7fffffff; }
…
};
MyUtilityClassDesc.ClassID() возвращает экземпляр следующего класса:
class CClassID
{
private:
ULONG a,b;
public:
CClassID() { a = b = 0xffffffff; }
CClassID(const CClassID& cid) { a = cid.a; b = cid.b; }
CClassID(ulong aa, ulong bb) { a = aa; b = bb; }
ULONG PartA() const { return a; }
ULONG PartB() const { return b; }
void SetPartA( ulong aa ) { a = aa; }
void SetPartB( ulong bb ) { b = bb; }
int operator==(const CClassID& cid) { return (a==cid.a&&b==cid.b);}
int operator!=(const CClassID& cid) const { return (a!=cid.a||b!=cid.b); }
CClassID& operator=(const CClassID& cid) { a=cid.a; b = cid.b; return (*this); }
bool operator<(const CClassID& rhs) const { return false; }
};
В этом виде все ОК. Последовательность вызова методов выше написал. Из CClassID вызывается только конструктор.
Delphi-аналоги:
ClassDesc=class {MaxHeapOperators}
public
destructor ClassDesc();virtual;
function IsPublic():integer;virtual;cdecl;
…
function ClassName():PAnsiChar;virtual;cdecl;
function SuperClassID():SClass_ID;virtual;cdecl;
function ClassID():TClass_ID;virtual;cdecl;
function Category():PAnsiChar;virtual;cdecl;
…
function NumActionTables():Integer;virtual;cdecl;
…
function InitialRollupPageState():DWord;virtual;cdecl;
function InternalName():PAnsiChar;virtual;cdecl;
…
end;
destructor ClassDesc.ClassDesc();
begin
inherited;
end;
function ClassDesc.IsPublic():integer;cdecl;
begin
Result:=1;
end;
function ClassDesc.ClassName():PAnsiChar;cdecl;
begin
Result:=pchClassName; //const pchClassName:PAnsiChar=’Delphi’;
end;
function ClassDesc.SuperClassID():SClass_ID;cdecl;
begin
Result:=$1020;;
end;
function ClassDesc.ClassID():TClass_ID;cdecl;
begin
Result:=c_id; //var c_id:TClass_ID; c_id:=TClass_ID.Create($11,$12);
end;
function ClassDesc.Category():PAnsiChar;cdecl; etc
begin
Result:=pchCategory; //const pchCategory:PAnsiChar=’Utility’;
end;
function ClassDesc.NumActionTables():Integer;cdecl;
begin
Result:=0;
end;
function ClassDesc.InitialRollupPageState():Longword;cdecl;
begin
result:=$7fffffff;
end;
Аналог класса CClass_ID:
TClass_ID=class {MaxHeapOperators}
a,b:Longword;
public
constructor Class_ID();overload;
constructor Class_ID(var cid:TClass_ID);overload;
constructor Class_ID(aa:Longword; bb:Longword);overload;
function PartA():Longword;
function PartB():Longword;
procedure SetPartA(aa:Longword);
procedure SetPartB(bb:Longword);
function operatorEq(var cid:TClass_ID):integer;
function operatorNEq(var cid:TClass_ID):integer;
function operatorAssign(var cid:TClass_ID):TClass_ID;
function operatorLess(var cid:TClass_ID):Boolean;
end;
constructor TClass_ID.Class_ID();
begin
a:=$ffffffff;
b:=a;
end;
constructor TClass_ID.Class_ID(var cid:TClass_ID);
begin
a:=cid.a;
b:=cid.b;
end;
constructor TClass_ID.Class_ID(aa:Longword; bb:Longword);
begin
a:=aa;
b:=bb;
end;
function TClass_ID.PartA():Longword;
begin
Result:=a;
end;
function TClass_ID.PartB():Longword;
begin
Result:=b;
end;
procedure TClass_ID.SetPartA(aa:Longword);
begin
a:=aa;
end;
procedure TClass_ID.SetPartB(bb:Longword);
begin
b:=bb;
end;
function TClass_ID.operatorEq(var cid:TClass_ID):integer; begin
Result:=0;
if (a=cid.a)and(b=cid.b)then Result:=1;
end;
function TClass_ID.operatorNEq(var cid:TClass_ID):integer; begin
Result:=0;
if (a<>cid.a)or(b<>cid.b)then Result:=1;
end;
function TClass_ID.operatorAssign(var cid:TClass_ID):TClass_ID; begin
a:=cid.a;
b:=cid.b;
Result:=self;
end;
function TClass_ID.operatorLess(var cid:TClass_ID):Boolean;
begin
Result:=false;
if (a<cid.a)or((a=cid.a)and(b<cid.b))then Result:=True;
end;
В данном случае, последовательность вызовов сохраняется, все ОК, но после InitialPageRollupState имею Access Violation.
ЗЫ. Строго говоря, в SDK оба класса наследуются от единого предка, которые не имеет виртуальных методов, а содержит несколько вариантов невиртуальных операторов new и delete. Однако, в сишном примере, все и без них работает. Почему не работает на Delphi?
← →
oxffff © (2008-08-09 14:54) [9]
> ПЗ (08.08.08 20:13) [8]
При очень беглом обзоре на [8]
(в понедельник на работе я постараюсь посмотреть более внимательно).
Я нашел следующее несоответствие.
ClassDesc=class {MaxHeapOperators}
public
destructor ClassDesc();virtual;
...
destructor ClassDesc.ClassDesc();
begin
inherited; <-этот вызов может приводить к AV.
end;
Поскольку в Delphi указатель на VMT по отрицательным смещениям имеет виртуальные методы. В том числе и на начальный деструктор.
В С++ этот виртуальный деструктор будет расположен по положительным смешениям. В этом будет не соответствие.
← →
oxffff © (2008-08-09 14:57) [10]
> oxffff © (09.08.08 14:54) [9]
Может приводить поскольку я не знаю по твоему коду описание декструктора MaxHeapOperators.
Если он перекрывает стандартный декструктор, то проблемы гарантированы на 100%. Если вызов стандартного перекрытого не используется, тогда проблем быть не должно.
← →
ПЗ (2008-08-09 20:18) [11]Нет, точно не здесь. Деструктор вызывается (я контролировал) после закрытия 3ds, когда все библиотеки выгружаются. А у меня они и загрузиться не успевают :-)))
На всякий случай, вот код этого самого MaxHeapOperators:
#ifdef BLD_UTIL
#define UtilExport __declspec( dllexport )
#else
#define UtilExport __declspec( dllimport )
#endif
class MaxHeapOperators
{
public:
UtilExport static void* operator new( size_t size );
UtilExport static void* operator new( size_t size, const std::nothrow_t & e );
UtilExport static void* operator new( size_t size, const char* filename, int line );
UtilExport static void* operator new( size_t size, const std::nothrow_t & e, const char * filename, int line );
UtilExport static void* operator new( size_t size, unsigned long flags );
UtilExport static void* operator new( size_t size, const std::nothrow_t & e, unsigned long flags );
UtilExport static void* operator new[]( size_t size );
UtilExport static void* operator new[]( size_t size, const std::nothrow_t & e );
UtilExport static void* operator new[]( size_t size, const char* filename, int line );
UtilExport static void* operator new[]( size_t size, const std::nothrow_t & e, const char * filename, int line );
UtilExport static void* operator new[]( size_t size, unsigned long flags );
UtilExport static void* operator new[]( size_t size, const std::nothrow_t & e, unsigned long flags );
UtilExport static void operator delete( void * ptr );
UtilExport static void operator delete( void * ptr, const std::nothrow_t& e );
UtilExport static void operator delete( void * ptr, const char * filename, int line );
UtilExport static void operator delete( void * ptr, const std::nothrow_t & e, const char * filename, int line );
UtilExport static void operator delete( void * ptr, unsigned long flags );
UtilExport static void operator delete( void * ptr, const std::nothrow_t & e, unsigned long flags );
UtilExport static void operator delete[]( void * ptr );
UtilExport static void operator delete[]( void * ptr, const std::nothrow_t& e );
UtilExport static void operator delete[]( void * ptr, const char * filename, int line );
UtilExport static void operator delete[]( void * ptr, const std::nothrow_t& e, const char * filename, int line );
UtilExport static void operator delete[]( void * ptr, unsigned long flags );
UtilExport static void operator delete[]( void * ptr, const std::nothrow_t&e, unsigned long flags );
UtilExport static void* operator new( size_t size, void * placement_ptr );
UtilExport static void operator delete( void * ptr, void * placement_ptr );
};
Реализацию опускаю за тривиальностью. Есть подозрение, что неизвестные методы AAA() и BBB(), которые мне пришлось добавить в сишной реализации - это конструктор по-умолчанию и конструктор копий. Где-то в MSDN попадалось, что они должны автоматически добавляться к любому классу. Тем более, что BBB() на этапе загрузки ВЫЗЫВАЕТСЯ. Я только сегодня это обнаружил, вставив MessageBox.
← →
ПЗ (2008-08-11 21:00) [12]Сегодня попробовал отладчиком смотреть, но адреса AV все время разные :-(
← →
oxffff © (2008-08-14 08:51) [13]Почему у тебя не совпадают позиции соответствующих методов в виртуальных таблицах?
Итак, имеем: class MyUtilityClassDesc
{
public:
virtual void AAA() {}// – ХЗ №1
virtual void BBB() {}// – ХЗ №2
virtual ~MyUtilityClassDesc() { }//Деструктор. Конструктора нет!
virtual int IsPublic() { return 1; }
ClassDesc=class {MaxHeapOperators}
public
destructor ClassDesc();virtual;
function IsPublic():integer;virtual;cdecl;
…
function ClassName():PAnsiChar;virtual;cdecl;
function SuperClassID():SClass_ID;virtual;cdecl;
function ClassID():TClass_ID;virtual;cdecl;
function Category():PAnsiChar;virtual;cdecl;
То есть при вызове ClassDesc.IsPublic(Delphi) у тебя будет вызываться
MyUtilityClassDesc.BBB(C++)
← →
ПЗ (2008-08-14 21:11) [14]Я выше написал - методы AAA, BBB пришлось добавлять только на C-реализации. На Delphi они не нужны (последовательность вызовов тогда нарушается). На delphi первым (как и должно быть) вызывается ClassDesc.SuperClassID, потом ClassDesc.ClassID, ClassDesc.SuperClassID, ClassDesc.IsPublic, ClassDesc.ClassID, ClassDesc.Category, ClassDesc.InitialRollupState. После этого происходит крэш.
Аналогичная последовательность (я проверил) у настоящих фирменных плагинов, написанных на C++ с наследованием от заголовочников и LIB из SDK.
Когда я попробовал написать MyUtilityClassDesc (выше) на С++ БЕЗ LIB из SDK (скопировал класс из заголовочника), у меня первым вызвался не SuperClassID, а Category(), который на две позиции ниже в VMT (что вызывало крэш, ессно). Экспериментальным путем я добавил два виртуальных метода и все заработало.
Сейчас проверил еще раз сишный вариант. BBB() вызывается перед IsPublic. ААА() не вызывается. ХЗ что это такое? М.б. конструктор копий там должен был быть? В заголовочнике SDK его нет.
← →
ПЗ (2008-08-14 21:19) [15]На всякий случай, попробую еще внести ясность. Итак имею:
1. "Совсем честную" DLL на С++ с использованием *.LIB и *.Н из SDK:
class MyUtilityClassDesc:public ClassDesc
где
class ClassDesc: public MaxHeapOperators - оба сидят в SDK.
Это честный павильный плагин, который работает. Методы вызывыются в последовательности, описанной выше.
2. "Не совсем честную" DLL на С++ без использования *.LIB и *.Н из SDK:
class MyUtilityClassDesc - сидит в самой DLL, ни от чего не наследуется.
Все работает, при условии добавления ААА,ВВВ. Последовательность вызовов как в п.1.
3."Совсем не честную" DLL на Delphi без использования *.LIB и *.Н из SDK:
ClassDesc=class ... end; - сидит в самой DLL, ни от чего не наследуется.
Никаких доп. методов не требуется. Последовательность вызовов совпадает с п.1, но после InitialPageRollupState крэш :-(
Уф....
← →
oxffff © (2008-08-14 22:44) [16]
> М.б. конструктор копий там должен был быть?
Обычный конструктор и конструктор копий в С++ не может быть виртуальным. Рекомендую посмотреть под отладчиком сами вызовы в честных и не честных случаях и тогда все станет кристально понятно.
← →
ПЗ (2008-08-15 21:02) [17]А что понимается под "посмотреть под отладчиком сами вызовы"? Исходников SDK у меня нет, только заголовочники. Брейкпоинты в методы я вставлял, но это то же, что и MessageBox. Что еще можно посмотреть?
← →
oxffff © (2008-08-15 21:32) [18]
> ПЗ (15.08.08 21:02) [17]
Посмотри внимательно, как происходит вызов метода и как и куда кладутся параметры на ассемблере в 1,2,3 из [15].
1. Посмотри как происходит сам вызов через таблицу VMT(какие смещения используются), сравни и найди разницу.
2. Корректно ли передаются параметры. И то ли ты передаешь в случае с Delphi.
Соответственно так ты сможешь найти причину.
Поскольку меня смущает твоя фраза
....
> Все работает, при условии добавления ААА,ВВВ. Последовательность
> вызовов как в п.1.
....
>... Delphi
> Никаких доп. методов не требуется.
По твоему получается, что VMT смещения в Delphi начинаются с нуля.
А на примере 2 для С++ начинаюся с "+8". Так быть не может.
← →
ПЗ (2008-08-16 20:22) [19]ОК, буду пробовать. В ассемблере я совсем не варю. В классе TObject я насчитал как минимум 8 виртуальных методов. Все они, как я понимаю, автоматом переползают в мой ClassDesc из-за наследования.
← →
ПЗ (2008-08-18 20:48) [20]Вопрос по отладчику: как посмотреть вызов метода, если его вызывает чужая программа?
Я поставил брейкпоинт на нужный метод, и ессно, вижу внутренности этого метода, когда прога останавливается на брейкпоинте. А как мне посмотреть сам момент его вызова?
← →
oxffff © (2008-08-18 21:09) [21]Поставить бряк при выходе из процедуры на вызове.
← →
ПЗ (2008-08-19 21:01) [22]Эээ... Так при выходе или на вызове? Если второе, то как это сделать? Вызов из другой программы идет. У меня исходников 3ds нет.
На выходе я ставил, но после ret содержимое регистров меняется. Чистоты эксперимента нет.
← →
oxffff © (2008-08-19 21:33) [23]
> ПЗ (19.08.08 21:01) [22]
Выходишь(из процедуры) по ret и мотаешь вверх(назад) до вызова процедуры. Ставишь бряк там. Смотришь передачу параметров и сам вызов.
Соблюдая терпение(если их будет несколько), попадаешь в свою процедуру.
← →
ПЗ (2008-08-25 21:27) [24]Жесть... Похоже один из методов врет с передачей параметров (которых нет) или с соглашением о вызове. Где-то неверно очищается стек. Идет CALL, но перед RET корректный адрес возврата в стее оказываетеся на одну позицию выше, чем надо. В результате - возврат в пустоту...
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2009.10.18;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.014 c