共用方式為


Control Panel Policy and the Missing Icon

A customer recently reported a rather peculiar problem.  They were working on a roll out of Windows 7, and one of the policies they employed on their domain was the “Show only specified control panel applets” setting.  As its name implies, this policy allows an administrator to specify the names of any Control Panel icons they want their users to see, and all others are not shown.  This company chose to allow a few of the Control Panel items included with Windows as well as the Mail icon that is added when Microsoft Office (specifically Outlook) is installed, which allows users to configure their Outlook profiles.

 

The problem the customer noticed was that, with the policy was in place, the first time a user opened the Control Panel on a computer, all of the allowed icons appeared except for Mail.  If the user closed Control Panel and reopened it, the icon suddenly appeared.  In fact, from that point on, the Mail icon would appear for that user on that computer.  However, if that user logged on to a different computer, or a different user logged in to the first user’s computer, the icon was missing until Control Panel was opened for a second time.

 

Figure 1: Policy setting on our test system

Figure 1: Policy setting on our test system

 

Figure 2: Result from first open

Figure 2: Result from first open

 

Figure 3: Result on subsequent opens (desired)

Figure 3: Result on subsequent opens (desired)

 

Control Panel isn’t something we get many cases on, so I had to figure out how it worked, and what would cause it to fail only the first time.  One of the first things I discovered as I debugged the issue was that the Control Panel is actually loaded in two passes – a “fast” pass and a “slow” pass.  This is done so icons we know will be loaded quickly can be displayed first so a user doesn’t get stuck with an empty window waiting for some of the slower icons to load.  What makes an icon fast or slow?  It turns out shell maintains a cache of Control Panel items, so it can display them quickly without having to query the actual item for its name, description, icon, etc.  If we have to ask the item for its information, that will take longer, so it is loaded during the second, slow phase:

 

shell32!CControlPanelDataWorkItem::_LoadSlowData

shell32!CControlPanelDataWorkItem::DoWork

shell32!CFrameTask::InternalResumeRT

shell32!CRunnableTask::Run

shell32!CShellTask::TT_Run

shell32!CShellTaskThread::ThreadProc

shell32!CShellTaskThread::s_ThreadProc

shlwapi!ExecuteWorkItemThreadProc

ntdll!RtlpTpWorkCallback

ntdll!TppWorkerThread

kernel32!BaseThreadInitThunk

ntdll!__RtlUserThreadStart

ntdll!_RtlUserThreadStart

 

Luckily for these icons, they won’t stay relegated to slow loading status forever.  During the slow loading phase, one of the things we do is add the item’s information to the Control Panel cache, so we can load it during the fast phase in the future.

 

shell32!CPLD_AddControlToReg

shell32!CControlPanelEnum::_NextNonCachedCpl

shell32!CControlPanelEnum::Next

shell32!CRegFolderEnum::Next

shell32!CControlPanelAppletList::_AddAppletsToCategories

shell32!CControlPanelAppletList::LoadSlowApplets

shell32!CControlPanelDataWorkItem::_LoadSlowData

shell32!CControlPanelDataWorkItem::DoWork

shell32!CFrameTask::InternalResumeRT

shell32!CRunnableTask::Run

shell32!CShellTask::TT_Run

shell32!CShellTaskThread::ThreadProc

shell32!CShellTaskThread::s_ThreadProc

shlwapi!ExecuteWorkItemThreadProc

ntdll!RtlpTpWorkCallback

ntdll!TppWorkerThread

kernel32!BaseThreadInitThunk

ntdll!__RtlUserThreadStart

ntdll!_RtlUserThreadStart

 

This explains why the item was being loaded on subsequent opens of Control Panel – the item was added to the user’s cache on the first run, so the next time, it was loaded during the fast pass:

shell32!CControlPanelEnum::_NextCachedCpl

shell32!CControlPanelEnum::Next

shell32!CRegFolderEnum::Next
shell32!CControlPanelAppletList::_AddAppletsToCategories

