DLL和EXE之间的Delphi XE5字符串大小交换限制

时间:2015-01-20 17:52:51

标签: delphi delphi-7 delphi-xe5

我有一个返回大字符串的Delphi 7 DLL函数,它工作正常,但在Delphi XE5中,我在特定大小后得到访问冲突。

我已经编写了一个示例演示,它反映了我的实际代码,在Delphi XE5中也生成了一个AV,它也返回一个大字符串,但是在特定大小之后,我又遇到了访问冲突?

13000行20个字符,它工作正常,但有14000行崩溃。 我用Delphi 7做了一些测试,它也可以正常工作。

我做错了什么?任何人都可以帮助我吗?

感谢。

这是我的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 ;

这是来自我的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;

1 个答案:

答案 0 :(得分:1)

你在这里拥有它的方式可能只是运气,它可以运作。

在DLL中,执行此操作时:

Buffer := pAnsiChar(AnsiString(l_AnsiStr));

您实际上是将DLL中分配的字符串缓冲区返回给调用EXE,即使您在调用之前已经明确分配了接收缓冲区。该接收缓冲区指针被覆盖。

崩溃最有可能发生,因为EXE中的堆管理器没有准备释放内存块,该内存块是在其他地方(在DLL中)分配的。

您可以尝试将字符串的内容复制到缓冲区,而不是分配给缓冲区,如下所示:

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

测试代码(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.

测试代码(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.