Search code examples
delphidllcallback

Calling a procedure in an application from a DLL


I am trying to figure out how to call a procedure in an application from a DLL (not how to call a procedure in the DLL). Both the application and the DLL are developed with Delphi 11.

  1. As an example, in the main application I have a procedure like the following:
    procedure DrawRectangle( X, Y, W, H: Integer );
    var
      MyRect: TRectangle;
    begin
      MyRect := TRectangle.Create( mainForm );
      //and so on...
    end;
    
  2. In the DLL I want to call the application's procedure:
    DrawRectangle( 10, 10, 100, 100 );
    

Any help with a simple example would be very much appreciated. I have searched and read several articles online and still cannot get a grasp of how to accomplish what I am trying to do.


Solution

  • That's just like setting a callback in general. Just combine writing a DLL with defining procedural types:

    library Lib;
    
    type
      // The callback must be defined type as procedure or function,
      // so the compiler knows all parameters and the calling convention.
      // The name is only relevant to the type, not the procedure.
      TProcDrawRectangle= procedure( X, Y, W, H: Integer ); stdcall;
    
    var
      // The pointer which either got something assigned to call or not.
      ProcToCall: TProcDrawRectangle= nil;
    
    // We call this to pass (the pointer to a) procedure which this DLL
    // can later call on its own to execute the procedure elsewhere.
    procedure LinkMyCallback( proc: TProcDrawRectangle ); stdcall;
    begin
      ProcToCall:= proc;
    end;
    
    // However, when/where the callback is executed is up to you.
    procedure OtherThings(); stdcall;
    begin
      //...
      if Assigned( ProcToCall ) then ProcToCall( 10, 20, 800, 600 );
    end;
    
    exports
      LinkMyCallback,
      OtherThings;
    
    begin
    end.
    

    Now do the same in your program, along with importing the DLL's procedures:

    program Main;
    
    type
      // The same definition as in the library, of course. Naturally you would put this
      // once in a unit, which is then used by both: your program and the DLL.
      TProcDrawRectangle= procedure( X, Y, W, H: Integer ); stdcall;
    
      // Statically linking the procedure (or functions) which are exported by the library.
      procedure LinkMyCallback( proc: TProcDrawRectangle ); stdcall; external 'Lib.dll';
      procedure OtherThings(); stdcall; external 'Lib.dll';
    
    // Will be called from the library.
    procedure CallbackHere( X, Y, W, H: Integer ); stdcall;
    begin
      //DrawRectangle( X, Y, W, H: Integer );
    end;
    
    begin
      // Hand over (a pointer to) the procedure which it wants to call
      // at a later time.
      LinkMyCallback( CallbackHere );
    
      // Now is a "later time": executing this library's procedure will in
      // turn call on the DLL side the procedure pointer, which is then
      // executing "CallbackHere()" in this program.
      OtherThings();
    end.
    

    Keep an eye on making all exported procedures/functions using the same calling convention - Delphi works best with stdcall. Avoid using Delphi-only types like String or classes/objects in general - better use simple types and helper functions to indirectly operate with objects. Do's and don'ts when working with DLLs is a different topic.