Форум: "Основная";
Текущий архив: 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
- DFoo не работает :-( Где наврал?
{
public:
virtual char AFoo()=0;
virtual char BFoo()=0;
char DFoo() {return 0;};
};
← →
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.004 c