Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.


Welcome, Guest
Guest Settings
Help

Thread: SOLVED: Problem in passing data from dll to host-app


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 6 - Last Post: Apr 19, 2018 3:27 AM Last Post By: Samuel Herzog Threads: [ Previous | Next ]
Samuel Herzog

Posts: 16
Registered: 9/10/00
SOLVED: Problem in passing data from dll to host-app  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 13, 2018 11:19 PM
Hello,

I have a problem with passing data from a dll to the host-application.
It works fine if the size of the data is not to bigger than 2603 chars, but if it goes above 2603 chars I get an Access Violation.

Here is the example code:
library mydll;
 
uses
  System.SysUtils,
  System.Classes;
 
{$R *.res}
 
var
  gMyBuffer:PChar;
 
function InitData(out BufferSize:integer):boolean;   // this method prepares some data and writes to the global var <gMyBuffer>
var
i:integer;
_SomeExampleData:string;
begin
// prepare some data
  for i := 1 to 2603 do _SomeExampleData:=_SomeExampleData+'a'; // this works
//  for i := 1 to *2604* do _SomeExampleData:=_SomeExampleData+'a'; // this does NOT work.
  BufferSize:=length(_SomeExampleData);
  GetMem(gMyBuffer,BufferSize+1);
  StrCopy(gMyBuffer,PChar(_SomeExampleData));
  result:=true;
end;
 
function GetData(out Data:PChar):boolean;
begin
  StrCopy(Data,gMyBuffer);
  result:=true;
end;
 
exports
  InitData,
  GetData;
end.


The code of the host-application:
unit Unit1;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
  public
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
type
TInitData= function (out BufferSize:integer):boolean;
TGetData= function (out Data:PChar):boolean;
 
procedure GetDataFromDll(out Data:string);
var
_DllHandle:THandle;
_InitData:TInitData;
_GetData:TGetData;
_BufferSize:integer;
_Data:PChar;
begin
  _DllHandle:=LoadLibrary('mydll.dll');
  if _DllHandle=0 then begin
    Showmessage('Problem to load dll <mydll.dll>.');
    exit;
  end;
  _InitData:=getprocaddress(_DllHandle,'InitData');
  _GetData:=getprocaddress(_DllHandle,'GetData');
  _InitData(_BufferSize);        // this prepares some data in the dll and retunrs the buffer-size
  GetMem(_Data,_BufferSize+1);   // let's allocate the memoryspace
  _GetData(_Data);               // and get the data from the dll.
  Data:=string(_Data);           // Access-Violation here 
  FreeMem(_Data);
  FreeLibrary(_DllHandle);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
_data:string;
begin
  GetDataFromDll(_Data);
  Showmessagefmt('Data is <%s>, Size is <%d>.',[_data,length(_data)]);
end;
 
end.


I am using Delphi 10.2.3 Tokyo.
Has someone an idea what is wrong here?

Thank you.

Edited by: Samuel Herzog on Apr 15, 2018 2:12 AM
Peter Below

Posts: 1,227
Registered: 12/16/99
Re: Problem in passing data from dll to host-app  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 14, 2018 12:38 AM   in response to: Samuel Herzog in response to: Samuel Herzog
Samuel Herzog wrote:

Hello,

I have a problem with passing data from a dll to the host-application.
It works fine if the size of the data is not to bigger than 2603
chars, but if it goes above 2603 chars I get an Access Violation.

That is just arbitrary, your problem is that you assume sizeof(char) =
1 in your code, which is not true anymore in the Delphi versions
released in the last 10 years or so.

Here is the example code:
library mydll;
 
uses
  System.SysUtils,
  System.Classes;
 
{$R *.res}
 
var
  gMyBuffer:PChar;
 
function InitData(out BufferSize:integer):boolean;   // this method
prepares some data and writes to the global var <gMyBuffer> var
i:integer;
_SomeExampleData:string;
begin
// prepare some data
  for i := 1 to 2603 do _SomeExampleData:=_SomeExampleData+'a'; //
this works //  for i := 1 to 2604 do
_SomeExampleData:=_SomeExampleData+'a'; // this does NOT work.
BufferSize:=length(_SomeExampleData);</div>
 
That should be 
 
 BufferSize:=length(_SomeExampleData) * Sizeof(char);
 
<div class="jive-quote">GetMem(gMyBuffer,BufferSize+1);
StrCopy(gMyBuffer,PChar(_SomeExampleData));   result:=true;
end;
 
function GetData(out Data:PChar):boolean;
begin
  StrCopy(Data,gMyBuffer);
  result:=true;
end;
 
exports
  InitData,
  GetData;
end.


The code of the host-application:
unit Unit1;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,   Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
  public
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
type
TInitData= function (out BufferSize:integer):boolean;
TGetData= function (out Data:PChar):boolean;
 
