Delphi 10.3 函数 CharUpper 和 CharUpperW 不同 Delphi 10.4

Delphi 10.3 functions CharUpper and CharUpperW differ Delphi 10.4

有人知道 Delphi 10.3 中的 CharUpperCharUpperW 声明与 Delphi 10.4 中的声明不同的原因吗?

Delphi 10.3

中的正确声明
    var
      chr   :WideChar;
    begin      
      chr := WideChar(CharUpperW(PWideChar('a'))); //chr = 'A'
//    chr := WideChar(CharUpperW(WideChar('a')));  //raise exeption: "access violation...

Delphi 10.4

中的正确声明
    var
      chr   :WideChar;
    begin   
//    chr := WideChar(CharUpperW(PWideChar('a')));  //raise exeption: "access violation...
      chr := WideChar(CharUpperW(WideChar('a')));   //chr = 'A'

编辑:Remy Lebeau 对 PWideChar 的解释是正确的,但 Delphi 10.4 版和更早版本仍然存在差异!

Lebeau expalation代码示例在10.4及更早版本编译,但函数输出不同。 10.4 之前的所有版本都获得正确的输出“A”!

var
  char , chr : WideChar;
begin
  chr := 'a';
  char := WideChar(CharUpperW(PWideChar(chr)));
end;

此示例在 10.4 下无法正常工作,输出是随机字符。

当然... 函数声明 CharUpperW 与 Delphi.

的boath版本相同
LPWSTR = PWideChar; 
function CharUpperW(lpsz: LPWSTR): LPWSTR; stdcall;**

编辑:在 10.4

下添加了反汇编代码
umCommon.pas.114: chr := 'a';
0064C52C 66BB6100         mov bx,[=16=]61
umCommon.pas.115: char := WideChar(CharUpperW(PWideChar(chr)));
0064C530 8D45FC           lea eax,[ebp-]
0064C533 8BD3             mov edx,ebx
0064C535 E8EEE7DBFF       call @UStrFromWChar
0064C53A 8B45FC           mov eax,[ebp-]
0064C53D E8C2E7DBFF       call @UStrToPWChar
0064C542 50               push eax
0064C543 E8809DDCFF       call CharUpperW

10.3下的反汇编代码

umCommon.pas.114: chr := 'a';
0063A905 66BB6100         mov bx,[=17=]61
umCommon.pas.115: char := WideChar(CharUpperW(PWideChar(chr)));
0063A909 0FB7C3           movzx eax,bx
0063A90C 50               push eax
0063A90D E8AAB1DDFF       call CharUpperW

Win32 CharUpperW() 函数不接受单个 WideChar 作为输入,只有 PWideChar。但是该指针的 解释 取决于它的 high-order 字是零(low-order 字包含单个字符)还是 non-zero (整个指针指向 null-terminated 字符串)。

CharUpperW(WideChar('a')) 甚至可以在任何版本中编译的唯一方法是,如果 Embarcadero 添加了他们自己的重载,将单个 WideChar 作为输入(我没有任何 10.x 已安装版本以验证)。

恕我直言,使用 CharUpperW() 有点危险,因为它滥用了指针。我不相信它带有类型转换的文字,而是使用一个变量,这样你就可以确保你给它的正是它真正想要的:

var
  chr : array[0..1] of WideChar;
begin
  chr[0] := 'a';
  chr[1] := #0;
  CharUpperW(chr);
end;
var
  chr : WideChar;
begin
  chr := 'a';
  char := WideChar(CharUpperW(PWideChar(chr)));
end;

也就是说,RTL 中还有其他函数可以处理此任务,请改用这些函数。

更新: 我为此创建了支持票:

RSP-31498: Bad codegen breaks Winapi.Windows.CharUpperW()

自从 Delphi 10.4 创建隐藏字符串并且 CharUpperW 在与 single-char 或 null-terminated 字符串输入一起使用时充当 different,您需要重写您的使其与 10.4 和 10.3(及更早)版本一致的代码:

var
  s: string;
begin
  s := 'a';
  CharUpperW(@s[1]);
end;

P.S。由于这些困难,我建议您使用 System.SysUtils.

中的 UpperCase / AnsiUpperCase

好的,在你的帮助下我找到了答案...... 实际上,函数 CharUpperCharUpperWWinapi.Windows 单元中,并且都调用相同的 API 调用 CharUpperW。 但是在 Delphi 版本 10.4 中单元 Winapi.Windows 是新函数重载:

function CharUpper(lpsz: LPWSTR): LPWSTR; overload; stdcall; external user32kernel name 'CharUpperW';
function CharUpper(tch: WideChar): WideChar; overload; stdcall;
begin
  Result := WideChar(IntPtr(CharUpperW(LPWSTR(IntPtr(tch)))));
end;

编译器采用这个重载函数而不是旧函数,结果在我的问题中有描述。

为了避免这个重载函数,我们必须更正类型转换变量 Chr 以获得正确的结果:

var
  Chr     :Char; //Or WideChar 
begin
  Chr := 'a';   
  Chr := Char(CharUpper(LPWSTR(ord(Chr))));

编辑:我们也可以像这样调用 CharUpperW API 函数:

var
  Chr     :WideChar; //or Char
begin
  Chr := 'a';   
  Chr := Char(CharUpper(LPWSTR(ord(Chr))));

反汇编代码相同:

umCommon.pas.109: Chr := 'a';
0063A906 66BB6100         mov bx,[=13=]61
umCommon.pas.110: Chr := Char(CharUpperW(LPWSTR(ord(Chr))));
0063A90A 0FB7C3           movzx eax,bx
0063A90D 50               push eax
0063A90E E8A9B1DDFF       call CharUpperW
0063A913 8BD8             mov ebx,eax