在Delphi 2009中将字符串转换为PAnsiChar

28

我正在将我的应用程序转换为Delphi 2009,并遇到了一些需要将字符串(宽字符)转换为 AnsiString 的调用的问题。

这里是一个示例来演示我遇到的问题:

var
  s: PAnsiChar;

...

s := PAnsiChar(Application.ExeName);

在Delphi 2007和之前的版本中,s := PChar(Application.ExeName)会返回应用程序exe路径。

在Delphi 2009中,s := PAnsiChar(Application.ExeName)只返回'E'。

我猜这是因为我将Unicode字符串转换为ansi字符串,但我该如何转换才能使PAnsiChar获取完整的字符串?

6个回答

42

我这里没有Delphi 2009,所以无法检查。但是也许你需要尝试:

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

正如gabr所指出的那样,这不是一个很好的做法,只有在您百分之百确定字符串仅包含具有直接映射到ANSI范围的字符时才会使用它。

这就是为什么您应该得到警告,因为您正在将Unicode转换为ANSI。


你不应该这样做,因为这是一个显式转换。同时,是的,它应该能够工作。 - gabr
1
我知道,但将其转换为PAnsiChar也有些可疑。 - Toon Krijthe
1
它确实可以工作,但需要显式转换的代价。还有其他替代方案吗?将其转换为PAnsiChar在我的回复中有解释。 - smartins
问题在于您总是有信息丢失的可能性。将PAnsiChar更改为字符串可能会更好。 - Toon Krijthe
在XE5中,这个问题导致我调用dll失败,包括shellexecute。简单地将PAnsiChar(myStringVar)强制转换会导致无法检测和无法重现的错误,我感到非常沮丧,直到同样的问题发生在外部dll上,我才找到了这个解决方案。现在运行良好。任何遇到shell-execute问题的人也应该查看一下这个。 - user30478

5

使用 RawByteString 而不是 String 类型:

s: RawByteString;

s := LoadSomeRegularString(usually a string type);

PAnsiChar(s) <<< all fine.

1
不要这样做。这是对RawByteString的完全滥用。相反,阅读RawByteString的文档,并弄清它真正的用途。 - David Heffernan

1

Gamecat 显式转换有效。我在下面更详细地解释问题,以便也许有人可以指出更好的解决方案。

我正在使用以下函数来检索应用程序编译日期:

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需要一个PAnsiChar作为ImageName参数,因此我需要将Unicode字符串转换。除了显式转换为AnsiString之外,还有其他替代方法吗?


不,我认为没有 Unicode 版本。CodeGear Imagehlp 单元将 MapAndLoad 声明为 LPSTR,它映射到 PAnsiChar。而且在 MSDN 上也没有提到 Unicode 版本。 - smartins
2
你可能应该添加一条注释,说明你为了Unicode兼容性做了哪些更改,并完全删除IFDEF - PAnsiChar和AnsiString至少在Delphi 4中已经可用,而类型转换在Ansi程序中也不会有影响。在我看来,代码越简单越好。 - mghie
这个IFDEF非常无意义。只需编写PAnsiChar(AnsiString(FileName)),它可以在任何地方工作。但更好的解决方案是找到一个替代MapAndLoad的方法,不需要ANSI输入。 - David Heffernan
更好的解决方案,因为您正在执行模块上进行此操作,如下所示:Result := PImageNtHeaders(HInstance + PImageDosHeader(HInstance)^._lfanew)^.FileHeader.TimeDateStamp / SecsPerDay + UnixDateDelta; - David Heffernan

0

WideCharToMultiByte 在 Delphi 的某些转换中被内部使用。 - Daniel Rikowski

0

我有完全相同的问题。PAnsiChar 只指向第一个字符。我编写了以下函数来处理旧功能。

// This function converts a string to a PAnsiChar
// If the output is not the same, an exception is raised
// Author: nogabel@hotmail.com

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;

1
你只需要使用PAnsiChar(AnsiString(stringVar)) - David Heffernan
5
AnsString是一个局部变量,函数退出后将不再存在,那么Result指向该变量是否会出现问题?换句话说,会不会导致访问冲突的问题? - dummzeuch

-2

我认为你有点偏离了。

每个Win32 API函数都有一个Unicode版本,如果它期望一个字符串。

尝试使用MapAndLoadW而不是MapAndLoad...


2
没有MapAndLoadW。这是我看的第一件事。并非所有的Win32 API都有Unicode对应项,大多数有,但像这个就没有。 - smartins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接