shell32!CControlPanelAppletList::EnsureLoaded

shell32!CControlPanelDataWorkItem::_LoadFastData

shell32!CControlPanelDataWorkItem::DoWork

shell32!CFrameTask::InternalResumeRT

shell32!CRunnableTask::Run

shell32!CShellTask::TT_Run

shell32!CShellTaskThread::ThreadProc

shell32!CShellTaskThread::s_ThreadProc

shlwapi!ExecuteWorkItemThreadProc

ntdll!RtlpTpWorkCallback

ntdll!TppWorkerThread

kernel32!BaseThreadInitThunk

ntdll!__RtlUserThreadStart

ntdll!_RtlUserThreadStart

 

But that only answers half the question – while we now know why the icon appears every other time, we still don’t know why it doesn’t show up the first time.  To figure this out, I debugged a system with the “Show only specified control panel applets” policy applied and one without the policy set, both as they opened Control Panel for the first time, and compared where the return codes from the functions diverged. 

 

Starting with CControlPanelEnum::_NextNonCachedCpl, in the non-working case, we can see that it returns 0:

0:003> bp shell32!CControlPanelEnum::_NextNonCachedCpl

0:003> g

Breakpoint 0 hit

eax=14c5fa8c ebx=14c5fa7c ecx=11224630 edx=00000000 esi=11224630 edi=14c5fa8c

eip=76977e11 esp=14c5f9b4 ebp=14c5f9c8 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

shell32!CControlPanelEnum::_NextNonCachedCpl:

76977e11 8bff            mov     edi,edi

0:020> k

ChildEBP RetAddr 

14c5f9b0 76977dff shell32!CControlPanelEnum::_NextNonCachedCpl

14c5f9c8 768a854d shell32!CControlPanelEnum::Next+0x5a

14c5fa18 769710d2 shell32!CRegFolderEnum::Next+0x22e

14c5fa94 769783e7 shell32!CControlPanelAppletList::_AddAppletsToCategories+0x35

14c5fac4 76978353 shell32!CControlPanelAppletList::LoadSlowApplets+0x68

14c5faec 76974db4 shell32!CControlPanelDataWorkItem::_LoadSlowData+0x2e

14c5fb00 769e06ad shell32!CControlPanelDataWorkItem::DoWork+0x4b

14c5fb18 768d83a6 shell32!CFrameTask::InternalResumeRT+0x14

14c5fb38 7691639b shell32!CRunnableTask::Run+0xce

14c5fb54 76918c1f shell32!CShellTask::TT_Run+0x167

14c5fb9c 76918d53 shell32!CShellTaskThread::ThreadProc+0xa3

14c5fba4 760cb2b1 shell32!CShellTaskThread::s_ThreadProc+0x1b

14c5fbb4 77c8d877 shlwapi!ExecuteWorkItemThreadProc+0xe

14c5fc28 77c90842 ntdll!RtlpTpWorkCallback+0x11d

14c5fd80 00000000 ntdll!TppWorkerThread+0x572

0:020> gu  <-- This steps out to the instruction after the call to CControlPanelEnum::_NextNonCachedCpl in CControlPanelEnum::Next.

ModLoad: 35c70000 35c8b000   c:\progra~1\common~1\system\msmapi\1033\mlcfg32.cpl

eax=00000000 ebx=14c5fa7c ecx=bea347f1 edx=006d2904 esi=11224630 edi=14c5fa8c

eip=76977dff esp=14c5f9bc ebp=14c5f9c8 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

shell32!CControlPanelEnum::Next+0x5a:

76977dff 84c0            test    al,al

 

But on the machine without the policy set, it returns 1:

eax=0c83fc34 ebx=0c83fc24 ecx=04ab1060 edx=00000000 esi=04ab1060 edi=0c83fc34

eip=76977e11 esp=0c83fb5c ebp=0c83fb70 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