procedure GetDataFromDll(out Data:string);
var
_DllHandle:THandle;
_InitData:TInitData;
_GetData:TGetData;
_BufferSize:integer;
_Data:PChar;
begin
  _DllHandle:=LoadLibrary('mydll.dll');
  if _DllHandle=0 then begin
    Showmessage('Problem to load dll <mydll.dll>.');
    exit;
  end;
  _InitData:=getprocaddress(_DllHandle,'InitData');
  _GetData:=getprocaddress(_DllHandle,'GetData');
  _InitData(_BufferSize);        // this prepares some data in the
dll and retunrs the buffer-size   GetMem(_Data,_BufferSize+1);   //
let's allocate the memoryspace   _GetData(_Data);               //
and get the data from the dll.    Data:=string(_Data);           //
Access-Violation here   FreeMem(_Data);
  FreeLibrary(_DllHandle);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
_data:string;
begin
  GetDataFromDll(_Data);
  Showmessagefmt('Data is <%s>, Size is <%d>.',[_data,length(_data)]);
end;
 
end.


I am using Delphi 10.2.3 Tokyo.
Has someone an idea what is wrong here?

Thank you.


--
Peter Below
TeamB

Samuel Herzog

Posts: 16
Registered: 9/10/00
Re: Problem in passing data from dll to host-app  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 14, 2018 7:54 AM   in response to: Peter Below in response to: Peter Below
Hello Peter,

thank you for your answer. So you suggest to use PByte and SizeOf to overcome the problem?
I tried that as well but ran also into AV's.
But I will spend some more time on the PByte/SizeOf variant.

Sam

Edited by: Samuel Herzog on Apr 15, 2018 9:53 PM
Peter Below

