4

I'm having trouble calling a function in a c++ dll from delphi.

The c++ function is defined as following

BALL_SCRUB_DLL_API int CALLING_CONVENTION bsl2_ModelBallFlight(float cam_X, 
    float cam_Y, 
    float cam_Z, 
    Ball3d* ball_data_in, 
    int n_balls_in, 
    Ball3d* ball_data_out, 
    int &n_balls_out);

The ball struct looks as follows:

  typedef struct 
  { float X;
  float Y;
  float Z;
  float VX;
  float VY;
  float VZ;
  int frame_id;
  int flag;
  } Ball3d;

I want to send an array of ball_data_in from my delphi app and the c++ dll will return the same array type but with modified values in ball_data_out.

I have defined a TBall3D record as follows:

TBall3D = record
    X : Single;
    Y : Single;
    Z : Single;
    VX : Single;
    VY : Single;
    VZ : Single;
    Framecount : Integer;
    BallFlag : Integer;
  end;
  PBall3D = ^TBall3D;
  TBall3DArray = array of TBall3D;
  PBall3DArray = ^TBall3DArray;

My function declration looks as follows:

  TBSL2_ModelBallFlight = function( const Cam_X, Cam_Y, Cam_Z : Single;
                                    const ball_data_in : PBall3DArray;
                                    const NFramesIn : Integer;
                                    var ball_data_out : PBall3DArray;
                                    const NFramesOut : Integer) : Integer; cdecl;

How do i make that call from delphi to the dll? Any help would be highly appreciated.

1 Answer 1

2

The problem is here:

TBall3DArray = array of TBall3D;

That is a dynamic array. Which is a Delphi data type which is not appropriate for interop. You can use dynamic arrays to hold the data, but not as parameters across the interop boundary. In any case, a dynamic array variable is a pointer to the first element. But you are passing the address of that pointer which is one level of indirection too many.

Declare the import like this:

TBSL2_ModelBallFlight = function(
  Cam_X: Single;
  Cam_Y: Single;
  Cam_Z: Single;
  ball_data_in: PBall3D;
  NFramesIn: Integer;
  ball_data_out: PBall3D;
  var NFramesOut: Integer
): Integer; cdecl;

This is a direct translation of the C++ code. Where the C++ code uses Ball3d*, a pointer to Ball3d, you use PBall3D, a pointer to TBall3D.

Note also the nuance of the final parameter. In the C++ code it is int &n_balls_out. That is a reference to int. So make it a var parameter in Delphi.

In order to call the code you can use your dynamic array type. Declare variables for the in and out data:

var
  ball_data_in, ball_data_out: TBall3DArray;

You will need to initialise the arrays with calls to SetLength. And then when you pass the parameters do so either with @ball_data_in[0] or PBall3D(ball_data_in). And likewise for the out parameter.

I don't know the precise protocol for the function, since a prototype is seldom enough, but the call might look like this:

SetLength(ball_data_in, NFramesIn);
SetLength(ball_data_out, NFramesOut);
retval := bsl2_ModelBallFlight(..., PBall3D(ball_data_in), NFramesIn, 
  PBall3D(ball_data_out), NFramesOut);
Sign up to request clarification or add additional context in comments.

2 Comments

+1. Nicely written. I much prefer the readability and clarity of PBall3D(ball_data_in) rather than the @ball_data_in[0] syntax.
Thank you @Ken. Not original. I learnt it here on the stack from Remy or Arnaud, can't remember which. Possibly from both of them! Learning things like that is why I'm here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.