Control Panel Policy and the Missing Icon

This is an archive post of content I wrote for the NTDebugging Blog on MSDN. Since the MSDN blogs are being retired, I’m transferring my posts here so they aren’t lost. The post has been back-dated to its original publication date.

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 [d:\w7rtm\minkernel\threadpool\compat\worker.c @ 284]
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