Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2009.09.27;
Скачать: [xml.tar.bz2];

Вниз

Delphi, Dll, классы, C++   Найти похожие ветки 

 
ПЗ   (2008-07-08 20:22) [0]

Возник вопрос:
Как вызывать в программе С++ методы класса, написанного на delphi в Dll?
Всё пляшет от SDK для 3dsMAX, который, как известно, хочет, чтобы все плагины писались на MSVC. Провел эксперимент. Написал dll на delphi:

library MyDLL;
uses
 SysUtils,
 Classes;

{$R *.res}
type
 TFoo= class
 public
    function AFoo:Char;virtual;cdecl;
 end;
var Foo:TFoo;
   PFoo:^TFoo;

function TFoo.AFoo:Char;
begin
 Result:="A";
end;

function Func1:Pointer;
begin
    PFoo:=@Foo;
    Result:=PFoo;
end;
exports
Func1;
begin
 Foo:=TFoo.Create;
end.

Написал клиента на Delphi, проверил, работает: экспортируемая функция возвращает указатель на переменную класса TFoo, чей метод успешно вызывается.
Попробовал написать клиента на CPP:

#include "stdafx.h"

class CFoo
{
public:
virtual char AFoo()=0;
};

typedef void* (__cdecl* PFUNC)();

int main(int argc, char* argv[])
{
printf("Hello World!\n");

HMODULE hLibrary=LoadLibrary("MyDLL.Dll");
PFUNC Func1= (PFUNC) GetProcAddress(hLibrary,"Func1");

CFoo* Foo=(CFoo*)Func1();
char ch=Foo->AFoo();
printf(&ch);

FreeLibrary(hLibrary);


return 0;
}

Вылетает на вызове метода. Неужели настолько отличаются бинарники, что нельзя обмануть CPP и вызвать метод дельфевого класса?


 
Eraser ©   (2008-07-08 20:27) [1]

> [0] ПЗ   (08.07.08 20:22)

обектные модели в Delph и C++ несколько отличаются друг от друго, поэтому нельзя вот так просто взять и приравнять. Но вызвать думаю можно, но как просто статичную функцию, естесственно доступа к полям не будет.


 
palva ©   (2008-07-08 20:47) [2]

Доступ к полям будет, если научиться при обращении к функции передавать в качестве дополнительного параметра адрес объекта. Но это надо посмотреть по окну CPU как там все организовано. По моему адрес объекта - первый параметр.


 
Eraser ©   (2008-07-09 00:27) [3]

> [2] palva ©   (08.07.08 20:47)

это да, но прийдется вручную организовывать объектную модель Делфи на C++ ) но в качестве частного решения можно конечно упростить и просто жестко прошить смещения.


 
icWasya ©   (2008-07-09 09:14) [4]

Можно попытаться использовать интерфейсы - вот у них всё одинаково


 
oxffff ©   (2008-07-09 11:18) [5]


> ПЗ   (08.07.08 20:22)  


У тебя ошибка в коде.


> function Func1:Pointer;
> begin
>     PFoo:=@Foo;
>     Result:=PFoo;
> end;


Ты передаешь двойной указатель на объект.

Нужно просто

function Func1:Pointer;
begin
Result:=Foo;
end;


 
oxffff ©   (2008-07-09 11:26) [6]

Также естественным условием interoperability является гарантия обеспечения смещение VMT равным 0.
Поэтому объявление хотя бы одного pure virtual метода должно быть первым.
И только после этого объявлять поля.
Правда возможно придется поплясать с выравниванием. :)


 
Dimka Maslov ©   (2008-07-09 11:37) [7]

Только использование COM-интерфейсов гарантирует совместимость.


 
han_malign ©   (2008-07-09 12:20) [8]


> Также естественным условием interoperability является гарантия
> обеспечения смещение VMT равным 0.

- для класса это гарантированно, но лучше делать "по правильному":
 TXFoo= class
 public
    function AFoo:Char;virtual;abstract;cdecl;
 end;
 TFoo= class(TXFoo)
 private
     ..........
 public
    function AFoo:Char;override;cdecl;
 end;


virtual char AFoo()=0; - соглашение о вызове зависит от ключей компиляции обычно это __thiscall или __fastcall, поэтому:
virtual char __cdecl AFoo()=0;


 
oxffff ©   (2008-07-09 14:22) [9]


