Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.10.18;
Скачать: CL | DM;

Вниз

В продолжение темы про 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;
Скачать: CL | DM;

Наверх




Память: 0.6 MB
Время: 0.019 c
6-1207054869
SpellCaster
2008-04-01 17:01
2009.10.18
Асинхронные сокеты "забивают" очередь сообщений


2-1249568385
caps14
2009-08-06 18:19
2009.10.18
перемещение изображений по форме


2-1250152468
JohnKorsh
2009-08-13 12:34
2009.10.18
Как программно управлять светодиодами клавиатуры?


2-1250593303
Miklyha
2009-08-18 15:01
2009.10.18
Не срабатывает Form1.Close;


15-1250678829
DSKalugin
2009-08-19 14:47
2009.10.18
Переведите пожалуйста с паскаля на php