Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7970197
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 4, 20262026-06-04T07:21:36+00:00 2026-06-04T07:21:36+00:00

This question arised from this one . The problem is: create non visual component

  • 0

This question arised from this one.

The problem is: create non visual component which can hold many callbacks commands from system.
User can define unlimited number of callbacks in the IDE. Callbacks will be defined in TCollection as TCollectionItem.


This is a pattern which work pretty good, but has some disadvantages. (described later)
Therefore I wonder, if it could be done better 😉


This is a main component, user can define in the IDE unlimited number of callback function through CommandsTable collection

TMainComp = class(TComponent)  
private
   CallbacksArray: array [0..x] of pointer;
   procedure BuildCallbacksArray;    
public 
   procedure Start;
published
   property CommandsTable: TCommandCollection read FCommandsTable write SetCommandsTable;
end;



Every collection item looks like this, InternalCommandFunction is callback, which is called from system. (Stdcall Calling Convention)

TCommandCollectionItem = class(TCollectionItem)
public
   function InternalCommandFunction(ASomeNotUsefullPointer:pointer; ASomeInteger: integer): Word; stdcall;
published
   property OnEventCommand: TComandFunc read FOnEventCommand write FOnEventCommand;
end; 


TComandFunc = function(AParam1: integer; AParam2: integer): Word of Object;



And here is a implementation. The whole process could be started with “Start” procedure

procedure TMainComp.Start;
begin  
  // fill CallBackPointers array with pointers to CallbackFunction
  BuildCallbacksArray;

  // function AddThread is from EXTERNAL dll. This function creates a new thread, 
  // and parameter is a pointer to an array of pointers (callback functions).
  // New created thread in system should call our defined callbacks (commands) 
  AddThread(@CallbacksArray);
end;   

And this is the problematic code. I think the only way how to get pointer to “InternalEventFunction” function
is to use MethodToProcedure() function.

procedure TMainComp.BuildCallbacksArray;
begin
   for i := 0 to FCommandsTable.Count - 1 do begin
      // it will not compile
      //CallbacksArray[i] := @FCommandsTable.Items[i].InternalEventFunctionWork;

      // compiles, but not work
      //CallbacksArray[i] := @TCommandCollectionItem.InternalCommandFunction;

      // works pretty good
      CallbacksArray[i] := MethodToProcedure(FCommandsTable.Items[i], @TCommandCollectionItem.InternalCommandFunction);

   end;         
end;


function TEventCollectionItem.InternalEventFunction(ASomeNotUsefullPointer:pointer; ASomeInteger: integer): Word; stdcall;
begin
  // some important preprocessing stuff
  // ...


  if Assigned(FOnEventCommand) then begin
     FOnEventCommand(Param1, Param2);
  end;
end;



As I described before, it works ok, but function MethodToProcedure() uses Thunk technique.
I like to avoid this because, program will not work on systems, where the Data Execution Prevention (DEP) is enabled
and also on 64-bit architectures, will be probably brand new MethodToProcedure() function required.


Do you know some better pattern for that?



just for completion, here is a MethodToProcedure(). (I don’t know who is the original author).

TMethodToProc = packed record
    popEax: Byte;
    pushSelf: record
      opcode: Byte;
      Self: Pointer;
    end;
    pushEax: Byte;
    jump: record
      opcode: Byte;
      modRm: Byte;
      pTarget: ^Pointer;
      target: Pointer;
    end;
  end;    

function MethodToProcedure(self: TObject; methodAddr: Pointer): Pointer;
var
  mtp: ^TMethodToProc absolute Result;
begin
  New(mtp);
  with mtp^ do
  begin
    popEax := $58;
    pushSelf.opcode := $68;
    pushSelf.Self := Self;
    pushEax := $50;
    jump.opcode := $FF;
    jump.modRm := $25;
    jump.pTarget := @jump.target;
    jump.target := methodAddr;
  end;
end;    
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-04T07:21:37+00:00Added an answer on June 4, 2026 at 7:21 am

    If you can change the DLL to accept an array of records instead of an array of pointers, then you can define the record to contain both a callback pointer and an object pointer, and give the callback signature an extra pointer parameter. Then define a simple proxy function that the DLL can call with the object pointer as a parameter, and the proxy can call the real object method through that pointer. No thunking or lower-level assembly needed, and it will work in both 32-bit and 64-bit without special coding. Something like the following:

    type
      TCallback = function(AUserData: Pointer; AParam1, AParam2: Integer): Word; stdcall;
    
      TCallbackRec = packed record
        Callback: TCallback;
        UserData: Pointer; 
      end;
    
      TCommandFunc = function(AParam1, AParam2: integer): Word of object; 
    
      TCommandCollectionItem = class(TCollectionItem) 
      private
        FOnEventCommand: TCommandFunc;
        function InternalCommandFunction(APara1, AParam2: Integer): Word; 
      published 
        property OnEventCommand: TCommandFunc read FOnEventCommand write FOnEventCommand; 
      end;  
    
      TMainComp = class(TComponent)  
      private
        CallbacksArray: array of TCallbackRec;
      public 
        procedure Start;
      published
        property CommandsTable: TCommandCollection read FCommandsTable write SetCommandsTable;
      end;
    

    .

    function CallbackProxy(AUSerData: Pointer; AParam1, AParam2: Integer): Word; stdcall;
    begin
      Result := TEventCollectionItem(AUserData).InternalEventFunction(AParam1, AParam2);
    end;
    
    procedure TMainComp.Start; 
    var
      i: Integer;
    begin 
      SetLength(CallbacksArray, FCommandsTable.Count);
      for i := 0 to FCommandsTable.Count - 1 do begin 
        CallbacksArray[i].Callback := @CallbackProxy; 
        CallbacksArray[i].UserData := FCommandsTable.Items[i]; 
      end;          
      AddThread(@CallbacksArray[0]);
    end;    
    
    function TEventCollectionItem.InternalEventFunction(AParam1, AParam2: Integer): Word;
    begin 
      // ... 
      if Assigned(FOnEventCommand) then begin 
        Result := FOnEventCommand(Param1, Param2); 
      end; 
    end; 
    

    If that is not an option, then using thunks is the only solution given the design you have shown, and you would need separate 32-bit and 64-bit thunks. Don’t worry about DEP, though. Simply use VirtualAlloc() and VirtualProtect() instead of New() so you can mark the allocated memory as containing executable code. That is how the VCL’s own thunks (used by TWinControl and TTimer, for instance) avoid DEP interference.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I've this question from an assignment to create a Store which rent out books,
This question arises from this question . One saying is that if a language
G'day, Edit: While this question covers a situation that can arise in programming a
This question is similar to this one How do I add options to a
This question is related to my problem in understanding rebase, branch and merge ,
This question is related with one of my earlier questions.. Previous Post In there
This question relates to this question which I asked earlier this week. The answer
Here's the question first: Is this possible? I'm taking my inspiration from Joe Wrobel's
This Question arises from a Q&A here I have some doubts that i think
this question relates to performance penalities that may or may not arise from having

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.