> han_malign ©   (09.07.08 12:20) [8]
>
> > Также естественным условием interoperability является
> гарантия
> > обеспечения смещение VMT равным 0.
>
> - для класса это гарантированно,
У классов С++ смещение плавающее поэтому, чтобы оно было такое как нужно для interoperability с Delphi необходимо объявить С++ виртуальный метод первым до объявление полей класса.

>но лучше делать "по правильному":

>
>  TXFoo= class
>  public
>     function AFoo:Char;virtual;abstract;cdecl;
>  end;

>  TFoo= class(TXFoo)
>  private
>      ..........
>  public
>     function AFoo:Char;override;cdecl;
>  end;
>

И что же здесь правильного?


 
ПЗ   (2008-07-09 20:37) [10]

Да действительно, с Result:=Foo MSVC DLLку заглотил! Простейший случай заработал, спасибо!
Насчет полей, вроде, можно не заморачиваться. В классах 3dsMSAX поля не значатся, весь обмен информацией через методы. Но есть засада: не все методы у них виртуальные. Как быть? Обычные методы MSVC заглотит?

А как быть с наследованием? SDK для C++ содержит целую иерархию классов в своих сишных LIBах. Переписав все методы в один класс на делфи (с учетом соглашения о вызовах), будет ли это аналогично наследованию с точки зрения клиентской стороны (3dsMAX\MSVC)?

ЗЫ. COM оно к сожалению тоже не поддерживает. Autodesk все еще в каменном веке живет :-) Если б поддерживало, проблем бы не было.


 
oxffff ©   (2008-07-09 21:34) [11]


> Как быть? Обычные методы MSVC заглотит?


Cвязывание обычных методов придется делать через прямой их экспорт в виде сигнатура метода + первый параметр указатель на объект + calling convention. Либо через виртуальные обертки.


> А как быть с наследованием? SDK для C++ содержит целую иерархию
> классов в своих сишных LIBах. Переписав все методы в один
> класс на делфи (с учетом соглашения о вызовах), будет ли
> это аналогично наследованию с точки зрения клиентской стороны
> (3dsMAX\MSVC)?


Наследование - это прямая агрегация с совпадающим временем жизни.
Главное чтобы VMT у С++ классов был на месте.
В противном случае все равно все решаемо.


 
oxffff ©   (2008-07-09 21:47) [12]


> А как быть с наследованием? SDK для C++ содержит целую иерархию
> классов в своих сишных LIBах. Переписав все методы в один
> класс на делфи (с учетом соглашения о вызовах), будет ли
> это аналогично наследованию с точки зрения клиентской стороны
> (3dsMAX\MSVC)?


Ничего тебе не мешает сделать туже иерархию в Delphi, несмотря даже на множественное наследование (сделаешь как одиночное в порядке следования виртуальных методов в VMT).
Далее виртуальное наследование С++ тоже не проблема, смысл в том агрегат разделяется. И обращение к нему косвенное через другую таблицу vbptr.


> ЗЫ. COM оно к сожалению тоже не поддерживает. Autodesk все
> еще в каменном веке живет :-) Если б поддерживало, проблем
> бы не было.


COM - это обыкновенный указатель на абстрактный класс. С семантикой подсчета ссылок. То есть все тоже самое.


 
ПЗ   (2008-07-10 20:18) [13]

Так, я понял, что я не первый, кто этим занимается, и это радует. Попробую задавать вопросы более последовательно. Итак, имею класс на пасе:
 TFoo= class
 public
    function AFoo:Char;virtual;cdecl;
    function BFoo:Char;virtual;cdecl;
    function DFoo:Char;cdecl;
 end;
Хочу загнать его в DLL и вызвать из клиента, написанного на MSVC (в идеале вызывать будет 3dsMAX). Вопросы:
а) Какие особенности надо соблюдать при написании пас-dll
б) Как правильно определить заголовок класса на CPP ? Попробовал class CFoo
{
public:
virtual char AFoo()=0;
virtual char BFoo()=0;
char DFoo() {return 0;};
};
- DFoo не работает :-( Где наврал?


 
oxffff ©   (2008-07-10 22:07) [14]


> Так, я понял, что я не первый, кто этим занимается, и это
> радует.


Я этим не занимался.  :)

Однако внимательно ли ты читал мой пост?
oxffff ©   (09.07.08 21:34) [11]