Posts: 1,227
Registered: 12/16/99
Re: Problem in passing data from dll to host-app [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 15, 2018 11:37 PM   in response to: Samuel Herzog in response to: Samuel Herzog
Samuel Herzog wrote:

Hello Peter,

thank you for your answer. So you suggest to use PByte and SizeOf to
overcome the problem? I tried that as well but ran also into AV's.
But I will spend some more time on the PByte/SizeOf variant.

Sam

Edited by: Samuel Herzog on Apr 15, 2018 9:53 PM

No, you can use PChar if the data you need to transfer is actually
text. You only have to take into account that sizeof(char) may not be 1
when you calculate the size of the buffer.

If the data is not text you should indeed transfer it as an array of
byte, though.

--
Peter Below
TeamB
Samuel Herzog

Posts: 16
Registered: 9/10/00
Re: SOLVED: Problem in passing data from dll to host-app  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 15, 2018 2:18 AM   in response to: Samuel Herzog in response to: Samuel Herzog
After spending some time I came to the following solution.

*************************************************************************************
*** DLL ****
*************************************************************************************
{-----------------------------------------------------------------------------
 Unit Name: mydll
 Author:    sam
 Date:      15-Apr-2018
 Purpose:   example how to pass a byte array from a dll to a host-application.
 History:
-----------------------------------------------------------------------------}
library mydll;
 
uses
  System.SysUtils,
  System.Classes;
 
{$R *.res}
 
type
TByteArray= array of byte;
 
var
  gDllBuffer:TByteArray;
 
{-----------------------------------------------------------------------------
  Procedure: InitData
  Author:    sam
  Date:      15-Apr-2018
  Arguments: out BufferSize:integer
  Result:    boolean
  Description: this method prepares some data and writes them to the global
               variable <gDllBuffer>.
-----------------------------------------------------------------------------}
function InitData(out BufferSize:integer):boolean;stdcall;
var
i:integer;
_SomeExampleData:TByteArray;
_ExampleDataSize:integer;
begin
  result:=false;
  SetLength(gDllBuffer,0);
// prepare some data
  Randomize;
  _ExampleDataSize:=Random(10000);
  SetLength(_SomeExampleData,_ExampleDataSize);
  for i := 0 to _ExampleDataSize-1 do _SomeExampleData[i]:=97+(i mod 26); // fill some data into the example array
// move the prepared data into the dll-internal buffer and return the buffersize to the host application.
  BufferSize:=length(_SomeExampleData);
  SetLength(gDllBuffer,BufferSize);
  Move(PByteArray(_SomeExampleData)^,PByteArray(gDllBuffer)^,BufferSize);
  result:=true;
end;
 
{-----------------------------------------------------------------------------
  Procedure: GetData
  Author:    sam
  Date:      15-Apr-2018
  Arguments: Buffer:PByte
  Result:    boolean
  Description: with this method the host-app will get the data from the dll.
-----------------------------------------------------------------------------}
function GetData(Buffer:PByte):boolean;stdcall;
begin
  result:=false;
  if Buffer=nil then exit;
  Move(PByteArray(gDllBuffer)^,Buffer^,length(gDllBuffer));
  result:=true;
end;
 
exports
  InitData,
  GetData;
end.


*************************************************************************************
*** APPLICATION ****
*************************************************************************************
unit Unit1;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
  public
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
type
TByteArray=array of byte;
 
TInitData= function (out BufferSize:integer):boolean;stdcall;
TGetData= function (Buffer:PByte):boolean;stdcall;
 
var
_InitData:TInitData;
_GetData:TGetData;
 
function GetDataFromDll(out Data:TByteArray):boolean;
var
_DllHandle:THandle;
_BufferSize:integer;
_Data:TByteArray;
begin
  result:=false;
  _DllHandle:=LoadLibrary('mydll.dll');
  if _DllHandle=0 then begin
    Showmessage('Problem to load dll <mydll.dll>.');
    exit;
  end;
  try
    _InitData:=getprocaddress(_DllHandle,'InitData');
    _GetData:=getprocaddress(_DllHandle,'GetData');
    if not _InitData(_BufferSize) then exit;           // this prepares some data in the dll and retunrs the buffer-size
    SetLength(_Data,_BufferSize);     // let's allocate the memoryspace
    if not _GetData(PByte(_Data)) then exit;      // and get the data from the dll.
    Data:=_Data;
    result:=true;
  finally
    FreeLibrary(_DllHandle);
    _DllHandle:=0;
  end;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
_ByteData:TByteArray;
_AnsiString:AnsiString;
_String:String;
begin
  if not GetDataFromDll(_ByteData) then exit;
  _AnsiString:=TEncoding.ANSI.GetString(_ByteData);
  ShowmessageFmt('size: <%d>,data: <%s>',[length(_AnsiString),_AnsiString]);     // do whatever you want to do with the data. (in this example we build ANSI-string)  
  _String    :=TEncoding.UNICODE.GetString(_ByteData);  
  ShowmessageFmt('size: <%d>,data: <%s>',[length(_String),_String]);             // do whatever you want to do with the data. (in this example we build UNICODE-string)
end;
 
end.


Edited by: Samuel Herzog on Apr 15, 2018 11:36 PM fixed typo TByteAttay.

Edited by: Samuel Herzog on Apr 15, 2018 11:38 PM

Edited by: Samuel Herzog on Apr 19, 2018 3:23 AM Replaced PByteArray with PByte as suggested by Rudy.
Rudy Velthuis (...


Posts: 7,731
Registered: 9/22/99
Re: SOLVED: Problem in passing data from dll to host-app [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 18, 2018 3:12 PM   in response to: Samuel Herzog in response to: Samuel Herzog
Samuel Herzog wrote:

After spending some time I came to the following solution.

**********************************************************************
*************** *** DLL ****
**********************************************************************
*************** {code}
{---------------------------------------------------------------------

Unit Name: mydll
Author: sam
Date: 15-Apr-2018
Purpose: example how to pass a byte array from a dll to a
host-application. History:

-------} library mydll;

uses
System.SysUtils,
System.Classes;

{$R *.res}

type
TByteArray= array of byte;
PByteArray = ^TByteArray;

-------} function GetData(Buffer:PByteArray):boolean;stdcall;

Nooooooo! Don't do that, please. **NEVER** export types that are
Delphi-specific.

I wrote a whole article about such things. Please read and heed it. If
you want to write usable DLLs, follow the dos and don'ts:

http://rvelthuis.de/articles/articles-dlls.html

In this case, do:

function GetData(Buffer: PByte; BufSize: Integer): Boolean; stdcall;

And let the user allocate the buffer. Let the user pass a PByte to
the first element of this buffer, which can be a static or a dynamic
array, or a block of memory allocated with GetMem, or whatever he deems
necessary. Also let him pass the size, so the DLL can check if the data
fits.

There are other ways (described in my article), for cases where the
above doesn't work out (i.e. if you don't know beforehand how big the
buffer must be, etc.).
--
Rudy Velthuis http://www.rvelthuis.de

"He can compress the most words into the smallest idea of any man
I know." -- Abraham Lincoln
Samuel Herzog

Posts: 16
Registered: 9/10/00
Re: SOLVED: Problem in passing data from dll to host-app [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Apr 18, 2018 10:46 PM   in response to: Rudy Velthuis (... in response to: Rudy Velthuis (...
Hello Rudy,

thank you for your answer.
Your article helps a lot.
I will change my example according to your suggestion. (and post it here again)

In my case the data gets fetched/perpare from a DB and the size/amount of the data to be passed from Dll to host-app varies.
So I have split it into two methods InitData and GetData. I am aware of the often used technique to make just one Method and
then call it twice. (first time with buffer=nil).

Btw, you might want to add this example (our your own) to your article, because it demonstrates the absolute basics on how to transfer 1..n Bytes from a dll to a host-app.
I have not found such an example by googling arround.

Best regards,

Sam

Edited by: Samuel Herzog on Apr 18, 2018 10:56 PM

Edited by: Samuel Herzog on Apr 18, 2018 11:08 PM
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02