2

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?

Nasreddine Galfout
  • 2,550
  • 2
  • 18
  • 36
  • If there's no way of passing a reference to the original instance, there's no way you can determine which one it refers to, unless there is only ever one instance. In this case you need to have a "global" reference, i.e. either a global variable or a class variable for the class in question – Dave Nottage Nov 23 '20 at 22:30
  • @DaveNottage unfortunately there is more than one instance that is using this dll. so I need some way to know which one the callback is for. – Nasreddine Galfout Nov 23 '20 at 22:32
  • @RemyLebeau No the new version does not provide a replacement, it was optimized for c# which can do that with delegates. for 1) do you think it will be possible to create each set dynamically?. for 2) can you explain what a thunk means (I already look it up and I still do not get the meaning). thank you for the comment. – Nasreddine Galfout Nov 23 '20 at 23:20
  • 1
    if each instance is in a seperate thread you might get away with using a ThreadVar – Remko Nov 24 '20 at 00:00
  • @RemyLebeau thank you very much. I think the first approach is above my head now (I will try it when I'm really stuck). I will try the `threadvar` idea it seems simpler at this point because the dll is thread safe. – Nasreddine Galfout Nov 24 '20 at 00:22
  • 1
    @RemyLebeau Can you write all your comments as an answer so I can accept it? you basically gave most of the possible solutions. – Nasreddine Galfout Nov 24 '20 at 00:25
  • @NasreddineGalfout done – Remy Lebeau Nov 24 '20 at 00:43
  • 1
    @RemyLebeau thank you for sharing your valuable experience. – Nasreddine Galfout Nov 24 '20 at 10:27

1 Answers1

3

If the new version of the DLL does not provide any replacement for the old arg parameter, then you basically have only 2 choices:

  1. 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.

  2. 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.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770