shell32!CControlPanelEnum::_NextNonCachedCpl:

76977e11 8bff            mov     edi,edi

0:012> k

ChildEBP RetAddr 

0c83fb58 76977dff shell32!CControlPanelEnum::_NextNonCachedCpl

0c83fb70 768a854d shell32!CControlPanelEnum::Next+0x5a

0c83fbc0 769710d2 shell32!CRegFolderEnum::Next+0x22e

0c83fc3c 769783e7 shell32!CControlPanelAppletList::_AddAppletsToCategories+0x35

0c83fc6c 76978353 shell32!CControlPanelAppletList::LoadSlowApplets+0x68

0c83fc94 76974db4 shell32!CControlPanelDataWorkItem::_LoadSlowData+0x2e

0c83fca8 769e06ad shell32!CControlPanelDataWorkItem::DoWork+0x4b

0c83fcc0 768d83a6 shell32!CFrameTask::InternalResumeRT+0x14

0c83fce0 7691639b shell32!CRunnableTask::Run+0xce

0c83fcfc 76918c1f shell32!CShellTask::TT_Run+0x167

0c83fd44 76918d53 shell32!CShellTaskThread::ThreadProc+0xa3

0c83fd4c 760cb2b1 shell32!CShellTaskThread::s_ThreadProc+0x1b

0c83fd5c 77c8d877 shlwapi!ExecuteWorkItemThreadProc+0xe

0c83fdd0 77c90842 ntdll!RtlpTpWorkCallback+0x11d

0c83ff30 778b3c45 ntdll!TppWorkerThread+0x572

0c83ff3c 77cc37f5 kernel32!BaseThreadInitThunk+0xe

0c83ff7c 77cc37c8 ntdll!__RtlUserThreadStart+0x70

0c83ff94 00000000 ntdll!_RtlUserThreadStart+0x1b

0:012> gu

ModLoad: 35c70000 35c8b000   c:\progra~1\common~1\system\msmapi\1033\mlcfg32.cpl

eax=00000001 ebx=0c83fc24 ecx=bea347f1 edx=00002bba esi=04ab1060 edi=0c83fc34

eip=76977dff esp=0c83fb64 ebp=0c83fb70 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

shell32!CControlPanelEnum::Next+0x5a:

76977dff 84c0            test    al,al

 

So we know something in this function is causing us to return FALSE in the failing case and TRUE in the working case.  An easy way to see where the return result of function calls is the wt (watch trace) debugger command with the -oR switch to see the return values.  I set the level (-l) to 1 so I’d only see functions directly called from CControlPanelEnum::_NextNonCachedCpl.  Here were the results.

 

Failing case:

 

eax=14c5fa8c ebx=14c5fa7c ecx=11224630 edx=00000000 esi=11224630 edi=14c5fa8c

eip=76977e11 esp=14c5f9b4 ebp=14c5f9c8 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

shell32!CControlPanelEnum::_NextNonCachedCpl:

76977e11 8bff            mov     edi,edi

0:020> wt -oR -l 1

Tracing shell32!CControlPanelEnum::_NextNonCachedCpl to return address 76977dff

   31     0 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   10     0 [  1]   shell32!DSA_GetItemPtr

   17     0 [  1]   comctl32!DSA_GetItemPtr eax = 1123aa78

   48    27 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

