Geoff Chappell - Software Analyst
BOOL InitCommonControlsEx (INITCOMMONCONTROLSEX const *picce);
Since the INITCOMMONCONTROLSEX structure is apparently used only for this function, its definition is as well given here:
typedef struct tagINITCOMMONCONTROLSEX { DWORD dwSize; DWORD dwICC; } INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;
The picce argument provides the address of a small structure that specifies which common controls to initialise.
The function returns TRUE for success, else FALSE.
The function fails if the picce argument is NULL. Otherwise, this argument is taken as addressing an INITCOMMONCONTROLSEX structure. The function fails if the dwSize member is not the size, in bytes, of this structure. The dwICC member is then interpreted as bit flags. The function fails if any invalid flags are set. The valid flags select initialisations to perform. If any initialisation fails, then so does the function.
Each of these initialisations registers window classes for one or more of the common controls. Note that the initialisations are repeatable, as when the same flag is set in multiple calls to the function. The purpose of the function is thus to ensure that the window classes for the wanted types of common control are registered in advance of trying to create any of the controls.
The following table lists the valid flags. Each flag is shown with the controls whose window classes are to be registered if the flag is set. This table is also a convenient place in which to summarise which COMCTL32 versions support which flags.
Value | Symbolic Name | Controls to Register | Applicable Versions |
---|---|---|---|
0x0001 | ICC_LISTVIEW_CLASSES | List-View Control Header Control |
|
0x0002 | ICC_TREEVIEW_CLASSES | ToolTip Control Tree-View Control |
|
0x0004 | ICC_BAR_CLASSES | Toolbar Control ToolTip Control Status Bar Trackbar Control |
|
0x0008 | ICC_TAB_CLASSES | ToolTip Control Tab Control |
|
0x0010 | ICC_UPDOWN_CLASS | Up-Down Control | |
0x0020 | ICC_PROGRESS_CLASS | Progress Bar Control | |
0x0040 | ICC_HOTKEY_CLASS | Hot Key Control | |
0x0080 | ICC_ANIMATE_CLASS | Animation Control | |
0x0100 | ICC_DATE_CLASSES | Month Calendar Control Date and Time Picker Control |
|
DropDown | 6.10 and higher | ||
0x0200 | ICC_USEREX_CLASSES | ComboBoxEx Control | |
0x0400 | ICC_COOL_CLASSES | Rebar Control | 4.70 and higher |
0x0800 | ICC_INTERNET_CLASSES | IP Address Control | 4.71 and higher |
0x1000 | ICC_PAGESCROLLER_CLASS | Pager Control | 4.71 and higher |
0x2000 | ICC_NATIVEFNTCTL_CLASS | Native Font Control | 4.71 and higher |
0x4000 | ICC_STANDARD_CLASSES | Button Control Static Control Edit Control ListBox Control ComboBox Control ComboLBox Control Scroll Bar Reader Mode Control |
6.0 and higher |
0x8000 | ICC_LINK_CLASS | SysLink Control | 6.0 and higher |
Version 4.0 does not guard against invalid flags.
Though not shown in the table, the ICC_STANDARD_CLASSES flag is supported in versions 5.80 and 5.81, but trivially. It is not rejected as invalid, but neither does it select an initialisation to perform. In version 5.82, the flag becomes invalid again.
Also not shown in the table is an undocumented flag, 0x80000000, which is supported in version 5.81 and higher. It is unusual in not causing COMCTL32 to register a window class. It appears to be intended for use by WINLOGON.EXE to arrange a reset of COMCTL32. Useful details would seem to require more study of the COMCTL32 subclassing functions (and of WINLOGON) than is presently within the scope of these notes.
In versions 5.82 and 6.10 from Windows Vista, and higher, the 0x80000000 flag has only trivial support. It is not rejected as invalid, but neither does it select an initialisation to perform.
The history of COMCTL32 includes some subtle changes to the algorithm for initialising each control. In calling these changes subtle, it is here meant that the variations might easily be overlooked as uninteresting, even really uninteresting, except that they turn out to have some practical consequences which Microsoft certainly has realised yet doesn’t document.
COMCTL32’s original algorithm for initialising each control is to register the corresponding class if it is not already registered:
A slight variation applies to the controls represented by ICC_DATE_CLASSES. These are handled together, such that only the class for the Month Calendar Control is checked before attempting registration of classes for both that control and the Date and Time Picker Control.
This original algorithm leaves InitCommonControlsEx prone to fail in programs that may call the function from more than one thread. If the calls overlap, it may happen that a class does not exist at the first step but the subsequent attempt to register the class fails because it has meanwhile got registered from another thread.
The first attempt, chronologically, to address this problem of atomic class registration came with the builds of version 6.0 from before Windows XP SP2. For all but one control, the steps in the original algorithm are reversed:
For who can know what reason, the Scroll Bar control is treated differently. For it, there is just the attempt at registration. If this fails, then so does the initialisation (and thence the function).
In these builds of version 6.0, Microsoft may have tried to solve a problem that matters only when multiple calls from different threads happen to overlap, but has instead made a problem whenever multiple calls are made to InitCommonControlsEx with ICC_STANDARD_CLASSES set, whether overlapped or not. The first call will typically succeed, but the repeats are doomed to fail because the class for the Scroll Bar control is already registered. This has practical consequences even for programmers who mean to call InitCommonControlsEx just once in their program: as described later, a programmer’s one and only call to InitCommonControlsEx always is in fact a repeated call, because an earlier call is made by COMCTL32 itself at process attachment.
The builds of version 6.0 from Windows XP SP2 and higher, but excluding Windows XP SP3, refine the test for whether the class is already registered:
Yet still the Scroll Bar is treated differently—not just from the other controls but from the previous builds. Its initialisation is still just to register the class, but failure is now ignored.
The first builds of version 5.82 retain the original algorithm, as if they have do not have the problem with atomic class registration which needed to be fixed for version 6.0. Perhaps it was only when COMCTL32 was used as an assembly that overlapping calls to InitCommonControlsEx from different threads were ever seen. Eventually, however, version 5.82 changed too—just not to anything like what’s done for the corresponding builds of version 6.0.
In builds of version 5.82 from Windows XP SP2 and higher, but excluding Windows XP SP3, the algorithm is greatly simplified: just register the class and ignore any failure. All initialisations always succeed.
The great simplification for later builds of version 5.82 is also the practical effect of changes made for version 6.10 as far as concerns the success or failure of InitCommonControlsEx. The algorithm for initialising a control is just to register the corresponding class and assume success. All initialisations always succeed.
However, failure to register a class is not exactly ignored. As discussed later, COMCTL32 remembers which classes it has registered, so that they can be un-registered at process detachment. The algorithm is in full:
An entirely different solution is ventured in the versions 5.82 and 6.0 from Windows XP SP3 specifically:
Chronologically, this release appeared among the Windows Vista service packs. Perhaps the algorithm that is broadly similar for versions 5.82 and 6.10 from Windows Vista was not thought entirely satisfactory (which arguably it is not), such that a revision was attempted but then abandoned, surviving only in Windows XP SP3.
Given that InitCommonControlsEx is presented as a simple function in Microsoft’s own documentation and that it is easy to find commentary on the Internet to the effect that the function is almost trivial, Microsoft has certainly made very heavy work of this problem of registering a window class if and only if it does not already exist. The problem must of course be handled atomically within USER32, such that it ought to have a simple and sound solution. Is it that Microsoft’s COMCTL32 programmers know or suspect that something is very wrong in USER32 such that the atomicity of its handling is unreliable? More plausible perhaps is that Microsoft’s own programmers can be as befuddled by sparse documentation as can anyone: see that Microsoft’s documentation of the RegisterClass function does not even go as far as saying whether the function succeeds or fails if the class is already registered.
One of the points, if not the main point, to the SxS versions of COMCTL32 is to redirect the window classes for some controls that would otherwise be implemented in USER32. An important piece of background is that the USER32 functions that work with window classes, e.g., RegisterClass but also GetClassInfo, vary their behaviour depending on the current activation context (not that you would know this from Microsoft’s documentation of those functions). The same name may represent a different window class implemented in a different module, depending on which activation context, if any, is current when the function is called. The implication for COMCTL32 is that when InitCommonControlsEx registers window classes, it matters which activation context is current.
Early builds of versions 5.82 and 6.0 leave InitCommonControlsEx to the caller’s choice of activation context. This is surely a bug and was eventually corrected. In builds of version 5.82 and 6.0 from Windows XP SP2, and higher, COMCTL32 imposes its own activation context while InitCommonControlsEx registers classes.
Recent developments in USER32 take away much of the reason that InitCommonControlsEx exists. Starting with Windows XP, the USER32 functions for creating a window do not require that the window class is yet registered. If the window class is named in the current activation context, then USER32 can call the corresponding assembly to ask it to register the class before USER32 proceeds to create the window.
For this to work, the assembly must export a function named RegisterClassNameW. In practice then, InitCommonControlsEx is redundant for any program whose manifest asks for a sufficiently late COMCTL32 version:
The InitCommonControlsEx function can be called automatically at process attachment, such that its explicit use by the programmer is often redundant. (Of course, this assumes that process attachment actually is attempted. If the program imports no other COMCTL32 function, which can easily enough be true in practice, then although the explicit call is redundant by the time it executes, its presence in the code may be the only reason that COMCTL32 gets loaded at all.)
This implied call most plausibly exists for backwards compatibility. In COMCTL32 versions that precede the InitCommonControlsEx function, all the supported controls are initialised automatically during process attachment. The introduction of InitCommonControlsEx preserved this behaviour by calling the function at process attachment to initialise those same controls that are supported in earlier versions and which are now represented by the flags in ICC_WIN95_CLASSES (0xFF).
The treatment is not fully backwards-compatible, however: in versions that predate InitCommonControlsEx, if any of the automatic initialisations fail, then so does the process attachment, but this is mostly not true of the implied initialisations in later versions. Indeed, though all subsequent versions of COMCTL32 have this implied call at process attachment, there is significant variation in the flags that are used for the call, in whether the call is made only in particular conditions, and in whether failure of the call is fatal to the process attachment.
Versions | Flags | Conditions | Failure |
---|---|---|---|
4.0 (NT) and 4.70 | ICC_WIN95_CLASSES | ignored | |
4.71 to 5.81 | ICC_WIN95_CLASSES | fatal | |
5.82 from Windows XP | all documented flags (0x3FFF) | fatal | |
5.82 from Windows Server 2003 and higher | ICC_WIN95_CLASSES | if running in 16-bit process | ignored |
6.0 from Windows XP | all documented flags (0xFFFF) | ignored | |
6.0 from Windows Server 2003, and higher | all documented flags (0xBFFF) except ICC_STANDARD_CLASSES |
if running in 16-bit process | ignored |
All that Microsoft’s documentation finds to say about any of these cases is:
Windows XP: If a manifest is used, InitCommonControlsEx is not required.
(Even this little got removed from the online documentation some time during 2007 or 2008.) Although the statement is true, it would be true even without using a manifest. Perhaps its author at Microsoft had something else in mind entirely, but the simple fact is that the Windows XP builds of COMCTL32, for both version 5.82 and 6.0, already have called InitCommonControlsEx for all the documented flags before the programmer’s own code has a chance. The InitCommonControlsEx function is therefore redundant for the Windows XP builds, whether a manifest is used or not. Even for COMCTL32 versions 5.81 and earlier, the implied call means that InitCommonControlsEx is redundant for the controls that pre-date the function.
At least there’s no harm in any of that, just redundancy. However, Microsoft’s documentation conveniently understates a problem. In builds of version 6.0 from before Windows XP SP2, not only is InitCommonControlsEx “not required” when a manifest is used, it is better left alone. For the programmer who thinks to include ICC_STANDARD_CLASSES among the flags, the function will be nothing but trouble. This is because these early builds have a defect, described above, concerning initialisation of the Scroll Bar control. Because of the implied call, which will almost certainly have succeeded, the programmer’s explicit call will fail. Microsoft cannot be ignorant of this case, nor of the trouble it may cause, so why isn’t it documented?
There is no function to undo the work of InitCommonControlsEx. Indeed, early versions of COMCTL32 do not provide at all for un-registering the window classes for any common controls. It seems that un-registration was left to be done as the system’s normal cleanup for process termination. This may be fine if COMCTL32 is loaded automatically because COMCTL32 appears in the program’s import directory, but it is problematic if a program loads and unloads COMCTL32 dynamically: most of the controls have the CS_GLOBALCLASS style and therefore remain registered if COMCTL32 is unloaded.
Microsoft first addressed this problem in the builds of version 4.72 from Windows NT 4.0 SP4 and Internet Explorer 4.01 SP2. (The Microsoft blog Don’t forget to unregister your window classes when your DLL shuts down dynamically speaks of a hotfix, presumably from before these retail releases.) In these builds and subsequently, COMCTL32 un-registers the window classes for common controls while handling process detachment. (Though Microsoft’s documentation of DllMain advises against calling USER32 functions, it is presumably no less dangerous to call UnregisterClass while handling process detachment than to call RegisterClass while handling process attachment.)
The original algorithm for each control is to un-register the window class if GetClassInfo succeeds. In versions 5.82 and 6.0 from Windows XP SP2, and higher, the algorithm changes so that the window class for a control is un-registered if the initialisation algorithm for that control either succeeded at registering the window class or found that the class was already registered.
The InitCommonControlsEx function is exported by name from COMCTL32.DLL in version 4.70 and higher, but also in one build of version 4.0. The function is certainly not exported from COMCTL32 in the original Windows 95, but is in Internet Explorer 3.00 for Windows NT. (I have no copy of an Internet Explorer 3.00 for Windows 95. See the list of COMCTL32 versions found for this survey.)
The function has long been documented, but not always. Though COMCTL32 versions that export this function had been on retail release for most of 1996, the function’s only mention in the January 1997 edition of the MSDN Library on CD is in two articles from Microsoft Systems Journal (dated October and November 1996) which promote development for Internet Explorer as the way to get “new and improved common controls” for arbitrary Windows programs.
The documentation puts the “minimum DLL version” at 4.70 for the InitCommonControlsEx function but at 4.71 for the INITCOMMONCONTROLSEX structure. This discrepancy is present at least as early as the January 1999 edition of the MSDN Library on CD.
A recent discrepancy affects the function’s declaration. The argument was for many years given simply as LPINITCOMMONCONTROLSEX, which is equivalent to INITCOMMONCONTROLSEX *. Sometime during 2005 or 2006, the const keyword was added, but inconsistently, to produce const INITCOMMONCONTROLSEX * in the COMMCTRL.H from the Windows Vista SDK but const LPINITCOMMONCONTROLSEX in the same SDK’s documentation. (The two are not the same. Microsoft’s compiler interprets them as INITCOMMONCONTROLSEX const * and INITCOMMONCONTROLSEX * const, respectively.)