pointersdelphirecord

Question about Record Pointer as a Function Variable


If I define the following procedure:

procedure get_String(var aStringPtr: PAnsiChar);
  function set_ReturnValue(aInputValue: AnsiString) : PAnsiChar; overload;
  var
    iLen: Integer;
  begin
    iLen   := Length(aInputValue);
    Result := AnsiStrAlloc(iStringLen + 1);
    System.AnsiStrings.StrPLCopy(Result, aInputValue, iLen);
  end;

var
  iData: AnsiString;
begin
  iData := 'Hello World';
  aStringPtr:= set_ReturnValue(iData);
end;

I am able to call the 'get_String()' procedure and get a string back by executing the following code:

var
  iMyStr: PAnsiChar;
begin
  get_String(iMyStr);

I am trying, unsuccessfully, to create a 'get_Record()' procedure that returns a properly filled record pointer:

  TMyDataRec = record
    ProductID: Integer;
    DownloadURL: WideString;
    MaintenanceDate: AnsiString;
  end;

type
  TMyDataRecPtr = ^TMyDataRec;

procedure get_Record(var aRecordPtr: TMyDataRecPtr);
var
  iData: TMyDataRec;
begin
  iData.ProductID       := 1;
  iData.DownloadURL     := 'www.thisisatest.com';
  iData.MaintenanceDate := '02/29/2023';

  aRecordPtr := <...>
end;

How do I populate 'aRecordPtr' so that a call to the 'get_Record()' procedure returns the proper data?

So far, all my attempts result in an access violation.


Solution

  • Typically records and record pointers are declared together, as follows:

    type
      PMyDataRec = ^TMyDataRec;
      TMyDataRec = record
        ProductID: Integer;
        DownloadURL: WideString;
        MaintenanceDate: AnsiString;
      end;
    
    // Note that the `PMyDataRec` pointer can be defined before the 
    // `TMyDataRec` record, if done in the same 'type' block.
    

    You get the access violation because you rely on stack memory, which doesn't exist anymore when the procedure exits. You need to allocate memory for the record with the New() method.

    procedure get_Record(var aRecordPtr: PMyDataRec);
    var
    //  iData: TMyDataRec;
      iData: PMyDataRec;
    begin
      new(iData); // this is missing
      iData.ProductID       := 1;
      iData.DownloadURL     := 'www.thisisatest.com';
      iData.MaintenanceDate := '02/29/2023';
    
      aRecordPtr := iData; //<...>
    end;
    

    Note also, you don't need the iData record pointer, you can operate directly on the aRecordPtr parameter.