1.

TFoo= class
public
   function AFoo:Char;virtual;cdecl;
   function BFoo:Char;virtual;cdecl;
   function DFooImpl:Char;cdecl;
   function DFoo:Char;cdecl;virtual;cdecl;
        begin
        result:=DFooImpl;
       end;
end;

2.TFoo= class
public
   function AFoo:Char;virtual;cdecl;
   function BFoo:Char;virtual;cdecl;
   function DFoo:Char;cdecl;
end;

function DFooExport(obj:TFoo;ParamList):Res;cdecl;
begin
result:=obj.DFoo(ParamList);
end;

exports DFooExport name "DFoo";

C++

__declspec ....DFooExternal
{
public:
virtual char AFoo()=0;
virtual char BFoo()=0;
char DFoo() { DFooExternal(this .........  };
};


 
ПЗ   (2008-07-11 20:26) [15]

Oxffff:Не получается так. Заголовочники классов предопределены SDK и менять я их не смогу. В реальности мне надо будет реализовать на делфи нечто в таком роде:

#ifdef BLD_PARAMBLK2
# define PB2Export __declspec( dllexport )
#else
# define PB2Export __declspec( dllimport )
#endif
class ClassDesc2 : public ClassDesc
{
private:
 Tab<ParamBlockDesc2*> pbDescs;  // parameter block descriptors
 Tab<IParamMap2*>  paramMaps;  // any current param maps
 IAutoMParamDlg*   masterMDlg;  // master material/mapParamDlg if any
 IAutoEParamDlg*   masterEDlg;  // master EffectParamDlg if any

protected:
 void SetMParamDlg(IAutoMParamDlg* dlg) { masterMDlg = dlg; }
 void SetEParamDlg(IAutoEParamDlg* dlg) { masterEDlg = dlg; }
 Tab<IParamMap2*>& GetParamMaps() { return paramMaps; }

public:
 PB2Export    ClassDesc2();
 PB2Export      ~ClassDesc2();
 PB2Export void   ResetClassParams(BOOL fileReset);
 PB2Export int   NumParamBlockDescs() { return pbDescs.Count(); }
 PB2Export ParamBlockDesc2* GetParamBlockDesc(int i) { return pbDescs[i]; }
 PB2Export ParamBlockDesc2* GetParamBlockDescByID(BlockID id);
 PB2Export ParamBlockDesc2* GetParamBlockDescByName(MCHAR* name);
 PB2Export void   AddParamBlockDesc(ParamBlockDesc2* pbd);
 PB2Export void   ClearParamBlockDescs() { pbDescs.ZeroCount(); }
 PB2Export void   BeginEditParams(IObjParam *ip, ReferenceMaker* obj, ULONG flags, Animatable *prev);
 PB2Export void   EndEditParams(IObjParam *ip, ReferenceMaker* obj, ULONG flags, Animatable *prev);
 PB2Export void   InvalidateUI();
 PB2Export void   InvalidateUI(ParamBlockDesc2* pbd);
 PB2Export void   InvalidateUI(ParamBlockDesc2* pbd, ParamID id, int tabIndex=-1); // nominated param
 PB2Export void   MakeAutoParamBlocks(ReferenceMaker* owner);
 PB2Export int   NumParamMaps() { return paramMaps.Count(); }
 PB2Export IParamMap2* GetParamMap(int i) { return paramMaps[i]; }
 PB2Export IParamMap2* GetParamMap(ParamBlockDesc2* pbd, MapID map_id = 0);
 PB2Export void   SetUserDlgProc(ParamBlockDesc2* pbd, MapID map_id, ParamMap2UserDlgProc* proc=NULL);
 inline void SetUserDlgProc(ParamBlockDesc2* pbd, ParamMap2UserDlgProc* proc=NULL) { SetUserDlgProc(pbd, 0, proc); }
 PB2Export ParamMap2UserDlgProc* GetUserDlgProc(ParamBlockDesc2* pbd, MapID map_id = 0);
 PB2Export IAutoMParamDlg* CreateParamDlgs(HWND hwMtlEdit, IMtlParams *imp, ReferenceTarget* obj);
 PB2Export IAutoMParamDlg* CreateParamDlg(BlockID id, HWND hwMtlEdit, IMtlParams *imp, ReferenceTarget* obj, MapID mapID=0);
 PB2Export IAutoEParamDlg* CreateParamDialogs(IRendParams *ip, SpecialFX* obj); // mjm - 07.06.00
 PB2Export IAutoEParamDlg* CreateParamDialog(BlockID id, IRendParams *ip, SpecialFX* obj, MapID mapID=0); // mjm - 07.06.00
 PB2Export void   MasterDlgDeleted(IAutoMParamDlg* dlg);
 PB2Export void   MasterDlgDeleted(IAutoEParamDlg* dlg);
 PB2Export IAutoMParamDlg* GetMParamDlg() { return masterMDlg; }
 PB2Export IAutoEParamDlg* GetEParamDlg() { return masterEDlg; }
 PB2Export void   RestoreRolloutState();
 PB2Export ParamID  LastNotifyParamID(ReferenceMaker* owner, IParamBlock2*& pb);
 PB2Export void   Reset(ReferenceMaker* owner, BOOL updateUI = TRUE, BOOL callSetHandlers = TRUE);
 PB2Export void   GetValidity(ReferenceMaker* owner, TimeValue t, Interval &valid);
};

Он наследуется от другого класса(у которого все методы виртуалтьные), от него еще кто-то наследуется. Клиент (MAX) получает ссылку на экземпляр класса и сам вызывает методы как хочет.  Вопрос: как реализовать этот класс не на сях, а на делфи, и скормить его сишному клиенту (MAX)? Универсальность мне не нужна и многие методы, думаю, можно заглушками сделать, т.к. они вряд ли реально  будут все вызываться.


 
oxffff ©   (2008-07-11 21:31) [16]


> ПЗ   (11.07.08 20:26) [15]


Я честно говоря не могу понять что тебе необходимо?
То ли вызов метода Pascal объекта из С++, то ли наоборот. :)
Давай "чистый" пример.


 
oxffff ©   (2008-07-11 21:43) [17]


