delphicharactervcldelphi-12-athens

Why do some non-ASCII Unicode symbols appended to strings disappear in Delphi 12?


Our company upgraded to Delphi 12 a few months ago.

Today I discovered that my image viewer's context menu, which used to look like in the left image below, now appears with a single menu item blanked out (right image below):

Screenshot of context menu before Delphi 12. Screenshot of the same menu in Delphi 12. A single menu item, "Zoom out" with hotkey MINUS SIGN, is entirely blank.

Similarly, in a different app, I discovered that a pen icon was missing from some list box captions:

List box item ending with a pen icon before Delphi 12. List box item with the pen icon strangely absent in Delphi 12.

The code snippets responsible for these two strings are

FmiZoomOut.Caption := 'Zoom out'#9'−'; // U+2212: MINUS SIGN

and

Item.Caption := FInbox[Item.Index].Name +
  IfThen(not FInbox[Item.Index].Note.IsEmpty, #32#32#32'✏');

respectively.

The problem appears to be caused by some string literals of the form #nn'x' where x is a non-ASCII Unicode character. You can easily reproduce this in a new VCL app with the code

ShowMessage('Snowman:'#32'☃');

in the form's OnCreate handler, for example.

Screenshot of message box with empty text message in Delphi 12.

What is causing this, and – more importantly – is there any workaround?


Solution

  • This certainly appears to be a bug introduced in Delphi 12.

    Consider the following code:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      var S := 'Test'#32'!';
      ShowMessage(S); // breakpoint here
    end;
    

    In memory, this looks correct:

    Screenshot of memory: FF FF FF FF 06 00 00 00 54 00 65 00 73 00 74 00 20 00 21 00 00 00 00 00

    This is a six-character constant string literal containing the expected characters.

    However,

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      var S := 'Test'#32'☃';
      ShowMessage(S);
    end;
    

    produces a clearly incorrect string heap object:

    Screenshot of memory: FF FF FF FF 06 00 00 00 00 00 6E 00 69 00 74 00 37 00 54 00 00 00 00 00

    This starts off correctly with reference count FF FF FF FF and length 6, but then the actual contents of the string are entirely off.

    Clearly, this is a compiler (or possibly RTL) bug.

    Likely, the consumer of this string will see the NUL character at the beginning and think that the string ends there. This explains the empty string shown in the message box.

    Fortunately, the work around is almost trivial: Replace #nn'x' with #nn + 'x'.

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      var S := 'Test'#32 + '☃';
      ShowMessage(S);
    end;
    

    Screenshot of memory: FF FF FF FF 06 00 00 00 54 00 65 00 73 00 74 00 20 00 03 26 00 00 00 00

    Screenshot of message dialog with snowman, in Delphi 12.