0
votes

I have got a DLL function that returns a pointer to ANSI text (PAnsiChar). I want to assign this to a (unicode-) string (This is Delphi XE2.). The following compiles but I get a warning "W1057 Implicit String cast from 'AnsiChar' to 'string'":

function TProj4.pj_strerrno(_ErrorCode: Integer): string;
var
  Err: PAnsiChar;
begin
  Err := Fpj_strerrno(_ErrorCode);
  Result := Err;
end;

EDIT: The text in question is an error message in English, so there are unlikely to be any conversion problems here.

I am now tempted to just explicitly typecast Err to string like this ...

Result := String(Err);

.. to get rid of the warning. Could this go wrong? Should I rather use a temporary AnsiString variable instead?

var
  s: AnsiString;
[...]
s := Err;
Result := String(s);

If yes, why?

Or should I make it explicit, that the code first converts a PAnsiChar to AnsiString and then the AnsiString to a String?

Result := String(AnsiString(Err));

And of course I could make it a function:

function PAnsicharToString(_a: PAnsiChar): string;
begin
   // one of the above conversion codes goes here
end;

All these options compile, but will they work? And what's the best practice here?

Bonus points: The code should ideally compile and work with Delphi 2007 and newer versions as well.

2
You need to know the code page used to encode the 8 bit text. Unless you know that you can't hope to make progress.David Heffernan
The text in this case is an error message in English, so it's unlikely to cause any issues.dummzeuch
So, how is the text encoded? Which code page? Do you know? If you are happy with "unlikely" then why do you want to know best practice?David Heffernan
I guess that @dummzeuch is trying to say that the text is plain ASCII, so unlikely to contain any inconvertible items, regardless of codepage. But we all know that Turkish EBCDIC (CP 1026) is not compatible with ASCII-7. <g>Rudy Velthuis

2 Answers

5
votes

If the text is encoded in the users current locale then I'd say it is simplest to write:

var
  p: PAnsiChar;
  str: string;
....
str := string(p);

Otherwise if you wish to convert from a specific code page to a Unicode string then you would use UnicodeFromLocaleChars.

3
votes

I think the general solution is assigning c char pointer to RawByteString, then set its codepage corresponding to c null-terminated string encoding.

var
  bys :TBytes;
  rbstr :RawByteString;
  ustr :string;
  pastr :PAnsiChar;
begin

  SetLength(bys,5);
  bys[0] := $ca;
  bys[1] := $e9;
  bys[2] := $d2;
  bys[3] := $b5;
  bys[4] := 0;

  pastr := @bys[0]; // just simulate char* returned by c api


  rbstr := pastr; // assign PAnsiChar to RawByteString
  // assume text encoded as codepage 936
  // Note here: set 3rd param to false!
  SetCodePage(rbstr,936,false);

  ustr := string(rbstr);
  ShowMessage(ustr);
end;

And the other cross-platform solution is (vcl,fmx,fmx with mobile platform)

function CString2TBytes(ptr :{$IFDEF NEXTGEN} MarshaledAString {$ELSE} PAnsiChar {$ENDIF}) :TBytes;
var
  pby :PByte;
  len :Integer;
begin
  pby := PByte(ptr);
  while pby^<>0 do Inc(pby);
  len := pby - ptr;
  SetLength(Result,len);
  if len>0 then Move(ptr^,Result[0],len);
end;

procedure TForm5.Button1Click(Sender: TObject);
var
  bys, cbys: TBytes;
  ustr: string;
  // PAnsiChar is undefined in mobile platform
  // remap param foo(outSting:PAnsiString) => foo(outString:MarshaledAString)
  ptr: {$IFDEF NEXTGEN} MarshaledAString {$ELSE} PAnsiChar {$ENDIF}; //
  encoding : TEncoding;
begin

  SetLength(bys, 5);
  bys[0] := $CA;
  bys[1] := $E9;
  bys[2] := $D2;
  bys[3] := $B5;
  bys[4] := 0;

  ptr := @bys[0]; // just simulate char* returned by c api

  cbys := CString2TBytes(ptr);

  // assume text encoded as codepage 936
  encoding := TEncoding.GetEncoding(936);
  try
    ustr := encoding.GetString(cbys);
    ShowMessage(ustr);
  finally
    encoding.Free;
  end;

end;