5 hours ago
I think there is a common misconception here about activation contexts and visual styles.
The activation context does not need to remain active for the entire lifetime of the application in order for visual styles to keep working.
What actually matters is that the activation context is active when Windows loads and initializes the version 6 Common Controls (ComCtl32.dll). Once the themed controls have been created, deactivating the activation context does not suddenly "turn off" visual styles for controls that already exist.
In other words, the activation context is used to tell the Side-by-Side loader which version of the Common Controls library should be used. It is not a permanent switch that must stay enabled as long as the application is running.
For that reason, my implementation in FreeBasic simply:
- Creates the activation context.
- Activates it.
- Lets Windows load the themed Common Controls v6.
- Immediately deactivates and releases the activation context.
After that, the controls continue to use visual styles normally.
Keeping the activation context active for the entire process is only necessary if you intend to create additional objects later that must also be created under that same activation context. For the simple goal of enabling themed Common Controls, activating it temporarily is sufficient.
This is why the following FreeBasic code activates the context and immediately deactivates/releases it:
ActivateActCtx(hActCtx, @activationCookie)
DeactivateActCtx(0, activationCookie)
ReleaseActCtx(hActCtx)
and the application still gets visual styles correctly.
Conceptually, it is very similar to having an external manifest: the manifest influences how the process is initialized and which assemblies are selected, but Windows does not continuously consult it afterwards to decide whether already-created controls should remain themed.
The important point is that visual styles are a property of the Common Controls v6 implementation that gets loaded, not of the current activation state of the activation context stack after the controls have already been created.
This is the function that I use in FreeBasic:
The activation context does not need to remain active for the entire lifetime of the application in order for visual styles to keep working.
What actually matters is that the activation context is active when Windows loads and initializes the version 6 Common Controls (ComCtl32.dll). Once the themed controls have been created, deactivating the activation context does not suddenly "turn off" visual styles for controls that already exist.
In other words, the activation context is used to tell the Side-by-Side loader which version of the Common Controls library should be used. It is not a permanent switch that must stay enabled as long as the application is running.
For that reason, my implementation in FreeBasic simply:
- Creates the activation context.
- Activates it.
- Lets Windows load the themed Common Controls v6.
- Immediately deactivates and releases the activation context.
After that, the controls continue to use visual styles normally.
Keeping the activation context active for the entire process is only necessary if you intend to create additional objects later that must also be created under that same activation context. For the simple goal of enabling themed Common Controls, activating it temporarily is sufficient.
This is why the following FreeBasic code activates the context and immediately deactivates/releases it:
ActivateActCtx(hActCtx, @activationCookie)
DeactivateActCtx(0, activationCookie)
ReleaseActCtx(hActCtx)
and the application still gets visual styles correctly.
Conceptually, it is very similar to having an external manifest: the manifest influences how the process is initialized and which assemblies are selected, but Windows does not continuously consult it afterwards to decide whether already-created controls should remain themed.
The important point is that visual styles are a property of the Common Controls v6 implementation that gets loaded, not of the current activation state of the activation context stack after the controls have already been created.
This is the function that I use in FreeBasic:
Code:
' ========================================================================================
' * Enables visual styles without using a manifest in the application.
' ========================================================================================
PUBLIC FUNCTION EnableVisualStyles () AS BOOLEAN
' // Fill the ACTCTXW structure
DIM actCtx AS ACTCTXW
actCtx.cbSize = SIZEOF(ACTCTXW)
actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID OR ACTCTX_FLAG_SET_PROCESS_DEFAULT OR ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID
' // Get system directory
DIM systemDir AS WSTRING * MAX_PATH
DIM cch AS UINT = GetSystemDirectoryW(@systemDir, MAX_PATH)
IF cch >= MAX_PATH THEN RETURN FALSE ' Ensure buffer is not exceeded
systemDir[cch] = ASC("\") ' Append backslash
actCtx.lpAssemblyDirectory = @systemDir
DIM wszSource AS WSTRING * MAX_PATH = systemDir & "shell32.dll"
actCtx.lpSource = CAST(WSTRING PTR, @wszSource) ' Use shell32.dll's manifest
actCtx.lpResourceName = CAST(WSTRING PTR, 124) ' Resource ID for the manifest (IDR_MANIFEST = 1, RT_MANIFEST = 24)
' // Create the activation context
DIM hActCtx AS HANDLE = CreateActCtxW(@actCtx)
IF hActCtx = INVALID_HANDLE_VALUE THEN RETURN FALSE
' // Activate the context
DIM activationCookie AS ULONG_PTR
ActivateActCtx(hActCtx, @activationCookie)
' // Deactivate and release the context
DeactivateActCtx(0, activationCookie)
ReleaseActCtx(hActCtx)
RETURN TRUE
END FUNCTION
' ========================================================================================
