Reputation: 2591
I could pass the following to the old version of the dll
type
PCallbackList = ^TCallbackList;
TCallbackList = record
arg: Pointer;
CallBack1: procedure(arg: Pointer; p1: Pointer; error: PAnsiChar); cdecl;
CallBack2: procedure(arg: Pointer; error: PAnsiChar); cdecl;
end;
and when the callback is fired I cast arg to the object instance that I assigned to it before I passed the callback list.
now the dll has changed and the new signature is:
type
PCallbackList = ^TCallbackList;
TCallbackList = record
CallBack1: procedure(p1: Pointer; error: PAnsiChar); cdecl;
CallBack2: procedure(error: PAnsiChar); cdecl;
end;
so the arg pointer now is removed and I have no way to pass the reference of the object instance to the dll with the list, so I have no way to know which instance the callback belongs to.
so how to pass an object method as a callback method?
Upvotes: 2
Views: 678
Reputation: 595732
If the new version of the DLL does not provide any replacement for the old arg
parameter, then you basically have only 2 choices:
use a different set of callback functions for each object instance. For instance, if you have 5 objects, then define 5 separate sets of callback functions, one for each object. Store the object pointers in global variables where the callbacks can reach them. Or, to mitigate the use of globals, if the DLL is thread-safe then you can create a separate thread for each object, store each object pointer in a threadvar
variable, and have the callbacks be called in the appropriate threads as needed.
use a thunk for each callback, where the target object method/pointer is stored inside each thunk itself. A thunk is a block of memory containing executable instructions and metadata. You can allocate such a block with the Win32 VirtualAlloc()
function using the PAGE_EXECUTE(_READWRITE)
flags, then put the object pointer and some specialized x86/x64 instructions in it, and then use it as a callback. When the block is called into like a function by the DLL, its instructions will get executed, which can then act on the stored pointer as needed.
Behind the scenes, a C# delegate
acts much like a thunk, just with native compiler support behind it.
For Delphi, if you wanted to go the thunk approach, you would have to implement the thunks manually, or find a 3rd party implementation. This is quite an advanced topic. I believe there are some questions on this subject on StackOverflow. I also wrote a series of articles on this subject for the C++Builder Journal, exploring the inner workings of the VCL's own MakeObjectInstance()
thunk that is used internally by TWinControl
and AllocateHWnd()
. The logic (not the code) could be applied to your situation. On my website, in the "Articles" section, is the first 3 articles of that series 1.
1: Unfortunately, I never finished the final 4th article, due to a system crash that wiped out all of the code I had written for it. And the Journal is no longer in publication.
Upvotes: 3