简体   繁体   中英

Delphi XE5 string size exchange limitation between DLL and EXE

I have a Delphi 7 DLL function that returns large string and it works fine but in Delphi XE5 I get an access violation after a specific size.

I have written a sample demo, that reflects my actual code, that generates also a AV in Delphi XE5 that returns also a large string but again after a specific size, I get an Access Violation ?

13000 lines of 20 chars, it works fine but with 14000 lines it crashes. I did some tests with Delphi 7 and it works fine also.

What am I doing wrong ? Can anyone help me out ?

Thanks.

Here is the code of my DLL :

    function RetLargeStr(Buffer : pAnsiChar; var BufferSize: Integer) : boolean ; stdcall;
    var l_ansiStr : string;
        loop : integer;
    begin
       Result := False;
       //13000 ok     14000+ fail ???
       for loop := 1 to 15000 do
          begin
             l_AnsiStr := l_AnsiStr + 'String of 20 chars' + Char($0D) + Char($0A) ;
          end;

       if Assigned(Buffer) and (BufferSize >= Length(l_ansiStr) + 1) then
          begin
              //Buffer := pAnsiChar(AnsiString(l_AnsiStr));

              move(l_AnsiStr, Buffer^, length(l_AnsiStr) + 1);
              Result := True;
           end;
       //Return actual size of output string.
       BufferSize := Length(l_AnsiStr) + 1;
    end ;

Here's the call from my EXE :

procedure TForm1.Button7Click(Sender: TObject);
var l_StrOut : pAnsiChar;
    l_Str : ansistring;
    p_Size  : integer;
begin
   p_Size := 600000;
   SetLength(l_Str, p_Size);
   l_strout := pAnsiChar(l_str);

   Memo2.Lines.Clear;
   if RetLargeStr(l_StrOut, p_Size)
      then Memo2.Lines.Add( l_StrOut );
end;

The way you have it here it's probably just luck that it works at all.

In the DLL, when you do this:

Buffer := pAnsiChar(AnsiString(l_AnsiStr));

you are actually returning the string buffer allocated in the DLL to the calling EXE, even though you've explicitly allocated a receive buffer before the call. That receive buffer pointer gets overwritten.

The crash most likely occurs because the heap manager in the EXE is unprepared for freeing a memory block, which was allocated somewhere else (in the DLL).

Instead of assigning to buffer, you might try copying the content of the string to it, like this:

 if Assigned(Buffer) and (BufferSize >= Length(l_ansiStr) + 1) then
      begin
          move(AnsiStr[1], Buffer^, length(AnsiStr) + 1));
          Result := True;
       end;

Test code (DLL):

library Project2;
uses
  SysUtils,
  Classes;

   function RetLargeStr(Buffer : pAnsiChar; var BufferSize: Integer) : boolean ; stdcall;
    var l_ansiStr : string;
        loop : integer;
    begin
       Result := False;
       //13000 ok     14000+ fail ???
       for loop := 1 to 15000 do
          begin
             l_AnsiStr := l_AnsiStr + 'String of 20 chars' + Char($0D) + Char($0A) ;
          end;

       if Assigned(Buffer) and (BufferSize >= Length(l_ansiStr) + 1) then
          begin
              //Buffer := pAnsiChar(AnsiString(l_AnsiStr));

              move(l_AnsiStr[1], Buffer^, length(l_AnsiStr) + 1);
              Result := True;
           end;
       //Return actual size of output string.
       BufferSize := Length(l_AnsiStr) + 1;
    end ;

exports
  RetLargeStr;

begin
end.

Test code (EXE):

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
  TForm3 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

function RetLargeStr(Buffer : pAnsiChar; var BufferSize: Integer) : boolean ; stdcall; external 'project2.dll';

procedure TForm3.Button1Click(Sender: TObject);
var l_StrOut : pAnsiChar;
    l_Str : ansistring;
    p_Size  : integer;
begin
   p_Size := 600000;
   SetLength(l_Str, p_Size);
   l_strout := pAnsiChar(l_str);

   Memo1.Lines.Clear;
   if RetLargeStr(l_StrOut, p_Size)
      then Memo1.Lines.Add( l_StrOut );
end;

end.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM