Is DELPHI and CMT compatible?

I need to call some C code from Delphi. The C code needs to be able to call back into the Delphi code. The example Calling a callback function in Delphi from a C++ DLL shown here is very effective. However, I want to pass a Delphi object that implements an interface, instead of passing a single Delphi function to C as a callback.

Edit: By interface, I mean C terminology, which is a pure virtual The class of the function. This is not necessarily the type defined using the Delphi interface keyword. In other words, the following class defines the interface I want to call from C:

ICallable = class 
procedure callMe stdcall; virtual; abstract;
procedure CallMeAgain stdcall; virtual; abstract;
end;

The ICallable interface will be implemented in Delphi again, as shown below :

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;

On the C side, compiled to DLL, I want to define the ICallable interface as follows:

class ICallable{
public:
virtual void callMe()=0;
virtual void callMeAgain()=0;
}

And export the following DLL functions for Delph i can call it:

#define DllExport extern "C" __declspec( dllexport )

DLLExport bool Callback(ICallable* callable){
callable->callMe();
callable->callMeAgain();
return true;
}

Finally back to Delphi:

function Callback(myCallable: ICallable): Boolean cdecl; external'dllname'

Question:

>This can only be implemented in the same way in C and Delphi Only valid when the virtual method table. Is that true?

This can only work of C++ and Delphi implement their virtual method tables in the same way. Is it the case?

I originally thought that the Delphi class does not have a VMT compatible with the C class. I think this is because all Delphi classes are derived from TObject, which declares virtual Methods. These virtual methods appear in VMT. I assume that these methods first appeared in VMT. However, it was found that the compiler arranges the built-in virtual methods of TObject to have a negative index in VMT. This means that user-defined virtual methods (in the child of TObject) Those defined in the class) start at index 0.

This means that the Delphi and C classes in the problem code do have compatible VMT. I believe this design choice is to support COM in earlier versions of Delphi . To support my claim, I suggest you participate in the documentation and emphasize:

The layout of a VMT is shown in the following table. On the 32-bit platforms, at positive offsets, a VMT consists of a list of 32-bit method pointers (64-bit method pointers on the 64-bit platform)–one per user-defined virtual method in the class type–in order of declaration. Each slot contains the address of the corresponding entry point of the virtual method. This layout is compatible with a C++ v-table and with COM. At negative offsets, a VMT contains a numbe r of fields that are internal to Delphi’s implementation. Applications should use the methods defined in TObject to query this information, since the layout is likely to change in future implementations of the Delphi language.

should It is emphasized that nothing in the C standard requires the use of VMT for virtual methods, let alone the implementation of VMT. In fact, every mainstream Windows compiler has VMT implemented in this way to support COM.

You can use Delphi interfaces instead of relying on such implementation details. However, as you know, these are COM interfaces, so you must implement IUnknown. You say you want to avoid COM mechanisms, but the only thing you need What I added is IUnknown. In my opinion, this is not particularly onerous. I think you think you need to register CLSID, implement class factories, etc. You don’t. You only need to implement IUnknown.

Anyway, If you really start to avoid using IUnknown then you cannot use the Delphi interface, and I can tell it there are two options:

>Manually implement VMT in Delphi code. VMT is just an array of function pointers. This Code that will lead you to look like the way COM does in C. It is entirely possible, but not very pleasant.
>Use the method outlined in the question and rely on TObject’s implementation of using negative VMT indexes for its built-in virtual methods Details.

Compilable code option 2 is as follows:

Delphi

{$APPTYPE CONSOLE}
< br />type
ICallable = class
public
procedure CallMe cdecl; virtual; abstract;
procedure CallMeAgain cdecl; virtual; abstract;
end;

MyCallable = class(ICallable)
public
proce dure CallMe; override;
procedure CallMeAgain; override;
end;

procedure MyCallable.CallMe;
begin
Writeln('CallMe');< br />end;

