How can you use both versions 5 and 6 of the common controls within the same module?


Commenter Medinoc was baffled by the statement that the decision to use the visual-styles-enabled version of the common controls library is done on a window-by-window basis. " Isn't it rather on a per-module basis, depending on each module's manifest? If it is indeed on a per-window basis, how does one choose?"

Whether a particular call to Create­Window (or one of its moral equivalents) gets the classic version of the control or the visual-styles-enabled version of the control depends on which activation context is active at the point of the call. If an activation context with version 6 of the common controls is active, then you get the control from version 6 of the common controls. Otherwise, you get the classic control.

If you use the ISOLATION_AWARE_ENABLED macro, then including commctrl.h turns on a bunch of macros that take all your calls to Create­Window and related functions, and converts them into something like this:

HWND CreateWindow_wrapped(... parameters ...)
{
 HWND hwnd = nullptr;
 ULONG_PTR ulCookie;
 if (ActivateActCtx(ModuleContext, &ulCookie)) {
  hwnd = CreateWindow(... parameters ...);
 DeactivateActCtx(0, ulCookie);
 }
 return hwnd;
}

where Module­Context is a global variable that holds the activation context you specified in your manifest.

In other words, any time your code tries to create a window, the wrapper macros activate your v6 manifest, create the window, then deactivate the manifest.

Remember that nobody walks the stack looking to see who the caller is. The return address is not reliable. (And checking the return address doesn't help for dynamically-generated code anyway.) The way to know which activation context is active is for somebody to actually come out and set it.

Back to the question: The way you choose whether you want a classic or a visual-styles-enabled version of a control is by deciding whether or not to have the v6 manifest active when you call Create­Window.

A common mistake is that people will call a function that requires a v6 manifest, such as Task­Dialog, but they will forget to activate the v6 manifest before calling. The result is that they call into version 6 of the common controls, but when the common controls library tries to create its task dialog, it fails because the v5 context is active, and the v5 context does not have a task dialog control.

Comments (9)
  1. Daniel says:

    While technically doable, I fail to see any real use case of this…

    (Maybe when "extending" an existing V5 application with nice features like TaskDialog without breaking the current look&feel?)

    I personally never encountered a situation where this is required…

  2. Roger Lipscombe says:

    @Daniel — this is useful when you've got a DLL that wants v6 controls loaded into a process that's using v5 controls. Personally, I've never needed it either, but I suspect that Windows Explorer extensions use it all the time (or should do — I suspect Raymond has some horror stories there, too).

  3. Myria says:

    Does LoadLibraryExW automatically activate the activation context of the new library for the duration of the LoadLibraryExW call?  I'm curious because when I was debugging why some iTunes installations corrupted themselves during updating earlier this year, I noticed that it was failing because msvcr90.dll was rejecting its activation context.  The EXE had no manifest; it LoadLibrary'd a DLL with a manifest that depended on msvcr90.dll, but msvcr90.dll's call to FindActCtxSectionStringW during msvcr90.dll's DllMain(DLL_PROCESS_ATTACH) failed.

  4. Rick C says:

    I know the linked article says it's per-window, but this post seems to imply you can have both a V5 and v6 version of the same control in the same window:  create one button, activate your context, create a second one, deactivate the context.

    Normally I'd test this out but I don't know anything about activation contexts and don't have the time this week to do all the research necessary.

    [It is per-window. The outer window, its first child window, and its second child window can choose independently which version they are. -Raymond]
  5. Daniel says:

    @Rick C: it's still "per window": Each button is it's own window after all. (There's nothing here to say that CHILD-Windows need to use the same version of the common controls)

  6. Koro says:

    Myria: If the loaded DLL's manifest is resource ID 2, it gets activated for the duration of the call to DllMain.

  7. John Doe says:

    @Myria, I've used LoadLibrary with multiple DLLs statically linked to msvcr80.dll with the manifest in the RT_MANIFEST ISOLATIONAWARE_MANIFEST_RESOURCE_ID resource of each DLL from an application without manifests and no use of the activation context API.

    I don't know about LoadLibraryEx, but LoadLibrary surely does activate the DLL's context, so msvcr80's DllMain doesn't fail, which is very similar to msvcr90's DllMain regarding the context check.

    Side rant:

    The maninfested runtimes were supposed to resolve the DLL hell, where every VC++ version had a msvcrt.dll.  However, either the name change or the manifest would suffice.  With the introduction and check for the manifest came the manifest hell.  I guess this was fashion when comctl32 became side-by-side, but that DLL didn't change name.

    For instance, which version msvcr90.dll are you talking about?

    9.0.21022.8

    9.0.30729.1

    9.0.30729.4137

    9.0.30729.4148

    9.0.30729.4926

    9.0.30729.4940

    9.0.30729.4974

    I know about redirection, it's done at system level when installing a newer redistributable package, but it's one more thing you have to know and worry about if you're (really) trying to isolate an application.  I'm not saying you should isolate every app, but sometimes you must isolate one app or two (e.g. services, particularly distributed ones).

    In newer versions, they resolved both the DLL hell and the manifest hell by just having a different name and not requiring or checking for a manifest.  Export symbols don't change between 10.0.x and 10.0.y or 10.0.z (e.g. msvcr110.dll), so not a big deal.

  8. Marc K says:

    It was very frustrating the first time I encountered the issue of version 5 of the library loading when I wanted version 6.  I can't think of how this could have been implemented with more complexity and potential to confuse.

  9. Oliver_F says:

    Thank you very much for this post!

    Our use case is exactly as Daniel above speculated: We are writing an MSAccess application in corporate environment. An while we cannot change the msaccess.exe.manifest to use CommonControls6, I can now write my own little manifest, switch to it using the ActivationContext API, call maybe the TaskDialog and clean up afterwards.

    As the same techniques basically applies to using "registration-free COM", I will probably be able to call into some additional COM objects without having to write to the registry, which I can't do as normal user either.

Comments are closed.