Search code examples
delphidelphi-10.2-tokyochar-pointer

Pointer of ^Pchar to array of PChar


when i migrate from Delphi 6 to Delphi 10.2 Tokyo i get error when i try to casting pointer of ^PChar to array of PChar

type
  PServEnt = ^TServEnt;
  TServEnt = packed record
    s_name: PChar;                 // official service name
    s_aliases: ^PChar;             // alias list
    s_port: Smallint;              // protocol to use
    s_proto: PChar;                // port #
  end;

function TIdStackWindows.WSGetServByPort(
  const APortNumber: Integer): TIdStrings;
var
  ps: PServEnt;
  i: integer;
  p: array of PChar;
begin
  Result := TIdStringList.Create;
  p := nil;
  try
    ps := GetServByPort(HToNs(APortNumber), nil);
    if ps <> nil then
    begin
      Result.Add(ps^.s_name);
      i := 0;
      p := Pointer(ps^.s_aliases); // get error Incompatible types: 'Dynamic array' and 'Pointer' 
      while p[i] <> nil do
      begin
        Result.Add(PChar(p[i]));
        inc(i);
      end;
    end;
  except
    Result.Free;
  end;
end;

this code working well at Delphi 2010 ,how to make it correct at Delphi 10.2 Tokyo


Solution

  • The error message is correct, and if the code compiled in earlier versions of Delphi then that was because those earlier versions of the compiler were deficient.

    A dynamic array is more than just a pointer to the first element. It also encapsulates the meta data which stores the length of the array, and the reference count. Your cast is therefore not valid. You got away with this invalid code because you did not attempt to access this meta data, but that's as much by chance as through intention.

    Don't attempt to cast to a dynamic array. Instead use pointer arithmetic. For instance:

    function TIdStackWindows.WSGetServByPort(
      const APortNumber: Integer): TIdStrings;
    var
      ps: PServEnt;
      p: PPChar;
    begin
      Result := TIdStringList.Create;
      try
        ps := GetServByPort(HToNs(APortNumber), nil);
        if ps <> nil then
        begin
          Result.Add(ps^.s_name);
          p := PPChar(ps^.s_aliases); // cast needed due to Indy record type's use of un-nameable type
          while p^ <> nil do
          begin
            Result.Add(p^);
            inc(p);
          end;
        end;
      except
        Result.Free;
        raise;
      end;
    end;
    

    I changed the type declaration of the alias list to PPChar to avoid incompatible type errors when assigning to the local variable of that type.

    Note also that I have corrected your exception handling which was previously swallowing exceptions and returning an invalid object reference.