> Вопрос: как реализовать этот класс не на сях, а на делфи,
>  и скормить его сишному клиенту (MAX)?


Тебе нужно сделать привязку внешней процедуры (сигнатура метода + параметер ссылка на объект) Delphi к методу С++(просто сигнатура).


 
Сергей М. ©   (2008-07-12 18:55) [18]


> Всё пляшет от SDK


Не-а..

Все "пляшет" от головы того , кто трындит тут про тот или иной SDK.

Ты, чудо, отладчик-то пошльзовал ?

Ну и что он, отладчик говорит про соглашение о вызове ?

Только не говори, мол. я твоя не понимайт - сразу Орешник подрастет)..


 
ПЗ   (2008-07-12 20:05) [19]

oxffff: Не совсем так. SDK, как известно, представляет собой набор заголовочников *.h и LIBов, где предопределена некая структура классов (пример я выше привел). Предполагается, что программист, подключая данные заголовочники, напишет на MSVC DLL, в которой, собственно, реализует экземпляры данных классов. 3ds подключает DLL, получает ссылку на экземпляр нужного ей класса и работает с ним.
В задаче спрашивается - как написать эту DLL не на C++, а на Delphi?

Именно для этого я выше написал эксперименты, с которыми вы мне помогли. Теоретически должно работать. Сейчас заткнулся на невиртуальных методах. Проблему выше описал.


 
ПЗ   (2008-07-18 21:00) [20]

Собрался с мыслями. Попробую уточнить задачу:
-Есть сторонняя программа (3dsMAX), написанная на С++
-Есть структура класса (ClassDesc2, см выше), реализация которого надо зашить в DLL

Вопрос: Как сделать эту DLL на Delphi?
Уточнение: Как реализовать на Delphi невиртуальные методы, чтобы 3ds их смогла вызвать обычным для себя образом?



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2009.09.27;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.52 MB
Время: 0.005 c
15-1248936484
123123
2009-07-30 10:48
2009.09.27
rsync


15-1248938142
desc
2009-07-30 11:15
2009.09.27
Справочная и оперативная информация.


15-1248920438
Skyle
2009-07-30 06:20
2009.09.27
VBScript, куда делся класс?


4-1217409567
leonidus
2008-07-30 13:19
2009.09.27
Корректно ли так устанавливать глобальный хук на клавиатуру?


2-1248266549
Kolan
2009-07-22 16:42
2009.09.27
Как поймать клик на перекрытом дочерними родительском контроле?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский