Why using the TTF_CENTERTIP flag makes the ToolTip balloon style back to Windows XP style?
I noticed that using the TTF_CENTERTIP flag when adding a ToolTip setting using TTM_ADDTOOL to a ToolTip window that has the TTS_BALLOON style causes this ToolTip to not appear with the Windows 10 style. In other words the balloon appears with rounded edges, as was the style of balloons in Windows XP.
Without using TTF_CENTERTIP the balloon appears with the correct style as seen above, however...
When using the TTF_CENTERTIP flag the style changes to "old style". The question is simple, why does this happen and how to make the balloons always have the latest style according to the OS on which the application runs?
Below is the code I use, written in Delphi but easy to understand. The peculiarity of this example lies in the fact that I am trying to show a line of text without breaks and setting the maximum width of the ToolTip to 300 pixels
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls;
type
TForm1 = class(TForm)
BUTN1: TButton;
BUTN2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
FHwndTip: HWND;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
Winapi.CommCtrl;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
TI: TToolInfo;
begin
FHwndTip := CreateWindowEx(WS_EX_NOACTIVATE or WS_EX_TOPMOST
,TOOLTIPS_CLASS
,nil
,TTS_ALWAYSTIP or TTS_BALLOON
,0,0,0,0
,Self.Handle
,0
,HInstance
,nil);
SendMessage(FHwndTip,TTM_SETTITLE,TTI_INFO,LPARAM(PChar('Title')));
SendMessage(FHwndTip,TTM_SETMAXTIPWIDTH,0,300);
ZeroMemory(@TI,SizeOf(TToolInfo));
TI.cbSize := SizeOf(TToolInfo);
TI.hwnd := Self.Handle; // The Form (or window) in which the button is
TI.uId := BUTN1.Handle;
TI.lpszText := 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tincidunt dictum dui vel luctus. Duis ornare arcu a varius pharetra. Curabitur tempor sit amet dui id malesuada. Aliquam efficitur, massa consectetur tristique iaculis, dolor diam tincidunt.';
TI.uFlags := TTF_IDISHWND or TTF_SUBCLASS;
SendMessage(FHwndTip,TTM_ADDTOOL,0,LPARAM(@TI));
TI.uId := BUTN2.Handle;
TI.lpszText := 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tincidunt dictum dui vel luctus. Duis ornare arcu a varius pharetra. Curabitur tempor sit amet dui id malesuada. Aliquam efficitur, massa consectetur tristique iaculis, dolor diam tincidunt.';
TI.uFlags := TTF_IDISHWND or TTF_SUBCLASS or TTF_CENTERTIP;
SendMessage(FHwndTip,TTM_ADDTOOL,0,LPARAM(@TI));
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
DestroyWindow(FHwndTip);
end;
end.
As the ToolTips display depends on manifests, below is the manifest that my application currently uses, in case that is relevant.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
publicKeyToken="6595b64144ccf1df"
language="*"
processorArchitecture="*"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"
/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!--The ID below indicates app support for Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!--The ID below indicates app support for Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!--The ID below indicates app support for Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!--The ID below indicates app support for Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!--The ID below indicates app support for Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
Windows API - Win32
-
Castorix31 81,831 Reputation points
2022-03-24T15:31:23.73+00:00 I cannot reproduce this on Windows 10 21H1, C.C. v6, VS 2022 :
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T16:06:40.183+00:00 Hello and thanks for the comment. Could you tell which code you used? Also, I see that you used text with line breaks. Try using text without line breaks... I'll post the code I use to reproduce my problem. It's Delphi, but it's understandable.
-
Castorix31 81,831 Reputation points
2022-03-24T16:25:31.167+00:00 I used about the same code, in C++
I changed the text and same behaviour : -
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T16:38:16.467+00:00 This is really very strange. My Windows is 21H1 also and the functions you are seeing in my code are imported from Windows' own DLLs :/. I can't understand what exactly happens. In my understanding, the fact that you are using C++ in VS and that I am using Pascal in Delphi should be totally irrelevant in this case, as the functions used are those imported from the same system dlls. I'm starting to suspect something related to manifests. I don't know how VS handles this, but Delphi automatically puts the manifest in the executable. If my suspicion is correct, is the manifest that VS uses different than what Delphi uses? I'll put this manifest in the question too...
-
Castorix31 81,831 Reputation points
2022-03-24T16:49:46.48+00:00 In C++, I just force C.C v6 with
#pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
But from your screencopy, you also use C.C v6
-
Castorix31 81,831 Reputation points
2022-03-24T16:56:34.953+00:00 I tested with your manifest, nothing changed...
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T16:58:10.617+00:00 Unfortunately you're right :( And now, after your test shows that the problem is not in my code, I'll have to work even harder to solve this problem, which shouldn't exist :( Thank you very much!
If this issue is ever resolved, I'll come back here to report.
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T17:00:20.527+00:00 Weirder even now :( Thanks!
-
Castorix31 81,831 Reputation points
2022-03-24T17:03:15.21+00:00 It could be in your code, but I don't see any difference with my C++ code
Sometimes, I met problems with sizeof(TOOLINFO), but I also tested with other C++ defines and no change//ti.cbSize = TTTOOLINFOA_V2_SIZE; //ti.cbSize = TTTOOLINFOW_V3_SIZE; ti.cbSize = sizeof(TOOLINFO);
-
RLWA32 40,771 Reputation points
2022-03-24T17:07:38.027+00:00 The issue reproduces for me on Win10 21H1 VS2019 16.11.11 in a Win32 C++ dialog application.
-
RLWA32 40,771 Reputation points
2022-03-24T17:14:37.61+00:00 The issue reproduced in my test. See above reply to Castorix31.
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T17:22:16.397+00:00 Trust me, if you don't see any difference between your code written in C++ and mine written in Pascal, it's because there isn't any difference at all. The functions there are imported from dlls, so they are the same functions you use :) If you used exactly the same functions, with the same parameters and it had different results I really don't know what could be causing the problem on my side, being that the only thing that changed was the language and that shouldn't influence in this case
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T17:25:36.757+00:00 And I thought it couldn't get any weirder HAHa If you, using VS (older), can reproduce the problem, then what's the difference? What does VS 2019 do differently?
-
Castorix31 81,831 Reputation points
2022-03-24T17:34:31.327+00:00 Not exacty the same parameters; for example, WS_POPUP must be set in C++, like in MSDN samples, and I noticed you did not set it (probably Delphi adds it elsewhere ? (without WS_POPUP, the Tooltip is not displayed))
-
RLWA32 40,771 Reputation points
2022-03-24T17:46:14.28+00:00 Interesting. For me, the tooltips were displayed with/without WS_POPUP. And the docs say "A tooltip control always has the WS_POPUP and WS_EX_TOOLWINDOW window styles, regardless of whether you specify them when creating the control."
-
RLWA32 40,771 Reputation points
2022-03-24T17:51:36.347+00:00 The issue also reproduced for me using VS2022 17.1.2 on Win10 21H1.
-
Castorix31 81,831 Reputation points
2022-03-24T17:57:22.083+00:00 After more tests, it works without WS_POPUP if I don't use WS_VISIBLE, which is useless too (I copied one of my old codes...))
But I still cannot get the rounded corners... -
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T19:20:40.81+00:00 Gentlemen (@Castorix31 and @RLWA32 ) I created this comment because I was already starting to get confused by the nested comments...
What we have so far is that @Castorix31 and @RLWA32 obtained different results using VS 2022, correct? Maybe it's time for you to put your code here so that there is no doubt that we are all using the exact same code, you in your VS and I in Delphi.
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T19:35:23.807+00:00 Here is my simplified code (more close to C++ ones): MSQA#786101V1.0
-
Castorix31 81,831 Reputation points
2022-03-24T19:42:51.557+00:00 I updated my code to make it like yours : Tooltip C++/Win32 code which works perfectly... at least on my PC (Windows 10 Education 21H1 - 19043.1526, VS 2022 17.1.1)
-
RLWA32 40,771 Reputation points
2022-03-24T20:01:34.953+00:00 This is what I used
m_hTip = CreateWindowEx(WS_EX_TOPMOST | WS_EX_NOACTIVATE, TOOLTIPS_CLASS, NULL, WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON, 0, 0, 0, 0, hwnd, NULL, m_hInst, NULL); SendMessage(m_hTip, TTM_SETTITLE, TTI_INFO, (LPARAM)_T("Title")); SendMessage(m_hTip, TTM_SETMAXTIPWIDTH, 0, 300); TOOLINFO tinfo{ sizeof tinfo }, tinfo2{ sizeof tinfo2 }; tinfo.hinst = m_hInst; tinfo.hwnd = hwnd; tinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; tinfo.uId = (UINT_PTR)GetDlgItem(hwnd, IDC_BUTTON1); tinfo.lpszText = (LPTSTR) _T("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tincidunt \ dictum dui vel luctus. Duis ornare arcu a varius pharetra. Curabitur tempor sit \ amet dui id malesuada. Aliquam efficitur, massa consectetur tristique iaculis, \ dolor diam tincidunt."); SendMessage(m_hTip, TTM_ADDTOOL, 0, (LPARAM)&tinfo); tinfo2.hinst = m_hInst; tinfo2.hwnd = hwnd; tinfo2.uFlags = TTF_IDISHWND | TTF_SUBCLASS | TTF_CENTERTIP; tinfo2.uId = (UINT_PTR)GetDlgItem(hwnd, IDC_BUTTON2); tinfo2.lpszText = (LPTSTR) _T("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tincidunt \ dictum dui vel luctus. Duis ornare arcu a varius pharetra. Curabitur tempor sit \ amet dui id malesuada. Aliquam efficitur, massa consectetur tristique iaculis, \ dolor diam tincidunt."); SendMessage(m_hTip, TTM_ADDTOOL, 0, (LPARAM)&tinfo2);
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T20:02:03.583+00:00 In fact your code is the same as mine, except for the use of the constant CW_USEDEFAULT, but I tested using them and the result for me did not change. Another thing you did that I didn't understand was TOOLINFO ti = { 0 }; Would this be the same as using ZeroMemory? If so, does not using this affect the end result?
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T20:19:17.117+00:00 Although I see differences between your code and @Castorix31 's one, I consider them to be very similar and therefore it is also very similar to mine. You went further, and used two TOOLINFO structures, one for each button and in this case, it just proved even more that it doesn't seem to have anything to do with this structure itself.
The mystery persists, but now from what I see we have different behaviors in the same development environments (VS2022). As I don't have deep knowledge of C++ I can't point out relevant differences between your codes and therefore I can't proceed. If you can and want, you can try to figure out what makes a code work, but not the other.
If you need more information or want me to test something, I'll be happy to help too.
-
Castorix31 81,831 Reputation points
2022-03-24T20:30:56.147+00:00 Another thing you did that I didn't understand was TOOLINFO ti = { 0 }; Would this be the same as using ZeroMemory?
Yes, it is the same thing, they initialize all the members of the structure to 0 (ZeroMemory is useless in this case, but I copied/translated your code to be 100% sure we use exactly the same code...)
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T20:48:59.87+00:00 So I can assume that whether you use this form alone or ZeroMemory alone, the result is exactly the same. In this case, the mystery persists, as @RLWA32 manages to reproduce the problem using apparently the same code as you :/
-
Castorix31 81,831 Reputation points
2022-03-24T21:02:55+00:00 As RLWA32 tested with VS 2019 and VS 2022, it does not seem to be a VS difference
Maybe the OS is a bit different (mine is Windows 10 Education 21H1 - 19043.1526 as I posted above)
I also tested by copy-paste the code posted by RLWA32, but impossible to reproduce the problem.
I have no more idea... -
RLWA32 40,771 Reputation points
2022-03-24T21:06:18.77+00:00 I'm baffled also. When I build Castorix31 code on my system the issue does NOT reproduce.
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T21:08:33.603+00:00 I just discovered another weirdness. @RLWA32 , open your example and place the window near the left edge of your desktop so that when it appears, the balloon would be "outside" the visible area of the screen. If you don't have a second monitor on the left side of your main monitor,
you will see that the balloon will appear completely on the main monitor (it will be shifted to the right) and because of that it will appear with the new style instead of the old style! -
RLWA32 40,771 Reputation points
2022-03-24T21:12:42.347+00:00 This is getting stranger by the minute. My head hurts. :)
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T21:14:45.517+00:00 And just to make it even weirder, the behavior is reversed for the ToolTip that doesn't have TTF_CENTERTIP when doing the same thing (put the window very close to the edge of the screen so that the balloon needs to be repositioned to appear)
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-24T21:48:01.803+00:00 For a moment I thought it was a difference in your codes, but as Castorix31 said, even copying the code from RLWA32-6355 the problem did not reproduce itself. The strange thing is that he did the same with Castorix31's code and managed to have the correct behavior!! I really don't know what to bet on anymore...
My Windows is Windows Pro 21H1 (10.0.19043.1586) e my ComCtl32.dll is 5.82.19041.1110 (In case this is the correct library that handles common controls)
-
Castorix31 81,831 Reputation points
2022-03-25T07:55:22.797+00:00 A progress :
I could reproduce the bug by using the same code I tested, but in a DialogBox : -
RLWA32 40,771 Reputation points
2022-03-25T08:16:29.557+00:00 My Win32 test application was a modeless dialog. :)
-
RLWA32 40,771 Reputation points
2022-03-25T08:22:07.163+00:00 And I noticed that If a short string for the tooltip text is used then both tooltips are shown with rounded corners.
-
Castorix31 81,831 Reputation points
2022-03-25T09:15:48.277+00:00 And I noticed that If a short string for the tooltip text is used then both tooltips are shown with rounded corners.
Ah yes. even without DialogBox
But rounded corners seem to be the normal behaviour then (?) ..
[Edit] : Rounded corners disappear with a line break in the text...
[Edit] : Ah, only with TTM_SETTITLE -
RLWA32 40,771 Reputation points
2022-03-25T09:39:27.437+00:00 Well, at least we know that we're not crazy -- seems to be a Windows issue.
-
Junjie Zhu - MSFT 15,211 Reputation points • Microsoft Vendor
2022-03-25T09:56:06.907+00:00 Hi @RLWA32 , @Castorix31 I didn't reproduce this issue on Windows 11, I referred to @Castorix31 method of reproduction. https://github.com/SchrdingerCAT123/MSQA_TTF_CENTERTIP_Test/blob/main/WIN32code.cpp
-
RLWA32 40,771 Reputation points
2022-03-25T10:28:01.23+00:00 Could you expand your answer a bit. The discussion in this thread has touched upon windows vs. dialogs, short vs. long strings (linebreaks), with/without TTM_SETTITLE, and behavioral difference based on location.
It would be helpful if your Windows 11 testing touched on the various scenarios discussed. Thanks.
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-26T01:56:30.573+00:00 Hi guys, I took some free time and converted the @Castorix31 code into Delphi and to my surprise it worked as it should! The code is available at https://osdn.net/users/derekwildstar/pastebin/7879 and the image below shows the result
I saw that you guys talked about the type of window being created. In Delphi, using normal means, I don't know exactly how it creates the windows, but I can certainly change their characteristics once created (SetWindowLong). The problem is figuring out exactly what is affecting the ToolTip display
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-26T02:00:19.127+00:00 @RLWA32 , as you said that you used a different type of window and that maybe because of that the problem was reproduced initially, could you share here the code you used to initialize and create the main window (parent window of the controls and ToolTip)?
-
RLWA32 40,771 Reputation points
2022-03-26T09:07:08.803+00:00 I uploaded a Visual Studio project with C++ code for a dialog-based application that demonstrates the issue. You can get it from OneDrive at https://1drv.ms/u/s!AmnqrCFBv4nDghj3X7H8MX3K7Na3?e=aB0WZH
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-27T13:35:47.847+00:00 Thanks! I'll compare it now :)
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-28T00:38:00.393+00:00 I was also able to reproduce the behavior, if interested, the source code is at https://osdn.net/users/derekwildstar/pastebin/7912
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-28T01:07:33.277+00:00 Well folks, after reproducing the behaviors exactly as you did, I came to few conclusions.
Delphi creates a hidden window (actually zero width and height) which it calls Application, from the TApplication class. As far as I was able to debug, first the TApplication class is registered (RegisterClass) and then CreateWindowEx is used as follows:
CreateWindowEx(WS_EX_TOOLWINDOW, 'TApplication', 'Project1',WS_POPUP or WS_CAPTION or WS_CLIPSIBLINGS or WS_SYSMENU or WS_MINIMIZEBOX,GetSystemMetrics(SM_CXSCREEN) div 2,GetSystemMetrics(SM_CYSCREEN) div 2,0, 0, 0, 0, HInstance, nil);
After that, apparently (at some point), what we call the Main Form is created, which is a window also created with CreateWindowEx in a parameterized way, but in my example here, it has the following parameters
CreateWindowEx(327680, 'TForm1', 'Form1',114229248,-2147483648,-2147483648, 320, 240, 0, 0, HInstance, nil);
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-28T01:08:24.697+00:00 (Cont)
Delphi doesn't use resource files to create dialog boxes like VS, it does it in a similar way (saves a property definition file in the program resources), but it doesn't use CreateDialog, so it's closer to the example of @Castorix31 . I repeat, the fact that Delphi uses resource files to configure its windows (Forms) is not even close to what is done in Visual Studio. What it does is more like a "script" that is run after the window is created to configure the properties of the window and its internal components (buttons, etc.). In other words, the .exe file generated by Delphi does not have a DIALOG section, so I strongly believe that this is not the reason for the problems.
I tried dealing with the ExStyles and Styles, but nothing changed the examples anyway. I tried to create a "hidden" window BEFORE the main window which worked correctly, in an attempt to provoke the problem and nothing happened.
In short I couldn't understand why when using the TTF_CENTERTIP flag the balloon style reverts to the Windows XP style. I understood that when using CreateDialog this happens, however this is not used by Delphi, so there is something that is common to CreateDialog and traditional window creation that I couldn't identify
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-28T01:13:03.79+00:00 (Cont)
As a C++ non-speaker I also don't know what the practical technical difference would be between something created with CreateDialog and something created with CreateWindow. I understood that they are different ways of doing the same thing, and I even found the way CreateDialog works interesting, as it allows you to define a good part of the window in a resource file, easy to edit but other than that I don't really understand why when using CreateDialog the behavior of TTF_CENTERTIP is changed.
I really appreciate your help and I hope that one day some MS engineer will read this question and be able to justify these behaviors that we were able to prove
-
Junjie Zhu - MSFT 15,211 Reputation points • Microsoft Vendor
2022-03-28T10:08:30.057+00:00 Hello @Carlos Barreto Feitoza Filho , I have reported your questions to the team, and I will inform you immediately if any progress
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-03-28T13:29:53.943+00:00 Thank you very much @Junjie Zhu - MSFT :)
-
Carlos Barreto Feitoza Filho 21 Reputation points
2022-04-03T13:57:24.007+00:00 Even though I've reached my limit when it comes to solving this problem, it just doesn't get out of my head. Today I opened one of my projects, in which I noticed this problem for the first time and decided to reduce the balloon text a little, which, as you know, also influences the problem,
and what I got was a hybrid balloon!!Note that the indicator (the tip of the balloon) has the modern style, while the balloon itself is the Windows XP style.
-
Junjie Zhu - MSFT 15,211 Reputation points • Microsoft Vendor
2022-04-04T10:04:53.067+00:00 I have reported this bug to the team and will keep you informed if there is any progress.
Sign in to comment