Delphi 10.3 functions CharUpper and CharUpperW differ Delphi 10.4

Viewed 378

Does somebody know the reason why CharUpper and CharUpperW declaration in Delphi 10.3 is different than in Delphi 10.4.

Correct declaration in Delphi 10.3

    var
      chr   :WideChar;
    begin      
      chr := WideChar(CharUpperW(PWideChar('a'))); //chr = 'A'
//    chr := WideChar(CharUpperW(WideChar('a')));  //raise exeption: "access violation...

Correct declaration in Delphi 10.4

    var
      chr   :WideChar;
    begin   
//    chr := WideChar(CharUpperW(PWideChar('a')));  //raise exeption: "access violation...
      chr := WideChar(CharUpperW(WideChar('a')));   //chr = 'A'

EDIT: The Remy Lebeau expalanation is right about PWideChar but there is still differences about Delphi version 10.4 and earlier versions!

Lebeau expalanation code sample compile in version 10.4 and earlier versions, but the output of function is different. All versions before 10.4 get correct output "A"!

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

This sample under 10.4 doesn't work correct the output is random character.

And of course... The declaration of function CharUpperW it the same in boath versions of Delphi.

LPWSTR = PWideChar; 
function CharUpperW(lpsz: LPWSTR): LPWSTR; stdcall;**

EDIT: added disassembled code under 10.4

umCommon.pas.114: chr := 'a';
0064C52C 66BB6100         mov bx,$0061
umCommon.pas.115: char := WideChar(CharUpperW(PWideChar(chr)));
0064C530 8D45FC           lea eax,[ebp-$04]
0064C533 8BD3             mov edx,ebx
0064C535 E8EEE7DBFF       call @UStrFromWChar
0064C53A 8B45FC           mov eax,[ebp-$04]
0064C53D E8C2E7DBFF       call @UStrToPWChar
0064C542 50               push eax
0064C543 E8809DDCFF       call CharUpperW

Disassembled code under 10.3

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

The Win32 CharUpperW() function does not take a single WideChar as input, only a PWideChar. But the interpretation of that pointer depends on whether its high-order word is zero (the low-order word contains a single char) or non-zero (the whole pointer is to a null-terminated string).

The only way that CharUpperW(WideChar('a')) can even compile, in any version, is if Embarcadero has added their own overload that takes a single WideChar as input (I don't have any 10.x versions installed to verify).

IMHO, CharUpperW() is a bit dangerous to use because of this abuse of a pointer. I would not trust it with typecasted literals, use a variable instead so you can ensure you are giving it exactly what it really wants:

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;

That being said, there are other functions in the RTL to handle this task, use those instead.

UPDATE: I’ve created a support ticket for this:

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

Since Delphi 10.4 create hidden string and CharUpperW acts different when it works with single-char or null-terminated string input, you need to rewrite your code to make it consistent for 10.4 and 10.3 (and older) versions:

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

P.S. Due to these difficulties, I would recommend you to use UpperCase / AnsiUpperCase from System.SysUtils.

Ok with your help I have find the answer... Actully the functons CharUpper and CharUpperW are in Winapi.Windows unit and both calls the same API call CharUpperW. But in Delphi version 10.4 of unit Winapi.Windows is new function overload:

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;

Compiler takes this overload function instead old one and result is described in my question.

To avoid this overload function we must correct typecast variable Chr to get right result:

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

EDIT: We can also call CharUpperW API function like:

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

The disassembled code is the same:

umCommon.pas.109: Chr := 'a';
0063A906 66BB6100         mov bx,$0061
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
Related