Преобразувайте низ в PAnsiChar в Delphi 2009

Преобразувам приложенията си в 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 да получи пълния низ?


person smartins    schedule 12.11.2008    source източник


Отговори (6)


Тук нямам Delphi 2009, така че не мога да го проверя. Но може би трябва да опитате:

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

Както Gabr вече посочи, това не е много добра практика и ще я използвате само ако сте 100% сигурни. Низът съдържа само знаци, които имат директно съответствие с диапазона ANSI.

Ето защо трябва да получите предупреждение, защото конвертирате Unicode в ANSI.

person Toon Krijthe    schedule 12.11.2008
comment
Не трябва, защото това е изрично преобразуване. И да, трябва да работи. - person gabr; 12.11.2008
comment
Знам, но преобразуването в PAnsiChar също е малко съмнително. - person Toon Krijthe; 12.11.2008
comment
Той работи с цената на изричното преобразуване. Има ли друга алтернатива? Преобразуването в PAnsiChar е обяснено в моя отговор по-долу. - person smartins; 12.11.2008
comment
Проблемът е, че винаги има шанс за загуба на информация. Вероятно е по-добре да промените PAnsiChar за низ. - person Toon Krijthe; 12.11.2008
comment
В XE5 този проблем ме накара да имам неуспешни извиквания към dll, включително shellexecute. простото предаване на PAnsiChar(myStringVar) предизвика неоткриваеми и невъзпроизводими грешки и бях разочарован, докато същото не се случи с външна dll и стигнах до това решение. сега работи добре. Всеки, който има проблем с изпълнението на shell, също трябва да провери това. - person user30478; 07.01.2019

Вместо да използвате тип String, използвайте RawByteString:

s: RawByteString;

s := LoadSomeRegularString(usually a string type);

PAnsiChar(s) <<< all fine.
person Meka    schedule 25.02.2010
comment
Не, не прави това. Това е пълна злоупотреба с RawByteString. Вместо да правите това, прочетете документацията за RawByteString и разберете за какво всъщност е предназначен. - person David Heffernan; 12.02.2015

Явното преобразуване на 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 първо?

person smartins    schedule 12.11.2008
comment
Не, не мисля, че има версия на Unicode. Модулът CodeGear Imagehlp декларира MapAndLoad като LPSTR, който се преобразува в PAnsiChar. И не се споменава в msdn за версия на Unicode. - person smartins; 12.11.2008
comment
Вероятно трябва да добавите коментар какво сте променили за съвместимостта с Unicode и да премахнете напълно IFDEF - PAnsiChar и AnsiString вече са налични поне в Delphi 4, а типовете не вредят в програмите на Ansi. Колкото по-прост е кодът, толкова по-добре IMHO. - person mghie; 13.11.2008
comment
Този IFDEF е доста безсмислен. Просто напишете PAnsiChar(AnsiString(FileName)), което работи навсякъде. Със сигурност обаче по-добро решение е да се намери алтернатива на MapAndLoad, която не е толкова куца, че да изисква въвеждане на ANSI. - person David Heffernan; 12.02.2015
comment
По-доброто решение, тъй като правите това на изпълняващия модул, е следното: Result := PImageNtHeaders(HInstance + PImageDosHeader(HInstance)^._lfanew)^.FileHeader.TimeDateStamp / SecsPerDay + UnixDateDelta; - person David Heffernan; 12.02.2015

Имах абсолютно същия проблем. PAnsiChar сочи само към първия знак. Написах следната функция, за да се справя със старата функционалност.

// 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;
person Community    schedule 05.03.2009
comment
PAnsiChar(AnsiString(stringVar)) е всичко, от което се нуждаете. - person David Heffernan; 12.02.2015
comment
Това няма ли да има проблем с това, че AnsString е локална променлива и следователно резултатът сочи към тази променлива, която вече няма да бъде налична след излизането на функцията? С други думи, нарушение на достъпа в чакането? - person dummzeuch; 28.04.2017

WideCharToMultiByte може да ви помогне.

person Jamie    schedule 12.11.2008
comment
WideCharToMultiByte се използва вътрешно в някои отливки на Delphi - person Daniel Rikowski; 31.12.2008

Мисля, че си малко настрана. Всяка API функция на Win32 има аналог на unicode, ако очаква низ. Опитайте MapAndLoadW вместо MapAndLoad...

person Community    schedule 20.04.2009
comment
Няма MapAndLoadW. Това беше първото нещо, което погледнах. Не всички Win32 API имат аналог на unicode, повечето имат, но някои като този не. - person smartins; 10.02.2010