Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send new size of array from C++ DLL to Delphi before function return

Tags:

c++

delphi

I want to change size of buffer array in Delphi program if C++ DLL function copies longer array. Delphi Code:

function sendUserAllocatedArray(AllocatedArrayPtr: PAnsiChar; Length: Integer): Integer; cdecl;
external 'DLLlibrary.dll';

var
  myCharPtr : PAnsiChar;
  size : integer;  
  UserAllocatedArray: array[0..10] of AnsiChar;
  arrayPtr: PAnsiChar;
begin
    UserAllocatedArray :=  'test123';
    arrayPtr := UserAllocatedArray;
    size := sendUserAllocatedArray(arrayPtr, Length(UserAllocatedArray));   
end

C++ DLL:

extern "C" __declspec(dllexport) int sendUserAllocatedArray(char* data, int length);

int sendUserAllocatedArray(char* data, int length)
{
    char char_array[] = "this array length is more than 10";
    datLength = sizeof(char_array);
    if(datLength < length)
        strcpy_s(data, length, char_array);
    else
        ....;   

    return length;
}

So i need to allocate more space if bigger buffer needed before DLL function copy the data to array. Do i need handler. How can it be done.

like image 529
user5005768Himadree Avatar asked Jan 30 '26 09:01

user5005768Himadree


2 Answers

Given the code you have shown, the only way to do what you want (without changing the DLL function's signature) is if the DLL function returns the required buffer length if the input buffer is too small, eg:

extern "C" __declspec(dllexport) int sendUserAllocatedArray(char* data, int length);

int sendUserAllocatedArray(char* data, int length)
{
    char char_array[] = "this array length is more than 10";
    int datLength = sizeof(char_array);
    if ((data) && (datLength <= length))
        memcpy(data, char_array, length);
    return datLength;
}

Then the Delphi code can do this:

function sendUserAllocatedArray(AllocatedArrayPtr: PAnsiChar; Length: Integer): Integer; cdecl; external 'DLLlibrary.dll';

var
  myCharPtr : array of AnsiChar;
  size : integer;  
  UserAllocatedArray: array[0..10] of AnsiChar;
  arrayPtr: PAnsiChar;
begin
  UserAllocatedArray := 'test123';
  size := sendUserAllocatedArray(UserAllocatedArray, Length(UserAllocatedArray));
  if size <= Length(UserAllocatedArray) then
  begin
    arrayPtr := UserAllocatedArray;
  end else
  begin
    SetLength(myCharPtr, size);
    arrayPtr := PAnsiChar(myCharPtr);
    StrLCopy(arrayPtr, UserAllocatedArray, size-1);
    size := sendUserAllocatedArray(arrayPtr, size);
  end;
  // use arrayPtr up to size chars as needed...
end;

Which can be simplified to this:

function sendUserAllocatedArray(AllocatedArrayPtr: PAnsiChar; Length: Integer): Integer; cdecl; external 'DLLlibrary.dll';

var
  size : integer;  
  UserAllocatedArray: array of AnsiChar;
begin
  SetLength(UserAllocatedArray, 10);
  StrLCopy(PAnsiChar(UserAllocatedArray), 'test123', Length(UserAllocatedArray)-1);
  repeat
    size := sendUserAllocatedArray(PAnsiChar(UserAllocatedArray), Length(UserAllocatedArray));
    if size <= Length(UserAllocatedArray) then Break;
    SetLength(UserAllocatedArray, size);
  until False;
  // use UserAllocatedArray up to size chars as needed...
end;

Or this:

function sendUserAllocatedArray(AllocatedArrayPtr: PAnsiChar; Length: Integer): Integer; cdecl; external 'DLLlibrary.dll';

var
  size : integer;  
  UserAllocatedArray: array of AnsiChar;
begin
  size := sendUserAllocatedArray(nil, 0);
  SetLength(UserAllocatedArray, size);
  StrLCopy(PAnsiChar(UserAllocatedArray), 'test123', size-1);
  size := sendUserAllocatedArray(PAnsiChar(UserAllocatedArray), size);
  // use UserAllocatedArray up to size chars as needed...
end;
like image 180
Remy Lebeau Avatar answered Jan 31 '26 23:01

Remy Lebeau


It is widely used solution in APIs (which fills passed memory array up with contents) to interpret the memory array input parameter NIL value as a mode switch to calculate the necessary memory needs.

This fuction should:

  1. Pass the memory needs back as a result.
  2. Check whether the memory pointer NIL or not.
  3. Check the passed memory array size and throw an exception in the right cases. (Comment : C has NOT exception mechanism! In case of C you must use special return values to inform the caller about the invalid input parameter value / processing errors)
  4. Of course, if you call C/C++ DLL function, the call convension must be set. Most of the cases cdecl/stdcall.

The example code for C DLL function:

unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TDLLFunction = function ( buffer_ : pointer; memSize_ : integer ) : integer; cdecl;

  TForm3 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    fDLLFunction : TDLLFunction;

  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

function testDLLFunction( mem_ : pchar; memSize_ : integer ) : integer; cdecl;
const
  CONST_text_Viered : array [0..3] of char = '1234';
begin
  result := ( length( CONST_text_Viered ) + 1 )*sizeOf( char );
  if ( mem_ <> NIL ) then
    if ( memSize_ >= result ) then
      strPCopy( mem_, CONST_text_Viered )
    else
      result := -1;
end;


procedure TForm3.Button1Click(Sender: TObject);
var
  memSize : integer;
  memArray : pchar;
begin
  memSize := fDLLFunction( NIL, 0 );
  getMem( memArray, memSize );
  try
    if ( fDLLFunction( memArray, memSize ) >= 0 ) then
    begin
      // Do some usefull think with memArray
    end else
      showMessage( 'Unsufficient memory error!' );
  finally
    freeMem( memArray );
  end;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
  fDLLFunction := @testDLLFunction;
end;

end.

Example code for C++ DLL function:

unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TDLLFunction = function ( buffer_ : pointer; memSize_ : cardinal ) : cardinal; stdcall;

  TForm3 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    fDLLFunction : TDLLFunction;

  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

type
  EUnsufficientMemoryError = class ( Exception )
    public 
      constructor Create( currentSize_, minimumSize_ : cardinal );
  end;

constructor EUnsufficientMemoryError.Create( currentSize_, minimumSize_ : cardinal );
begin
  inherited Create( 'Unsufficient memory! Current size: ' + intToStr( currentSize_ ) + ' Minimum size: ' + intToStr( minimumSize_ ) );
end;

function testDLLFunction( mem_ : pchar; memSize_ : cardinal ) : cardinal; stdcall;
const
  CONST_text_Viered : array [0..3] of char = '1234';
begin
  result := ( length( CONST_text_Viered ) + 1 )*sizeOf( char );
  if ( mem_ <> NIL ) then
    if ( memSize_ >= result ) then
      strPCopy( mem_, CONST_text_Viered )
    else
      raise EUnsufficientMemoryError.Create( memSize_, result );
end;


procedure TForm3.Button1Click(Sender: TObject);
var
  memSize: cardinal;
  memArray : pchar;

begin
  memSize := fDLLFunction( NIL, 0 );
  getMem( memArray, memSize );
  try
    try
      fDLLFunction( memArray, memSize );
      // Do some useful think with memArray
    except
      on e : EUnsufficientMemoryError do
        //...
        ;
    end;
  finally
    freeMem( memArray );
  end;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
  fDLLFunction := @testDLLFunction;
end;

end.
like image 44
The Bitman Avatar answered Jan 31 '26 23:01

The Bitman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!