23
votes

I'm converting my applications to Delphi 2009 and faced an intriguing issue with some calls that need to convert a string (wide) to AnsiString.

Here's an example to demonstrate the issue I'm having:

var
  s: PAnsiChar;

...

s := PAnsiChar(Application.ExeName);

With Delphi 2007 and previous versions, s := PChar(Application.ExeName) would return the application exe path.

with Delphi 2009, s := PAnsiChar(Application.ExeName) returns only 'E'.

My guess is that's because I'm converting a unicode string to an ansi string but how can I convert it so that a PAnsiChar gets the full string?

6

6 Answers

34
votes

I have no Delphi 2009 here, so I can't check it. But maybe you have to try:

s := PAnsiChar(AnsiString(Application.ExeName));

As gabr already pointed, this is not a very good practice, and you will only use it if you are 100% sure. The string only contains characters that have a direct mapping to the ANSI range.

That's why you should get a warning because you are converting Unicode to ANSI.

5
votes

Instead of using type String, use RawByteString:

s: RawByteString;

s := LoadSomeRegularString(usually a string type);

PAnsiChar(s) <<< all fine.
1
votes

Gamecat explicit conversion works. I'm explaining the problem in more detail below so that perhaps someone can point to a better solution.

I'm using the following function to retrieve the application compilation date:

function LinkerTimeStamp(const FileName: string): TDateTime;
var
  LI: TLoadedImage;
begin
  {$IFDEF UNICODE}
  Win32Check(MapAndLoad(PAnsiChar(AnsiString(FileName)), nil, @LI, False, True));
  {$ELSE}
  Win32Check(MapAndLoad(PChar(FileName), nil, @LI, False, True));
  {$ENDIF}
  Result := LI.FileHeader.FileHeader.TimeDateStamp / SecsPerDay + UnixDateDelta;
  UnMapAndLoad(@LI);
end;

MapAndLoad requires a PAnsiChar for the ImageName Parameter so I need to convert the unicode string. Is there any other alternative as to explicitly convert to AnsiString first?

0
votes

I had the exact same problem. The PAnsiChar only points to the first character. I wrote the following function to handle the old functionality.

// This function converts a string to a PAnsiChar
// If the output is not the same, an exception is raised
// Author: [email protected]

function StringToPAnsiChar(stringVar : string) : PAnsiChar;
Var
  AnsString : AnsiString;
  InternalError : Boolean;
begin
  InternalError := false;
  Result := '';
  try
    if stringVar <> '' Then
    begin
       AnsString := AnsiString(StringVar);
       Result := PAnsiChar(PAnsiString(AnsString));
    end;
  Except
    InternalError := true;
  end;
  if InternalError or (String(Result) <> stringVar) then
  begin
    Raise Exception.Create('Conversion from string to PAnsiChar failed!');
  end;
end;
0
votes
-2
votes

I think You are a bit off. Every Win32 API function has a unicode counterpart, if it is expecting a string. Try MapAndLoadW instead of MapAndLoad...