ModLoad: 35c70000 35c8b000   c:\progra~1\common~1\system\msmapi\1033\mlcfg32.cpl

   34     0 [  1]   shell32!CPLD_InitModule eax = 1

   55    61 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   15     0 [  1]   shell32!CDPA_Base<RESULT_FOLDER_ITEM,CTContainer_PolicyUnOwned<RESULT_FOLDER_ITEM> >::AppendPtr eax = 0

   59    76 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   14     0 [  1]   shell32!CPL_AddModuleReference eax = 0

   61    90 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   21     0 [  1]   shell32!CPL_ReleaseModuleReference eax = 0

   76   111 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

  128     0 [  1]   shell32!CPLD_AddControlToReg eax = 1

   78   239 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   12     0 [  1]   shell32!FindCPLModuleInList eax = c666998

   84   251 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   10     0 [  1]   shell32!DSA_GetItemPtr

   17     0 [  1]   comctl32!DSA_GetItemPtr eax = c5e7410

   91   278 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   90 0 [ 1] shell32!CControlPanelEnum::_CanEnumerateApplet eax = 0

   97   368 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   21     0 [  1]   shell32!CPL_ReleaseModuleReference eax = 0

  108   389 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

  143     0 [  1]   shell32!CPLD_FlushRegModules eax = 0

  116   532 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

    3     0 [  1]   shell32!__security_check_cookie eax = 0

  118   535 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

 

Working case:

 

eax=0c83fc34 ebx=0c83fc24 ecx=04ab1060 edx=00000000 esi=04ab1060 edi=0c83fc34

eip=76977e11 esp=0c83fb5c ebp=0c83fb70 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

shell32!CControlPanelEnum::_NextNonCachedCpl:

76977e11 8bff            mov     edi,edi

0:012> wt -oR -l 1

Tracing shell32!CControlPanelEnum::_NextNonCachedCpl to return address 76977dff

   31     0 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   10     0 [  1]   shell32!DSA_GetItemPtr

   17     0 [  1]   comctl32!DSA_GetItemPtr eax = 4a732c8

   48    27 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

ModLoad: 35c70000 35c8b000   c:\progra~1\common~1\system\msmapi\1033\mlcfg32.cpl

   34     0 [  1]   shell32!CPLD_InitModule eax = 1

   55    61 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   15     0 [  1]   shell32!CDPA_Base<RESULT_FOLDER_ITEM,CTContainer_PolicyUnOwned<RESULT_FOLDER_ITEM> >::AppendPtr eax = 0

   59    76 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   14     0 [  1]   shell32!CPL_AddModuleReference eax = 0

   61    90 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   21     0 [  1]   shell32!CPL_ReleaseModuleReference eax = 0

   76   111 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

  128     0 [  1]   shell32!CPLD_AddControlToReg eax = 1

   78   239 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   12     0 [  1]   shell32!FindCPLModuleInList eax = 2ff3968

   84   251 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   10     0 [  1]   shell32!DSA_GetItemPtr

   17     0 [  1]   comctl32!DSA_GetItemPtr eax = 4aa81a8

   91   278 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

  111 0 [ 1] shell32!CControlPanelEnum::_CanEnumerateApplet eax = 1

  104   389 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

   21     0 [  1]   shell32!CPL_ReleaseModuleReference eax = 0

  112   410 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

    3     0 [  1]   shell32!__security_check_cookie eax = 1

  114   413 [  0] shell32!CControlPanelEnum::_NextNonCachedCpl

 

Here, we can see that the function CControlPanelEnum::_CanEnumerateApplet returns true in the working case, but false in the failing case.  This is what is causing the return value from _NextNonCachedCpl to differ, which is ultimately what causes the icon to load/not load.  So let’s take a look at _CanEnumerateApplet.  Using wt inside this function showed something rather interesting:

 

Failing:

 

eax=0c5e7410 ebx=769298fd ecx=11224630 edx=00000000 esi=11224630 edi=0c666998

eip=7693a72f esp=14c5f980 ebp=14c5f9b0 iopl=0         nv up ei pl nz na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202

shell32!CControlPanelEnum::_CanEnumerateApplet:

7693a72f 8bff            mov     edi,edi

0:020> wt -oR -l 1

