1

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

  • FYI, this is EXTREMELY OLD Indy code. `TIdStringList` no longer exists in Indy, it was removed in 2007. `TIdStackWindows.WSGetServByPort()` was updated for Unicode in 2010, and later re-written as `TIdStackWindows.AddServByPortToList()`. It is implemented quite a bit differently than what has been shown here. You need to upgrade to an up-to-date version of Indy. – Remy Lebeau Mar 27 '19 at 19:54
  • Thank you @RemyLebeau ,i used Indy10 comes with Delphi 10.2, all errors have gone – Deaa Darawsheh Mar 28 '19 at 07:18

1 Answers1

4

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • thanks for your solution ,but i get "Incompatible types" error at `p := ps^.s_aliases;` but if i use `p := Pointer(ps^.s_aliases);` the error goes,is there any impact for that? – Deaa Darawsheh Mar 27 '19 at 13:35
  • That's fine, it just suppresses strict type checking. Better would be to use `PPChar` in place of `^PChar`. – David Heffernan Mar 27 '19 at 13:48
  • Thank you ,i used `p: PPChar;` and the casting `p := PPChar(ps^.s_aliases);` compiled without errors – Deaa Darawsheh Mar 27 '19 at 14:03
  • Change both record field and local variable to that type and you don't need to cast. Although if that's an Indy type then you are stuck with the cast. – David Heffernan Mar 27 '19 at 14:09
  • But yeah, that is an Indy type, you can't change it, gotta use the cast – David Heffernan Mar 27 '19 at 14:12
  • 1
    FYI, `getservbyport()` returns data using Ansi strings, there is no Unicode version, so you need to use `PPAnsiChar` instead of `PPChar` in Delphi 2009+. – Remy Lebeau Mar 27 '19 at 19:56