procedure MyCallable.CallMeAgain;
begin
Writeln('CallMeAgain');
end;

const< br /> 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 
< br />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_THRE AD_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;
}

< p>Yield

CallMe
CallMeAgain
TRUE

I need Delphi calls some C code. The C code needs to be able to call back into the Delphi code. The example Calling a callback function in Delphi from a C++ DLL shown here is very effective. However, I want to pass a Delphi object that implements the interface instead of a single The Delphi function is passed to C as a callback.

Edit: Through the interface, I am referring to the C terminology, which is a class with pure virtual functions. This is not necessarily defined using the Delphi interface keyword In other words, the following class defines the interface I want to call from C:

ICallable = class 
procedure callMe stdcall; virtual; abstract;< br /> procedure CallMeAgain stdcall; virtual; abstract;
end;

The ICallable interface will be implemented in Delphi again, as shown below:

MyCallable = class(ICallable)
pro cedure 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;

In C End, compiled to DLL, I want to define the ICallable interface as follows:

class ICallable{
public:
virtual void callMe()=0 ;
virtual void callMeAgain()=0;
}

And export the following DLL function so that Delphi can call it:

#define DllExport extern "C" __declspec( dllexport )

DLLExport bool Callback(ICallable* callable){
callable->callMe();
callable->callMeAgain() ;
return true;
}

Finally return to Delphi:

function Callback(myCallable: ICallable): Boolean cdecl; external'dllname'

Question:

>This is only valid when C and Delphi implement their virtual method table in the same way. Is this true?

This can only work of C++ and Delphi implement their virtual method tables in the same way. Is it the case?

I originally thought that the Delphi class does not have a VMT compatible with the C class. I think this is because all Delphi classes are derived from TObject, which declares virtual methods. These virtual methods appear in VMT. I assume these The method first appeared in VMT. However, it was found that the compiler arranged for TObject’s built-in virtual methods to have a negative index in VMT. This means that user-defined virtual methods (those defined in subclasses of TObject) start at index 0. < /p>

This means that the Delphi and C classes in the problem code do have compatible VMT. I believe this design choice is to support COM in earlier versions of Delphi. To support my claim, I suggest you take the documentation And emphasize:

The layout of a VMT is shown in the following table. On the 32-bit platforms, at positive offsets, a VMT consists of a list of 32-bit method pointers (64-bit method pointers on the 64-bit platform)–one per user-defined virtual method in the class type–in order of declaration. Each slot contains the address of the corresponding entry point of the virtual method. This layout is compatible with a C++ v-table and with COM. At negative offsets, a VMT contains a number of fields that are internal to Delphi’s implementation. Applications should use the methods defined in TObject to query this information, since the layout is likely to change in future implementations of the Delphi language.

It should be emphasized that there is nothing in the C standard It is required to use VMT for virtual methods, not to mention the implementation of VMT. In fact, every mainstream Windows compiler has VMT implemented in this way to support COM.

You can use the Delphi interface, Rather than relying on such implementation details. But, as you know, these are COM interfaces, so you must implement IUnknown. You said you want to avoid COM mechanisms, but the only thing you need to add is IUnknown. In my opinion, This is not particularly onerous. I think you think you need to register CLSID, implement class factories, etc. You don’t. You just need to implement IUnknown.

Anyway, if you really start to avoid using IUnknown then you You can’t use the Delphi interface, and I can tell it there are two options:

>Manually implement VMT in Delphi code. VMT is just an array of function pointers. This will cause you to look like COM in C The code for the way it is done. It is entirely possible, but not very pleasant.
>Use the method outlined in the question and rely on the implementation details of TObject using negative VMT indexes for its built-in virtual methods.

The compiled code option 2 is as follows:

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;< br />
begin
Callable := MyCallable.Create;
Writeln(Callback(Callable));
Callable.Free;
Readln;
end .

C

#include 

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;
}

Yield

CallMe 
CallMeAgain
TRUE

Leave a Comment

Your email address will not be published.