The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,262 other subscribers

Delphi XE6 .. 10.1 Berlin truncating non-VCL-control texts to 256 characters when styled

Posted by jpluimers on 2020/09/23

As of Delphi XE6, the VCL also styles non-VCL controls, but this truncates the texts to 256 characters, for instance in non-balloon hints. This is fixed in Delphi 10.2 Berlin, by making the buffer dynamic and switching obtaining those texts from using GetWindowText to sending a WM_GETTEXT message.

A fix for Delphi XE6..10.1 Berlin is at gitlab.com/wiert.me/public/delphi/DelphiVclStylesAndHintText, with many thanks to Stefan Glienke who based the patch on the ones used in Spring4D. I think they are similar to the ones in [Archive.is] VCL Fix Pack 1.4 | Andy’s Blog and Tools.

The Old New Thing explains the difference between GetWindowText and WM_GETTEXT in [WayBack] The secret life of GetWindowText – The Old New Thing. TL;DR:

GetWindowText strikes a compromise.

  • If you are trying to GetWindowText() from a window in your own process, then GetWindowText() will send the WM_GETTEXT message.
  • If you are trying to GetWindowText() from a window in another process, then GetWindowText() will use the string from the “special place” and not send a message.

So for your own process, it does not matter as GetWindowText uses WM_GETTEXT.

–jeroen

Related:

VCL styling truncates some native Windows controls to 256 characters

During testing, I found out that long Windows native controls – like non-balloon tooltip – will get truncated for most Delphi versions when VCL styling is enabled.

The fix is at https://gitlab.com/wiert.me/public/delphi/DelphiVclStylesAndHintText

The Delphi versions affected appear to be at least Delphi XE6 through XE8, which started to incorporate more stuff from https://github.com/RRUZ/vcl-styles-utils which has a different implementation of TSysControl.GetText than the VCL one and limits to 1K characters.

Basically it is https://github.com/RRUZ/vcl-styles-utils/blame/master/Common/Vcl.Styles.Utils.SysStyleHook.pas#L454

function TSysControl.GetText: String;
var
  Buffer: array [0 .. 1023] of Char;
begin
  SetString(Result, Buffer, Winapi.Windows.GetWindowText(Handle, Buffer, Length(Buffer)));
end;

versus the VCL doing a combination of

function TSysControl.GetText: String;
begin
  Result := GetSysWindowText(Handle);
end;

and

function GetSysWindowText(Window: HWND): string;
var
  Text: array[0..256] of Char;
begin
  SetString(Result, Text, Winapi.Windows.GetWindowText(Window, Text, Length(Text)));
end;

The last function should be something like either of the ones below.

// Two ways of doing this: using GetWindowText or by sending a WM_GETTEXT message
// https://blogs.msdn.microsoft.com/oldnewthing/20030821-00/?p=42833 explains the difference and favours WM_GETTEXT, but your mileage might vary
// - GetWindowText: https://msdn.microsoft.com/en-us/library/windows/desktop/ms633520.aspx
// - WM_GETTEXT: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632627.aspx

function GetSysWindowText_GetWindowText(Window: HWND): string;
var
  RequiredTextLength: Integer;
  ObtainedTextLength: Integer;
begin
  RequiredTextLength := SendMessage(Window, WM_GETTEXTLENGTH, 0, 0);
  SetString(Result, PChar(nil), RequiredTextLength);
  if RequiredTextLength <> 0 then
  begin
    ObtainedTextLength := Winapi.Windows.GetWindowText(Window, PChar(Result), RequiredTextLength);
    if ObtainedTextLength < RequiredTextLength then
      SetLength(Result, ObtainedTextLength);
  end;
end;

function GetSysWindowText_WMGetText(Window: HWND): string;
var
  RequiredTextLength: Integer;
  ObtainedTextLength: Integer;
begin
  RequiredTextLength := SendMessage(Window, WM_GETTEXTLENGTH, 0, 0);
  SetString(Result, PChar(nil), RequiredTextLength);
  if RequiredTextLength <> 0 then
  begin
    ObtainedTextLength := SendMessage(Window, WM_GETTEXT, WParam(RequiredTextLength + 1), LParam(PChar(Result)));
    if ObtainedTextLength < RequiredTextLength then
      SetLength(Result, ObtainedTextLength);
  end;
end;

The fundament of the patch code in the project is by Stefan Glienke as it builds on the patch mechanism used in various Spring4D patches.

I have done minor alterations, including a dependency on JEDI.inc from the JEDI repository to patch only for affected Delphi versions.

The patch will not be applied when:

  • you define Skip_Patch_Vcl_Themes_GetSysWindowText at your project level.
  • Delphi < XE6 or Delphi > 10.1 Berlin

In order to reproduce the issue, I have also adopted ComponentBaloonHintU from https://stackoverflow.com/questions/26247528/displaying-x-icon-in-tballoonhint

You can see the behaviour by defining Skip_Patch_Vcl_Themes_GetSysWindowText.

view raw

README.md

hosted with ❤ by GitHub

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.