I am in the process of trying to make an app that has most of its development done in Visual C++ 6.0 adhere to Windows theming, or to be precise the visual styles aspect of it. The application in question is a MFC application with the MBCS characterset. Getting theming to kick in in the first place required the InitCommonControlsEx(...); trick combined with the proper manifests for the Common Controls 6.0. Nothing special thus far.
With all that done, I have tons of windows which have some semblance of drawing glitches showing - white rectangles on the background where there used to be gray like there is in the rest of the window, but they do not hinder.
The biggest troublemaker is in a CDialog descendant that implements an (archaic) 'did you know...' tip dialog. It refuses to draw anything other than the buttons, checkbox and decorative frame the moment the manifest is in place. However, if I take the manifest OUT, everything works fine.
Comparison images right after invoking the dialog
I have already stepped through the OnPaint code (which draws the icon and the 'did you know' text, and all functions return sensible and successful values. I also tried removing the entire customdrawn bits (commented out the mappings for the WM_PAINT and WM_CTLCOLOR messages), but even that caused nothing to show or act different for that matter.
Once I click the 'Next Tip' button, it will properly paint the next tip in the list. However, the customdrawn header with the bulb icon remains missing.
The code really does nothing special that I can spot, and I'm at a loss. Since I suspect the dialog definition is of most help, I'll include that, although I can post other things if people have ideas on where the problems are hiding.
IDD_TIP DIALOG DISCARDABLE 0, 0, 231, 164
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Tip of the Day"
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME,12,11,207,123
LTEXT "Some String",IDC_TIPSTRING,28,63,177,60
CONTROL "&Show Tips on StartUp",IDC_STARTUP,"Button",
BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,13,146,85,10
PUSHBUTTON "&Next Tip",IDC_NEXTTIP,109,143,50,14,WS_GROUP
DEFPUSHBUTTON "&Close",IDOK,168,143,50,14,WS_GROUP
CONTROL "",IDC_BULB,"Static",SS_BITMAP,20,17,190,111
END
The machine I am testing this on is a W7 64-bit machine running VS2010. The application in question itself is 32-bit.
Edit:
A new day provides new insights. Somehow, the issue is with the handling of the WM_CTLCOLOR message. When I set it to return a NULL_BRUSH, I finally saw things being drawn (that of course blurred on top of another after initial drawing). Nailing it down even further, it seems that when nCtlColor == CTLCOLOR_STATIC, if I return a NULL_BRUSH, stuff will display. The moment I make it a WHITE_BRUSH nothing gets drawn again, however.
Original code:
HBRUSH CTipDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if (pWnd->GetDlgCtrlID() == IDC_TIPSTRING)
return (HBRUSH)GetStockObject(WHITE_BRUSH);
else if (nCtlColor == CTLCOLOR_STATIC)
/* Visual styles enabled causes the things we draw in our WM_PAINT
* to get cleared (and not redrawn) if we do not use a hollow brush here.
* Likely the default behaviour differs when using theming. */
return (HBRUSH)GetStockObject(HOLLOW_BRUSH);
return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}
Problem solved! The lines that are in bold are what I changed to fix my problem, including the obligatory comments. :)
As far as I know, version 6 of the Common Controls only officially supports Unicode.
According to a blog entry by Raymond Chen, those controls work with the ANSI character set to a degree, but only for backwards compatibility reasons. No one should be using that intentionally.
If you're an ANSI application and you create controls from the common controls library, you may encounter strange behavior. It'll mostly work, but things may be weird at the fringe.
...
And it means that all you folks who are using version 6 of the common controls but haven't converted to Unicode are relying on a compatibility loophole. The ANSI support is there for the old programs that thought they were talking to a version 5 common control; it isn't there for you.
I've never tried doing such a thing so I cannot say for sure, but you may be encountering some of this "strange behavior". If that is the case, then the only officially supported options would be to either convert to Unicode or go back to using version 5 of the controls. Otherwise you would be left trying to find workarounds to any odd behavior you might find, and that doesn't sound like a good situation to be in.
Related
I'm working on a basic desktop app in C++ / Win32.
My goal right now is to create a basic "sticky note" app that would be pinned / glued to the desktop, i.e always in front of the desktop but always behind of any other application.
Really a personal project there, just to fight my bad memory and have my tasks/notes always visible on the desktop so I couldn't miss them when starting the computer & so on.
The behaviour I'm aiming for would be similar to Stardock Fences ("kind of" because I'm not going to store any desktop icon in there, but you hopefully get the idea)
I started with the sample code from the Get Started with Win32 and C++ docs to have the most basic Win32 minimal window setup.
What I got so far :
I managed to keep my window on bottom of every other app and in front of the desktop by calling SetWindowPos in the window procedure (WindowProc), when handling the event WM_SETFOCUS (I first tried with the event WM_WINDOWPOSCHANGING as suggested in this answer but this resulted in an annoying flickering when dragging the window).
case WM_SETFOCUS:
SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
return 0;
The problem : my window stays in front of the desktop, except when I click on the "Show Desktop" button in the taskbar (or hit the Windows + D shortcut). As I often use this shortcut myself, I'd like my window to stay over the desktop no matter what.
A not-satisfying-enough-but-still-something I managed to do is to bring back my window in front of the desktop when clicking on any other window after hitting Windows + D (this mostly makes sense with multiple monitors, as opening a random app on the first one for example, will toggle back my own app in front of the desktop on another screen).
I could do this using this time the event WM_SIZE and calling ShowWindow then SetWindowPos, still in the WindowProc
case WM_SIZE:
ShowWindow(hwnd, SW_SHOWNORMAL);
SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
return 0;
Not ideal though, as I'd really want my app to always remain in front of the desktop and "survive" to the Show Desktop action.
What I tried :
I checked out those answers but couldn't figure out how to achieve what I want.
How to make 'always-on-bottom'-window
Window “on desktop” : Note on this one, I tried the trick with SetParent like this in the wWinMain
HWND desktop = FindWindow(L"ProgMan", L"Program Manager");
if (desktop == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
SetParent(hwnd, desktop);
However, my app isn't visible at all anymore with this, even though the FindWindow didn't return NULL but an actual handle.
Make aplication always on Bottom (pinned to desktop, behind all other apps) in C++/WinAPI [duplicate]
Disable Minimize, Maximize, Close buttons in Win32 I tried those to "intercept" the Show Desktop event but it seems this event doesn't get fired with the Show Desktop action.
Did I miss something ?
As #JonathanPotter pointed out, when hitting Windows + D or the Show Desktop button, the event WM_WINDOWPOSCHANGING gets fired, and the window gets moved to -32 000, -32 000 (its size also gets changed)
NOTE : a window without the style WS_MINIMIZEBOX seems not receiving WINDOWPOSCHANGING event when hitting Windows + D. Thus, no -32 000 coordinates detection in that case... Also noticed the same issue when using the ex style WS_EX_TOOLWINDOW (as this one gets rid of the minimize box, even if you set the style flag WS_MINIMIZEBOX).
Didn't find a solution for that case, so I'm sticking to an overlapped window.
To prevent this movement, just set the flags SWP_NOMOVE and SWP_NOSIZE on the WINDOWPOS structure passed in the lParam
So as a final result, to achieve the wanted behaviour (i.e always behind every other window but always in front of the desktop), the only needed code to add to the doc's sample is the following, placed in the window procedure WindowProc's switch statement :
EDIT : the best place to force the Z order with HWND_BOTTOM thus ensuring the window is always on bottom is also in the WM_WINDOWPOSCHANGING event. Indeed, calling SetWindowPos to force it in the WM_SIZE event when dragging the window over, as I was doing previously, causes some flickering on the window when resizing it, whereas no flickering occurs when setting directly the hwndInsertAfter property of the WINDOWPOS structure in WM_WINDOWPOSCHANGING.
case WM_WINDOWPOSCHANGING:
{
WINDOWPOS* pos = (WINDOWPOS*)lParam;
// Show desktop (Windows + D) results in the window moved to -32000, -32000 and size changed
if (pos->x == -32000) {
// Set the flags to prevent this and "survive" to the desktop toggle
pos->flags |= SWP_NOMOVE | SWP_NOSIZE;
}
// Also force the z order to ensure the window is always on bottom
pos->hwndInsertAfter = HWND_BOTTOM;
return 0;
}
I had the same problem and found a solution via using "autohotkey". autohotkey is a powerful free scripting language for automation in windows that you can download from here. Below scripts are an instance of making sticky notes always on top when you execute it inside a .ahk file (you can copy these codes to a simple notepad and then change .txt to .ahk if you already had installed autohotkey on your computer). by right clicking on .ahk file, you can also see option of Compiling .ahk into .exe which enables you to share it with your other friends too:
#SingleInstance Force
GroupAdd, ontop, Sticky Notes ; you can replace sticky note by any application,
; if you know the name of that application window
; (and its ahk_class for more specific cases).
; for example you can replace "Sticky Notes"
; with "Calculator" to make calculator stay always
; on top.
Loop {
WinWait, ahk_group ontop
WinSet, AlwaysOnTop, On
SoundBeep, 1500
WinWaitClose
SoundBeep, 1000
}
(main codes are credit of mikeyww)
So, I have stumbled upon an interesting bug with the Windows API and I'm wondering if anyone has some insight on how to work around it. It seems that even Google has struggled with it. It should be noted that while I will be fixing this in Qt source itself, the problem is with Windows default message handling, not Qt. All of the files that I will mention can be found online as they are all open source libraries. Below is somewhat of a complex problem and I will try and give as much context as possible. I've put a lot of time and effort into fixing this myself, but being that I've only been an engineer for about 8 months, I'm still quite inexperienced and could very well have missed something obvious.
The context:
I have written a program that uses Qt to skin my windows with custom skins. These skins go over the system default non-client UI skins. In other words, I use custom painted frames (supported by Qt). Since Qt5 I've been having issues with my program when it is run on any pre-Windows Aero OS ( less than XP and greater than Vista with Windows Aero disabled). Unfortunately, Qt devs have all but confirmed that they do not really support XP anymore, so I will not rely on them to fix the bug.
The Bug:
Clicking anywhere in the non-client area while running a machine with composition disabled (Windows Aero disabled or not existing) will cause Windows to repaint its system default non-client UI on top of my custom skin.
My Research
A bit of debugging and investigation led me to qWindowsProc in qwindowscontext.cpp. I was able to determine that the last windows message to be handled before my window's skin was painted over was WM_NCLBUTTONDOWN. This seemed strange, so I took to the internets.
Sure enough, I found a file called hwnd_message_Handler.cc that comes from Google's Chromium Embedded Framework (CEF). In that file are many comments about how various windows messages, for some insane reason, cause repaints of the system default non-client frames over custom frames. The following is one such comment.
// A scoping class that prevents a window from being able to redraw in response
// to invalidations that may occur within it for the lifetime of the object.
//
// Why would we want such a thing? Well, it turns out Windows has some
// "unorthodox" behavior when it comes to painting its non-client areas.
// Occasionally, Windows will paint portions of the default non-client area
// right over the top of the custom frame. This is not simply fixed by handling
// WM_NCPAINT/WM_PAINT, with some investigation it turns out that this
// rendering is being done *inside* the default implementation of some message
// handlers and functions:
// . **WM_SETTEXT**
// . **WM_SETICON**
// . **WM_NCLBUTTONDOWN**
// . EnableMenuItem, called from our WM_INITMENU handler
// The solution is to handle these messages and **call DefWindowProc ourselves**,
// but prevent the window from being able to update itself for the duration of
// the call. We do this with this class, which automatically calls its
// associated Window's lock and unlock functions as it is created and destroyed.
// See documentation in those methods for the technique used.
//
// The lock only has an effect if the window was visible upon lock creation, as
// it doesn't guard against direct visiblility changes, and multiple locks may
// exist simultaneously to handle certain nested Windows messages.
//
// IMPORTANT: Do not use this scoping object for large scopes or periods of
// time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh).
//
// I would love to hear Raymond Chen's explanation for all this. And maybe a
// list of other messages that this applies to ;-)
Also in that file exists several custom message handlers to prevent this bug from occurring. For example, another message I found that causes this bug is WM_SETCURSOR. Sure enough, they have a handler for that which, when ported to my program, worked wonderfully.
One of the common ways they handle these messages is with a ScopedRedrawLock. Essentially, this just locks redrawing at the beginning of the hostile message's default handling (via DefWindowProc) and remains locked for the duration of the call, unlocking itself when it comes out of scope (hence, ScopedRedrawLock). This will not work for WM_NCLBUTTONDOWN for the following reason:
Stepping through qWindowsWndProc during the default handling of WM_NCLBUTTONDOWN, I saw that WM_SYSCOMMAND is handled in the same call stack directly after WM_NCLBUTTONDOWN. The wParam for this particular WM_SYSCOMMAND is 0xf012 - another officially undocumented value**. Luckily in the remarks section of the MSDN WM_SYSCOMMAND page somebody commented about it. Turns out, it is the SC_DRAGMOVE code.
For reasons that may seem obvious, we cannot simply lock redrawing for the handling of WM_NCLBUTTONDOWN because Windows automatically assumes that the user is trying to drag the window if he clicks on a non-client area (in this case, HTCAPTION). Locking here will cause the window to never redraw for the duration of the drag- until Windows receives a button up message(WM_NCLBUTTONUP or WM_LBUTTONUP).
And sure enough, I find this comment in their code,
if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU &&
delegate_->IsUsingCustomFrame()) {
// TODO(msw): Eliminate undesired painting, or re-evaluate this workaround.
// DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we
// need to call it inside a ScopedRedrawLock. This may cause other negative
// side-effects (ex/ stifling non-client mouse releases).
DefWindowProcWithRedrawLock(message, w_param, l_param);
handled = true;
}
This makes it seem as though they had the same problem, but didn't quite get around to solving it.
The only other place CEF handles WM_NCLBUTTONDOWN in the same scope as this problem is here:
else if (message == WM_NCLBUTTONDOWN && delegate_->IsUsingCustomFrame()) {
switch (w_param) {
case HTCLOSE:
case HTMINBUTTON:
case HTMAXBUTTON: {
// When the mouse is pressed down in these specific non-client areas,
// we need to tell the RootView to send the mouse pressed event (which
// sets capture, allowing subsequent WM_LBUTTONUP (note, _not_
// WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be
// sent by the applicable button's ButtonListener. We _have_ to do this
// way rather than letting Windows just send the syscommand itself (as
// would happen if we never did this dance) because for some insane
// reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed
// window control button appearance, in the Windows classic style, over
// our view! Ick! By handling this message we prevent Windows from
// doing this undesirable thing, but that means we need to roll the
// sys-command handling ourselves.
// Combine |w_param| with common key state message flags.
w_param |= base::win::IsCtrlPressed() ? MK_CONTROL : 0;
w_param |= base::win::IsShiftPressed() ? MK_SHIFT : 0;
}
}
And while that handler addresses a similar problem, its not quite the same.
The Question
So at this point I'm stuck. I'm not quite sure where to look. Maybe I'm reading the code incorrectly? Maybe the answer is there in CEF and I'm just overlooking it? It seems like CEF engineers encountered this problem and have yet to come up with the solution, given the TODO: comment. Does anybody have any idea what else I could do? Where do I go from here? Not solving this bug is not an option. I'm willing to dig deeper but at this point I'm contemplating actually handling Windows drag events myself rather than having the DefWindowProc handle it. Though, that might still cause the bug in the case where the user is actually dragging the window.
Links
I have included a list of links that I have been using in my research. Personally, I downloaded CEF source myself so that I could better navigate the code. If you are truly interested in solving this problem, you might need to do the same.
WM_NCLBUTTONDOWN
WM_NCHITTEST
WM_SYSCOMMAND
DefWindowProc
hwnd_message_handler.cc
hwnd_message_handler.h
qwindowscontext.cpp
Tangent
Just to bring validation to CEF's code, if you look in the header of hwnd_message_handler, you will also notice that there are two undocumented windows messages of value 0xAE and 0xAF. I was seeing 0xAE during the default handling of WM_SETICON that was causing problems, and this code helped confirm that what I was seeing was indeed real.
I found this page which suggests hiding your window by removing WS_VISIBLE immediately before calling DefWindowProc(), then showing it immediately after. I haven't tried it, but it's something to look at.
So, the actual way this fix was achieved was by removing the WS_CAPTION flag during NC_LBUTTONDOWN and adding it back during NC_LBUTTONUP message handling. However, because of the way Windows calculates its size before rendering, it could miscalculate since it removes the caption area from consideration. So, you will need to offset this while handling the WM_NCCALCSIZE message.
Keep in mind that the amount of pixels you will need to offset will vary depending on which windows theme or OS you are in. i.e. Vista has a different theme than XP. So you will need to decide on a scale factor to keep it clean.
I know a few flags that make the window always on top (eg Qt :: ToolTip, Qt :: WindowStaysOnTopHint, Qt :: Popup), but each time the method is the same problem.
By clicking on the start menu, the area tray, empty field between programs and tray - window and so is hiding.
Everything is fine when I switch between different applications and I click anywhere except the above-mentioned places.
Just run other applications that I used to use, so it might be a function of the uninvited Windows.
Code does not make sense given, because at the same time where do dumb mistake, and indeed act as a flag to be apart of this "small" problem.
These things work:
HWND hWnd = reinterpret_cast(this->winId());
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
But it turns out that exactly the same as the flag of the subject. : (
Explanation of what exactly is the problem:
http://youtu.be/k5TCtr1hPKY
The solution is, regular exercise such thing:
if(this->isActiveWindow() == false} {
this->raise();
}
Only the minimized window does not always work, but to me it is unnecessary.
I have a window with several controls on it, but only the edit control is ignoring the visual styles. I've tried to track down the cause but have had no luck so far. Here is what it looks like:
As seen, the button and the listbox controls have the proper visual style. The edit control, however, does not. I had the proper style applied once a few days ago after changing a bunch of code unrelated to it so I know it's possible.
Things to note:
I have tried calls to InitCommonControls and InitCommonControlsEx with no success.
I have the WS_EX_CLIENTEDGE applied to the edit control and have always had that extended style turned on, even when it was working prior.
I have included as much code as I can to help track down this issue.
From my TextBox class (which inherits Component):
protected:
virtual void OnRegistering(CreationParameters& createParams)
{
// this is passed in as the lpClassName parameter in
// CreateWindowEx() and the lpszClassName parameter in WNDCLASSEX
createParams.BaseClassName = WC_EDIT;
// passed as the dwStyle parameter in CreateWindowEx()
createParams.WindowStyles |= ES_AUTOHSCROLL | ES_AUTOVSCROLL;
// passed as the dwExStyle parameter in CreateWindowEx()
createParams.WindowExStyles |= WS_EX_CLIENTEDGE;
}
Would an issue with handling the WM_CTLCOLOREDIT or WM_ERASEBKGND messages have anything to do with this? Or an issue with handling fonts? Or something else? Because I am 100% out of ideas at this point.
The issue was that the HBRUSH I was using to color the edit control was, somehow, inexplicably never initialized and was NULL. Since I know other people were having issues with this, here is my solution.
The problem manifested itself when WM_CTLCOLOREDIT arrived in the message queue. If an improper/incorrect HBRUSH is returned when that message arrives, the edit control reverts back to old visual styles. Since my HBRUSH was NULL, the old visual style was applied to the edit control.
We're writing Windows desktop apps using C++ and Win32. Our dialog boxes have an ugly appearance with "Windows XP style": the background to the static text is grey. Where the dialog box background is also grey, this is not a problem, but inside a tab control, where the background is white, the grey background to the text is very noticeable.
In the past we have done a lot of our own drawing of controls, but these days we are trying to use the standard look'n'feel as much as possible, and to avoid overriding standard behaviour as much as possible.
We are using the Win32 API, which is getting a bit dated, but I think the problem occurs even with ATL. We are creating a DIALOGTEMPLATE. The text is in a "static" control (0x0082). The only flag we set for the style is "SS_LEFT". The text control is inside a tab control: "SysTabControl32" with only one flag: WS_CLIPSIBLINGS set on it. I've experimented with SS_WHITERECT and WS_EX_TRANSPARENT and other settings, to no avail.
All of this gets drawn with the standard Windows dialog box message handler. My main question is "what are we doing wrong?" rather than "how can I work around it?", although I'll settle for the latter if no-one can help me with the first.
Any ideas?
The usual way of implementing pages in a tab control is required to access MS's solution to this problem :-
Instead of creating individual controls in the tab area, create a modeless child dialog for each page and have the controls on that. Create the page dialogs with the main dialog (not the tab) as their parent and as the user switches between tabs, simply show and hide the relevant page dialog.
In the WM_INITDIALOG handler for each page, call the uxtheme API EnableThemeDialogTexture
With the ETDT_ENABLETAB flag this automatically changes the background color of the dialog and all its child controls to paint appropriately on a tab.
If you have overridden WM_ERASEBKGND or WM_CTLCOLOR* in your pages DialogProc you will need to revert to default handling (return FALSE) as these methods are where the dialog code is going to do its heavy lifting. The style bits should simply be set as though the child page dialog page was going to be created on a normal windows 9X tabbed dialog.
The reason why the background is gray is because that is the default.
To override it, you can process the WM_CTLCOLORSTATIC message in the parent window and return a custom brush.
Your problem is not with ATL or WinAPI. In MFC there is the same problem.
Set Tab control as parent window for Static controls. But I think that overriding WM_DRAWITEM is more flexible solution.
I've got Win32/MFC applications with labels inside tabs on dialogs, and the background colour looks fine on them (that is, it reflects the XP-look theme, rather than being flat grey) without any obvious special handling.
Under the hood what's supposed to happen is that the label posts a WM_CTLCOLOR message to its parent, which sets the device context up appropriately: the default handling in Windows should set the appropriate background colour, at least for dialogs and tab controls.
One possibility is that you're doing something non-standard in your handling of WM_CTLCOLOR: is it over-ridden somewhere in your application? Possibly you have some old code that is setting the label background colour in this way.
(Also, as Rob asks, are you using a manifest to get comctl32 6.0 into your application?)
We're not overriding the WM_CTLCOLORSTATIC message. There's no occurrence of this string in our source code and nothing like it in our message handlers.
We've worked around this problem by overriding the WM_DRAWITEM message for tab controls to paint their contents with the grey background (standard for dialog boxes without tab controls) rather than the white background (standard for the contents of tab controls).
brush = CreateSolidBrush(GetSysColor(COLOR_MENU));
FillRect(lpdis->hDC, &lpdis->rcItem, brush);
SetBkColor(lpdis->hDC, GetSysColor(COLOR_MENU));
wtext = ToWideStrdup(c->u.tabcontrol.Tabs[lpdis->itemID].name);
rect = lpdis->rcItem;
rect.top += DlgMarginY - 1;
rect.bottom += DlgMarginY;
DrawTextW(lpdis->hDC, wtext, -1, &rect, DT_CENTER | DT_VCENTER);
free(wtext);
DeleteObject(brush);
This is obviously a workaround, not a proper answer to my question.
Incidentally, we initialise the "common controls", of which I believe the tab control is one, using code like this...I don't suppose this is related to the issue?
#pragma comment(linker, "/manifestdependency:\"type='win32' " \
"name='Microsoft.Windows.Common-Controls' " \
"version='6.0.0.0' " \
"processorArchitecture='*' " \
"publicKeyToken='6595b64144ccf1df' " \
"language='*'\"")
...
hCommCtrl = GetModuleHandle("comctl32.dll");`
if (hCommCtrl) {
ptrInit = (TfcInit_fn) GetProcAddress(hCommCtrl, "InitCommonControlsEx");
if (ptrInit) {
data.dwSize = sizeof(INITCOMMONCONTROLSEX);
data.dwICC = ctrlClass;
if (ptrInit(&data) )
gCommCtrlsInitialized |= ICC_TAB_CLASSES | ICC_BAR_CLASSES;
}
}
Have spent an amazing amount of time to try to fix a problem so seemingly simple, tried almost every constant for hbrBackground without success.
As an amateur found that the most simple & time efficient fix was to simply create a "Static" class child window that envelops the entire window. Just another hack level fix though