Current version

v1.10.4 (stable)

Navigation

Main page
Archived news
Downloads
Documentation
   Capture
   Compiling
   Processing
   Crashes
Features
Filters
Plugin SDK
Knowledge base
Contact info
 
Other projects
   Altirra

Archives

Blog Archive

Another common controls V6 bug

Here is a stack trace of a Win32 program being debugged under WinDbg. Can you guess what the bug is from this stack trace?

0:000> kb
ChildEBP RetAddr  Args to Child             
0018f470 70582d28 00000000 00000000 00000001 USER32!NtUserInvalidateRect+0x1
0018f48c 7054ff2f 069faf50 00000000 00000004 COMCTL32!Tab_OnHScroll+0x4e
0018f4cc 705284ec 069faf50 00000001 0018f524 COMCTL32!Tab_UpdateArrows+0xe4
0018f4dc 705284cb 069faf50 00000000 7052723b COMCTL32!Tab_Size+0x17
0018f524 766362fa 00a6096a 00000005 00000000 COMCTL32!Tab_WndProc+0x18f
0018f550 76636d3a 7052723b 00a6096a 00000005 USER32!InternalCallWinProc+0x23
0018f5c8 766390c9 00000000 7052723b 00a6096a USER32!UserCallWinProcCheckWow+0x109
0018f658 76636a8c 05569460 00000000 7052723b USER32!RealDefWindowProcWorker+0x622
0018f678 76636ae9 00a6096a 00000047 00000000 USER32!RealDefWindowProcW+0x4a
0018f6c0 7052734c 00a6096a 00000047 00000000 USER32!DefWindowProcW+0x6f
0018f714 766362fa 00a6096a 00000047 00000000 COMCTL32!Tab_WndProc+0x399
0018f740 76636d3a 7052723b 00a6096a 00000047 USER32!InternalCallWinProc+0x23
0018f7b8 76636de8 00000000 7052723b 00a6096a USER32!UserCallWinProcCheckWow+0x109
0018f814 76638fa7 05569460 00000000 00000047 USER32!DispatchClientMessage+0xe0
0018f850 7793010a 0018f868 00000000 0018f9c4 USER32!__fnINLPWINDOWPOS+0x2c
0018f864 05569460 00000000 00000047 00000000 ntdll!KiUserCallbackDispatcher+0x2e

The bug is that the tab control in the common controls library (comctl32) is invalidating the desktop window via InvalidateRect(NULL, NULL, TRUE), which causes the entire screen to redraw and flicker. This is noticeable with Visual Studio's slow redraw, and one of the first things I do to track it down is to set a conditional breakpoint for InvalidateRect(NULL). In this case, it immediately pointed to the tab control as the culprit.

From what I've been able to tell, this happens if you create a tab control at 0x0 size, add tabs to it, select a tab, and then resize it to a non-empty size. This appears to be yet another comctl32 v6 bug since removing the manifest and using v5.80 makes the bug go away. I'm beginning to regret enabling visual styles since this is about the fourth v6-specific bug that I've hit.

If you're wondering about the reason for such a weird chain of events, it was because of some deferred layout code. When bringing up a complex UI or handling resizing, it's best to try to do all window operations at once to avoid redundant redraws, which slows down the relayout and can add unnecessary flickering. This means using dirty flags to avoid redundant layout operations, using DeferWindowPos() in Win32 to batch window operations and keeping windows hidden until they're in the correct position. Unfortunately, in this case it triggered the tab control bug by delaying the first resize until after tabs had already been added. The workaround is simple: create the tab control as 1x1 instead of 0x0.

Comments

This blog was originally open for comments when this entry was first posted, but was later closed and then removed due to spam and after a migration away from the original blog software. Unfortunately, it would have been a lot of work to reformat the comments to republish them. The author thanks everyone who posted comments and added to the discussion.