Мне нужно вызвать некоторый код C ++ из Delphi. Код C ++ должен иметь возможность обратного вызова в код Delphi в ответ. Пример, показанный здесь Вызов функции обратного вызова в Delphi из C ++ DLL работает на отлично Однако вместо того, чтобы передавать в C ++ одну функцию Delphi в качестве обратного вызова, я хотел бы передать объект Delphi, реализующий интерфейс.
Изменить: По интерфейсу я имею в виду терминологию C ++, которая является классом с чисто виртуальными функциями. Это не обязательно тип, определенный с Delphi interface
ключевое слово. Другими словами, следующий класс определяет интерфейс, который я хотел бы вызвать из C ++:
ICallable = class
procedure callMe stdcall; virtual; abstract;
procedure CallMeAgain stdcall; virtual; abstract;
end;
ICallable
Интерфейс, в свою очередь, будет реализован в Delphi следующим образом:
MyCallable = class(ICallable)
procedure callMe override;
procedure callMeAgain override;
end;
procedure MyCallable.callMe
begin
WriteLine('I was called');
end;
procedure MyCallable.callMeAgain
begin
WriteLine('I was called again');
end;
На стороне C ++, которая скомпилирована как DLL, я хочу определить интерфейс ICallable следующим образом:
class ICallable{
public:
virtual void callMe()=0;
virtual void callMeAgain()=0;
}
И экспортируйте следующую функцию DLL, чтобы она могла быть вызвана Delphi:
#define DllExport extern "C" __declspec( dllexport )
DLLExport bool Callback(ICallable* callable){
callable->callMe();
callable->callMeAgain();
return true;
}
И наконец вернемся в Delphi:
function Callback(myCallable: ICallable) : Boolean cdecl; external 'dllname'
Вопрос:
Это может работать только на C ++, и Delphi реализует свои таблицы виртуальных методов таким же образом. Это так?
Первоначально я думал, что класс Delphi не имеет VMT, совместимого с классом C ++. Я думал, что, потому что все классы Delphi происходят из TObject
который объявляет виртуальные методы. Эти виртуальные методы появляются в VMT. Я предполагал, что эти методы появятся первыми в VMT. Однако выясняется, что компилятор организует, что встроенные виртуальные методы TObject
имеют отрицательные показатели в VMT. Это означает, что пользовательские виртуальные методы (те, которые определены в подклассах TObject
) начать с индекса 0.
Это означает, что классы Delphi и C ++ в вашем коде, о котором идет речь, действительно имеют совместимые VMT. Я считаю, что этот выбор дизайна был сделан для поддержки COM в более ранних версиях Delphi. В подтверждение своих утверждений я отсылаю вас к документация говорит, с моим акцентом:
Компоновка VMT показана в следующей таблице. На 32-разрядных платформах с положительным смещением VMT состоит из списка 32-разрядных указателей на методы (указатели на 64-разрядные методы на 64-разрядной платформе) — по одному на каждый пользовательский виртуальный метод в классе type- в порядке декларации. Каждый слот содержит адрес соответствующей точки входа виртуального метода. Этот макет совместим с C ++ v-таблицей и с COM. При отрицательных смещениях VMT содержит ряд полей, которые являются внутренними для реализации Delphi. Приложения должны использовать методы, определенные в TObject, для запроса этой информации, поскольку в будущих реализациях языка Delphi компоновка может измениться.
Следует подчеркнуть, что ничто в стандарте C ++ не обязывает использовать VMT для виртуальных методов, тем более как реализованы VMT. В действительности, каждый основной компилятор Windows имеет VMT, реализованные таким образом для поддержки COM.
Вместо того чтобы полагаться на такие детали реализации, вы можете использовать интерфейсы Delphi. Однако, как вы знаете, это COM-интерфейсы, поэтому вы должны реализовать IUnknown
, Вы говорите, что хотите избежать механизма COM, но вам нужно только добавить IUnknown
, Это не особенно обременительно, на мой взгляд. Я понимаю, что вы думаете, что вам нужно зарегистрировать CLSID, реализовать фабрики классов и так далее. Вы не Вам просто нужно реализовать IUnknown
,
Во всяком случае, если вы действительно настроены избегать IUnknown
тогда вы не можете использовать интерфейсы Delphi и иметь два варианта, насколько я могу судить:
TObject
использует отрицательные индексы VMT для своих встроенных виртуальных методов.Компилируемый для кода вариант 2 выглядит так:
Delphi
{$APPTYPE CONSOLE}
type
ICallable = class
public
procedure CallMe cdecl; virtual; abstract;
procedure CallMeAgain cdecl; virtual; abstract;
end;
MyCallable = class(ICallable)
public
procedure CallMe; override;
procedure CallMeAgain; override;
end;
procedure MyCallable.CallMe;
begin
Writeln('CallMe');
end;
procedure MyCallable.CallMeAgain;
begin
Writeln('CallMeAgain');
end;
const
dllname = 'C:\Users\heff\Desktop\Win32Project1\Debug\Win32Project1.dll';
function Callback(Callable: ICallable): Boolean; cdecl; external dllname;
var
Callable: ICallable;
begin
Callable := MyCallable.Create;
Writeln(Callback(Callable));
Callable.Free;
Readln;
end.
C ++
#include <Windows.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
class ICallable
{
public:
virtual void CallMe() = 0;
virtual void CallMeAgain() = 0;
};
extern "C" __declspec(dllexport) bool Callback(ICallable* callable)
{
callable->CallMe();
callable->CallMeAgain();
return true;
}
Выход
Позвоните мне Перезвони мне ПРАВДА
Других решений пока нет …