Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic array causing an invalid pointer exception

I have a procedure that takes a dynamic array TData = TArray<Byte> as a parameter.

procedure DoStuff(const Input: TData);
begin
  // do nothing
end;

And a function that returns a dynamic array.

function SomeData: TData;
begin
  Result := [1, 2];
end;

When I use the procedure in the example below, DoStuff gets the following data (1, 2, 3, 1, 3) but I get an EInvalidPointer exception after DoStuff completes.

procedure TForm1.Button1Click(Sender: TObject);
begin    
  DoStuff([1, 2, 3] + SomeData);
end;

Calling DoStuff([1, 2] + SomeData); does not result in an error, it seems to get touchy when the array is greater than 4 items. If I use a temp variable to hold the array, DoStuff still gets (1, 2, 3, 1, 2) but there is no error.

procedure TForm1.Button2Click(Sender: TObject);
var
  Temp: TData;
begin
  Temp := [1, 2, 3] + SomeData;

  DoStuff(Temp);
end;

It looks like pointer exception is related to how one of the dynamic arrays are freed when they fall out of scope.

Should I not be using dynamic arrays in this way? When working, this solved my current problem very cleanly.

I also have tried using array of Byte; instead of TArray<Byte>; but had the same result.

Full test unit:

unit Main;

interface

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

type
  TData = TArray<Byte>;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

procedure DoStuff(const Input: TData);
function SomeData: TData;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  DoStuff([1, 2, 3] + SomeData);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Temp: TData;
begin
  Temp := [1, 2, 3] + SomeData;

  DoStuff(Temp);
end;

procedure DoStuff(const Input: TData);
begin
  // do nothing
end;

function SomeData: TData;
begin
  Result := [1, 2];
end;

end.
like image 562
John van Beek Avatar asked Oct 24 '17 05:10

John van Beek


Video Answer


1 Answers

Your code has no defects and any errors can only be due to compiler / RTL bugs.

Support for dynamic array literals and the dynamic array + operator was somewhat patchy when first released. These features were added in XE7 and I believe that most of the bugs were ironed out by XE8.

My guess would be that you are using XE7, or perhaps a layer version which still contains a more obscure bug than the many obvious ones from XE7.

Your options would seem to be:

  1. Update to a later Delphi version.
  2. Work around the code by avoiding use of the + operator with dynamic arrays and / or dynamic array literals.

I still use XE7 and workaround the issues with my own generic concatenation function. An example of how to do this can be found here: https://stackoverflow.com/a/15924484/505088

Such workaround help you avoid issues with the + operator, but not if the issue is with handling array literals. If your problem is with array literals then you may end up needing to use TData.Create(1, 2, 3) in place of [1, 2, 3].

If the defect is present in later versions then please submit a bug report to Embarcadero's Quality Portal.

like image 157
David Heffernan Avatar answered Oct 22 '22 09:10

David Heffernan