Tracing shell32!CControlPanelEnum::_CanEnumerateApplet to return address 7693a68e

   27     0 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

  737     0 [  1]   shlwapi!PathFindFileNameW eax = c6669f8

   40   737 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

   59     0 [  1]   shell32!IDControlCreate eax = 0

   44   796 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

   43     0 [  1]   shlwapi!SHWindowsPolicy eax = 1

   53   839 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

    2     0 [  1]   shell32!CRegFolder::QueryInterface

    5     0 [  1]   shell32!CKnownFoldersFolder::QueryInterface

   12     0 [  1]   shell32!CAggregatedUnknown::QueryInterface eax = 0

   71   858 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

   48 0 [ 1] shell32!CRegFolder::GetDetailsEx eax = ffffffff`80070002

   77 906 [ 0] shell32!CControlPanelEnum::_CanEnumerateApplet

   24 0 [ 1] shell32!CRegFolder::Release eax = 3

   79   930 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

    6     0 [  1]   shell32!ATL::CComVariant::Clear eax = 0

   88   936 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

    3     0 [  1]   shell32!__security_check_cookie eax = 0

   90   939 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

 

Working:

 

0:012> wt -oR -l 1

Tracing shell32!CControlPanelEnum::_CanEnumerateApplet to return address 7693a68e

   27     0 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

  737     0 [  1]   shlwapi!PathFindFileNameW eax = 2ff39c8

   40   737 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

   59     0 [  1]   shell32!IDControlCreate eax = 0

   44   796 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

   43     0 [  1]   shlwapi!SHWindowsPolicy eax = 1

   53   839 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

    2     0 [  1]   shell32!CRegFolder::QueryInterface

    5     0 [  1]   shell32!CKnownFoldersFolder::QueryInterface

   12     0 [  1]   shell32!CAggregatedUnknown::QueryInterface eax = 0

   71   858 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

   48 0 [ 1] shell32!CRegFolder::GetDetailsEx eax = 0

   77 906 [ 0] shell32!CControlPanelEnum::_CanEnumerateApplet

   28 0 [ 1] shell32!CPL_DoesPolicyAllow eax = 1

   82 934 [ 0] shell32!CControlPanelEnum::_CanEnumerateApplet

   24 0 [ 1] shell32!CRegFolder::Release eax = 3

   84   958 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

    6     0 [  1]   shell32!ATL::CComVariant::Clear eax = 0

   98   964 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

  358     0 [  1]   shell32!CControlPanelFolder::GetCustomAttributes eax = 0

  109  1322 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

    3     0 [  1]   shell32!__security_check_cookie eax = 1

  111  1325 [  0] shell32!CControlPanelEnum::_CanEnumerateApplet

 

Looking at the two, we can see that they both call GetDetailsEx, but in the failing case we seem to get an error result: 80070002, and in the working case we get 0, and then proceed to call CPL_DoesPolicyAllow.  CPL_DoesPolicyAllow is not called on the machine with the policy applied.  A quick code review of CPL_DoesPolicyAllow showed that this function checks if a policy prevents or allows an item from being displayed.  So if we can just get to that function, the icon will load.  Now we need to know why GetDetailsEx fails, preventing us from calling CPL_DoesPolicyAllow.   If we look up the error code, it is pretty generic:

 

0:020> !error 80070002

Error code: (HRESULT) 0x80070002 (2147942402) - The system cannot find the file specified.

 

Next I stepped through and into GetDetailsEx.  I’ll save you the tedious steps and output, since the procedure is similar to what I’ve already shown above.  Anyway, I was able to trace the error 2 as being returned by a call to the registry a few functions deep into GetDetailsEx:

 

kernel32!RegOpenKeyExW

shell32!CControlPanelFolder::_GetExtPropRegKey

shell32!CControlPanelFolder::_InitExtPropRegValNameCache

shell32!CControlPanelFolder::_PropertyFromPidl

shell32!CControlPanelFolder::GetDetailsEx

shell32!CRegFolder::GetDetailsEx

shell32!CControlPanelEnum::_CanEnumerateApplet

shell32!CControlPanelEnum::_NextNonCachedCpl

 

This is good news, because it means we can find out what we’re looking for that we aren’t finding.  First I stepped out to get back to CControlPanelFolder::_InitExtPropRegValNameCache.  Sure enough, eax was 2, which is our ‘cannot find the file’ error.

 

0:020> gu

eax=00000002 ebx=00000000 ecx=778ac2da edx=00000002 esi=14c5eff0 edi=00000000

eip=76975ead esp=14c5ed40 ebp=14c5efc4 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

shell32!CControlPanelFolder::_GetExtPropRegKey+0xb1:

76975ead 50              push    eax

0:020> !error 2

Error code: (Win32) 0x2 (2) - The system cannot find the file specified

 

Since we know that RegOpenKeyEx takes the subkey it is looking for as the second parameter, let’s look back at the assembly before the call to see what we passed in:

 

0:020> ub @eip

shell32!CControlPanelFolder::_GetExtPropRegKey+0x98:

76975e94 7c1f            jl      shell32!CControlPanelFolder::_GetExtPropRegKey+0xb9 (76975eb5)

76975e96 56              push    esi

76975e97 6a01            push    1

76975e99 53              push    ebx

76975e9a 8d8590fdffff    lea     eax,[ebp-270h]

76975ea0 50              push    eax

76975ea1 ffb588fdffff    push    dword ptr [ebp-278h]

76975ea7 ff15a0128776    call    dword ptr [shell32!_imp__RegOpenKeyExW (768712a0)]

 

Remember that function parameters are passed in in reverse order, so the second parameter is the second to last value passed to the function.  In this case, we can see that it was stored in eax.  Of course, eax has been overwritten by our return value of 2, but we can see that just before pushing eax, we get the value from ebp-0x270.  Dumping that out as a Unicode string, we get the key we were looking for:

 

0:020> du @ebp-270h

14c5ed54  "Software\Microsoft\Windows\Curre"

14c5ed94  "ntVersion\Control Panel\Extended"

14c5edd4  " Properties\System.ApplicationNa"

14c5ee14  "me"

 

The first parameter is the root key, which was:

 

0:020> dc @ebp-278h L1

14c5ed4c  80000002 

 

We can look up this value in the Windows SDK header files and see that it is for HKLM:

#define HKEY_LOCAL_MACHINE                  (( HKEY ) (ULONG_PTR)((LONG)0x80000002) )

 

Aha!  So now we know we were trying to access “HKLM\ Software\Microsoft\Windows\CurrentVersion\Control Panel\Extended

 Properties\System.ApplicationName” which didn’t exist, so we were unable to proceed with loading the icon. 

 

What’s that key for, you ask?  MSDN tells us that it is used to store the Canonical Name of the Control Panel item.  Canonical Names were added to the Control Panel in Windows Vista as a better way to organize and name the items in the Control Panel.  By convention, the canonical name of “Mail” is “Microsoft.Office.Outlook.Profiles.”  Because the “Show only specified control panel applets” policy can now accept either the legacy name (“Mail”) or the canonical name, we are checking the registry for this string.  Since the key is not present, GetDetailsEx cannot determine the canonical name, and the icon does not load.

 

As a test, I created the key HKLM\ Software\Microsoft\Windows\CurrentVersion\Control Panel\Extended

 Properties\System.ApplicationName and added a REG_SZ to it with a name of the file location of the Mail CPL file ("C:\Program Files\Common Files\SYSTEM\MSMAPI\1033\MLCFG32.CPL") and a value of "Microsoft.Office.Outlook.Profiles".  Sure enough, the icon loads up on the first try for new users.

 

While new Control Panel items should implement a canonical name, that doesn’t work for existing CPLs, like Mail from Outlook 2003.  Since the canonical name isn’t actually required (CPL_DoesPolicyAllow works with both name formats), we’re following up with the development team to allow CPL_DoesPolicyAllow to be called even if a canonical name is not found.

 

-Matt Burrough

Comments

  • Anonymous
    April 22, 2011
    Excellent! Thanks for the great tip for using "wt" to show return values!