Skip to content

Instantly share code, notes, and snippets.

@jpluimers
Last active June 13, 2018 13:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpluimers/9ca45efbce2f5102f7b82310ca4ddcb9 to your computer and use it in GitHub Desktop.
Save jpluimers/9ca45efbce2f5102f7b82310ca4ddcb9 to your computer and use it in GitHub Desktop.
VCL styling truncates some native Windows controls to 256 characters

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment