dummzeuch
dummzeuch

Reputation: 11217

What's the correct way to assign PAnsiChar to a (unicode-) string?

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.

Upvotes: 0

Views: 4123

Answers (2)

Sam Shaw
Sam Shaw

Reputation: 317

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;

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 612794

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.

Upvotes: 5

Related Questions