Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to force XP look in feel in shell extensions

288 views
Skip to first unread message

Matt Attaway

unread,
Mar 9, 2004, 7:56:06 PM3/9/04
to
Hello all,

I'm working on shell extension which uses ATL for the dialogs.
( I was using WTL, but I've gone back to simple ATL dialogs for now. )
An interesting factoid about shell extensions is that if they are invoked
from Explorer the resulting dialogs to not pick up the XP look and feel. If they
are called from Windows open dialog they do pick up the "Luna" look. A bit odd, but that's life. =)

I would like to force my dialog to have the XP look if appropriate. I've
heard mutterings on the Internet about having to set the "context." However that
appears to be an MFC concept.

My questions are, has anyone heard of this "context?" If so, is there a
way with ATL to set the context or should I look into using MFC instead?

And yup, I'm following all of the proper guidelines for picking up the XP
look. =)

Thanks,
Matt

David Lowndes

unread,
Mar 10, 2004, 5:14:41 AM3/10/04
to
>I'm working on shell extension which uses ATL for the dialogs.
>( I was using WTL, but I've gone back to simple ATL dialogs for now. )
>An interesting factoid about shell extensions is that if they are invoked
>from Explorer the resulting dialogs to not pick up the XP look and feel. If they
>are called from Windows open dialog they do pick up the "Luna" look. A bit odd, but that's life. =)

Matt,

This sounds similar to the situation I experienced with an MFC based
shell extension.

I don't really understand the fix I was given from MS, but this is the
essence of it. I don't know if it'll work (with a bit of alteration)
in your ATL situation.

class CMyContext
{
public:
BOOL Init()
{
BOOL bRet = FALSE;
BOOL bTemp = FALSE;

OSVERSIONINFO info = { 0 };
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

bTemp = GetVersionEx(&info);
if (bTemp)
{
//do special XP theme activation code only on XP
or higher...
if ( (info.dwMajorVersion >= 5) &&
(info.dwMinorVersion >= 1) &&
(info.dwPlatformId == VER_PLATFORM_WIN32_NT))
{
ACTCTX actctx = {0};
TCHAR szModule[MAX_PATH] = {0};

HINSTANCE hinst = AfxGetInstanceHandle();
::GetModuleFileName(hinst, szModule, MAX_PATH);

actctx.cbSize = sizeof(ACTCTX);
actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID |
ACTCTX_FLAG_RESOURCE_NAME_VALID;

actctx.lpSource = szModule;
actctx.lpResourceName = MAKEINTRESOURCE(2);
actctx.hModule = hinst;

m_hActCtx = ::CreateActCtx(&actctx);
if (INVALID_HANDLE_VALUE != m_hActCtx)
{
bRet = TRUE;
}
}
}

return bRet;
}

CMyContext() : m_ulActivationCookie(0), m_hActCtx(0)
{
BOOL bRet = Init();

if (bRet && m_hActCtx && (INVALID_HANDLE_VALUE !=
m_hActCtx))
ActivateActCtx(m_hActCtx, &m_ulActivationCookie);
}

~CMyContext()
{
if (m_hActCtx && (m_hActCtx!= INVALID_HANDLE_VALUE))
{
DeactivateActCtx(0, m_ulActivationCookie);
ReleaseActCtx(m_hActCtx);
}
}

private:
ULONG_PTR m_ulActivationCookie;
HANDLE m_hActCtx;
};


... and before you call DoModal to display your dialog in your shell
extension, create an instance of that class to run the code in that
class:

CMyContext con;
dlg.DoModal();

Dave
--
MVP VC++ FAQ: http://www.mvps.org/vcfaq

Henk Devos

unread,
Mar 11, 2004, 6:31:46 AM3/11/04
to
The ActCtx functions only exist in XP, so if you link to them statically
your DLL can't get loaded in older Windows versions. So if you want it to
run on older Windows versions, you should use dynamic linking. If you don't
the version check is wrong.
You also make a classical mistake in the version checking: Version 6.0 is
higher than 5.1.

"David Lowndes" <dav...@example.invalid> wrote in message
news:rcqt40159b0i6lj15...@4ax.com...


> >I'm working on shell extension which uses ATL for the dialogs.
> >( I was using WTL, but I've gone back to simple ATL dialogs for now. )
> >An interesting factoid about shell extensions is that if they are invoked
> >from Explorer the resulting dialogs to not pick up the XP look and feel.
If they
> >are called from Windows open dialog they do pick up the "Luna" look. A
bit odd, but that's life. =)
>
> Matt,
>
> This sounds similar to the situation I experienced with an MFC based
> shell extension.
>
> I don't really understand the fix I was given from MS, but this is the
> essence of it. I don't know if it'll work (with a bit of alteration)
> in your ATL situation.
>
> class CMyContext
> {
> public:
> BOOL Init()
> {
> BOOL bRet = FALSE;
> BOOL bTemp = FALSE;
>
> OSVERSIONINFO info = { 0 };
> info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
>
> bTemp = GetVersionEx(&info);
> if (bTemp)
> {

> file://do special XP theme activation code only on XP

David Lowndes

unread,
Mar 11, 2004, 12:32:19 PM3/11/04
to
>The ActCtx functions only exist in XP, so if you link to them statically
>your DLL can't get loaded in older Windows versions. So if you want it to
>run on older Windows versions, you should use dynamic linking.

Hmm, my program does work on Win9x systems, so now you've said this.
I'll have to check why - I may have it delayloaded, I can't remember.

>You also make a classical mistake in the version checking: Version 6.0 is
>higher than 5.1.

My fault for not checking the code MS gave me - unless they know
something we don't with regards to Windows 6! :)

David Lowndes

unread,
Mar 11, 2004, 2:26:03 PM3/11/04
to
>>The ActCtx functions only exist in XP, so if you link to them statically
>>your DLL can't get loaded in older Windows versions. So if you want it to
>>run on older Windows versions, you should use dynamic linking.
>Hmm, my program does work on Win9x systems, so now you've said this.
>I'll have to check why - I may have it delayloaded, I can't remember.

In fact those functions are in kernel32 which can't be delayloaded, so
I don't know how this works with older OS's - there must be something
going on that I'm not aware of and hadn't thought to consider since it
hadn't shown any problems with older OS's.

Looking at my DLL with depends doesn't indicate that the AxtCtx
functions are referenced.

Dave Anderson [MS]

unread,
Mar 12, 2004, 7:05:13 PM3/12/04
to
I suspect that you are building with ISOLATION_AWARE_ENABLED defined to 1.
ISOLATION_AWARE_ENABLED enables wrapper functions in winbase.inl (and other
files, such an Winuser.inl) for the activation context functions. These
wrappers essentially use the function address returned by calling
GetProcAddress on Kernel32 to call the specified function. This would allow
you to call CreateActCtx, etc. and still run on pre-Windows XP systems.

For example, the function that wraps CreateActCtxW (from Winbase.inl):

ISOLATION_AWARE_INLINE HANDLE WINAPI IsolationAwareCreateActCtxW(PCACTCTXW
pActCtx)
{
HANDLE result = INVALID_HANDLE_VALUE;
typedef HANDLE (WINAPI* PFN)(PCACTCTXW pActCtx);
static PFN s_pfn;
if (s_pfn == NULL)
{
s_pfn =
(PFN)WinbaseIsolationAwarePrivatetRgCebPnQQeRff_xReaRYQP_QYY("CreateActCtxW"
);
if (s_pfn == NULL)
return result;
}
result = s_pfn(pActCtx);
return result;
}


ISOLATION_AWARE_ENABLED also enables wrappers for functions in USER32 (such
as CreateWindowEx), COMCTL32 (such as PropertySheet), and COMDLG32 (such as
GetOpenFileName). These functions will activate the activation context
based on the module's manifest, call the specified function, then
deactivate the activation context. For example, the function that wraps
CreateWindowExW (from Winuser.inl):

ISOLATION_AWARE_INLINE HWND WINAPI IsolationAwareCreateWindowExW(DWORD
dwExStyle,LPCWSTR lpClassName,LPCWSTR lpWindowName,DWORD dwStyle,int X,int
Y,int nWidth,int nHeight,HWND hWndParent,HMENU hMenu,HINSTANCE
hInstance,LPVOID lpParam)
{
HWND windowResult = NULL;
ULONG_PTR ulpCookie = 0;
const BOOL fActivateActCtxSuccess = IsolationAwarePrivateT_SqbjaYRiRY
|| IsolationAwarePrivatenPgViNgRzlnPgpgk(&ulpCookie);
if (!fActivateActCtxSuccess)
return windowResult;
__try
{
windowResult =
CreateWindowExW(dwExStyle,lpClassName,lpWindowName,dwStyle,X,Y,nWidth,nHeigh
t,hWndParent,hMenu,hInstance,lpParam);
}
__finally
{
if (!IsolationAwarePrivateT_SqbjaYRiRY)
{
const BOOL fPreserveLastError = (windowResult == NULL);
const DWORD dwLastError = fPreserveLastError ? GetLastError() :
NO_ERROR;
(void)IsolationAwareDeactivateActCtx(0, ulpCookie);
if (fPreserveLastError)
SetLastError(dwLastError);
}
}
return windowResult;
}

What does ISOLATION_AWARE_ENABLED have to do with creating Window controls
from a shell extension? Explorer will load both the 5.82 and 6.0 versions
of COMCTL32.DLL when it starts. This allows for components that rely on
version 5.82 to continue to get their expected behavior while allowing
other components to use 6.0 version. COMCTL32 5.82 and 6.0 implement most
of the same window classes (SysListView32, SysTreeView32, etc.), while
version 6.0 also implements standard window controls that are found in
USER32 (Button, Edit, ComboBox, etc.) How does Windows know which version
to use when calling CreateWindowEx? This is where the activation context
comes into play. Windows allows for side-by-side window classes and the
activation context determines which implementation of the window class to
use when a window of that class is created. See "Creating Side-By-Side
Window Classes" for more information.
<http://msdn.microsoft.com/library/en-us/sbscs/setup/creating_side-by-side_w
indows_classes.asp>

So why doesn't defining ISOLATION_AWARE_ENABLED work with shell extensions
that use MFC or ATL? The short answer is that the runtime libraries were
not built using ISOLATION_AWARE_ENABLED. Therefore, the runtime libraries
will not use the IsolationAware* wrappers when calling Win32 functions such
as CreateWindowEx. Consider that when you call Create on a CButton object,
you are calling into the MFC runtime to create the button, which eventually
calls CreateWindowEx without activating an activation context. The likely
outcome is that the Create call will create an instance of the Button class
that is implemented in USER32, even if you have a manifest in the calling
module that references COMCTL32 version 6.

Anyone that is creating a DLL that is loaded into a process and wants to
use the version 6 common controls will need to create and manage an
activation context. This applies to shell extensions, ActiveX controls, and
other types of add-ins, such as add-ins for Office applications. Defining
ISOLATION_AWARE_ENABLED generally can handle managing an activation context
for you without having to write any additional code, provided the
IsolationAware* functions are used to 'wrap' the underlying Win32 call with
an activation context. As we have found, ISOLATION_AWARE_ENABLED does not
wrap Win32 calls made by MFC/ATL. Therefore, it is up to a component that
is using MFC/ATL to manage its own activation context.

In the example given above, a DLL calling CButton::Create should call
ActivateActCtx to activate the activation context prior to the Create call
and then call DeactivateActCtx after the create call to deactivate the
activation context. The button will then be created using the version 6
COMCTL32 implementation of the Button class, assuming that the activation
context is created using a manifest that references COMCTL32 version 6.

For those interested in how this applies to the .NET Framework, the
Application.EnableVisualStyles instructs the Framework to create and manage
an activation context when creating windows.

I recommend reading the "Using the Activation Context API" section in the
Platform SDK for more information.
<http://msdn.microsoft.com/library/en-us/sbscs/setup/using_the_activation_co
ntext_api.asp>

Dave Anderson
Microsoft Developer Support

This posting is provided "AS IS" with no warranties, and confers no rights.

David Lowndes

unread,
Mar 13, 2004, 10:53:33 AM3/13/04
to
>I suspect that you are building with ISOLATION_AWARE_ENABLED defined to 1.

You're right, I am. Thanks for the explanation Dave, that's certainly
helped clarify what's going on and how it works.

There just aren't enough hours in the day to find out about all the
things I ought to have some insight into - at least not, and have any
semblance of a life too!

Cheers
Dave Lowndes

0 new messages