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.
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;
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With