Under The Hood: Excel hung on SaveAs
I am sorry for being inactive for a long time, as a “come-back-gift” I am starting a few new sections on my blog, I am sure you’ll enjoy ’em all. This post marks the beginning of series named “Under The Hood” (yeah, sure … I am guilty for being inactive for a long time, and I will do a pleading filled post ASAP :) )
So, the case was, “Whenever you click on “Save As” in excel, it hangs. No dialog box pops up”
My initial troubleshooting was based on the fact that more then likely it’s caused because of an ill behaving addin. I disabled all the addins, xla’s etc but, guess what! Still he could reproduce the issue.
At this point I was stuck and needed some more clues desperately .. so I reviewed every point in the case again and I do see a few interesting things ..
1) Initially issues was exhibiting only on a few boxes, but now it’s on a few hundred boxes.
2) Issue is reproducible in excel as well as in word (Although, it’s not always reproducible in word …)
The first clue is something that might point to a danger – the issue is spreading! what could spread? either a planned deployment or a virus!! I kept my figures crossed that it’s not the latter …
The other clue points to a fact that the issue is in some common code base, the first-n-closest-to-office-common-code-base that I could think of is – mso.dll.
Now, to move further – customer helped me with a memory dump. Here is the stack
0:005> k100
ChildEBP RetAddr
0b87e458 77f6946c ole32!CoCreateInstance+0x32 <— Creating instance of something
0b87e480 7c9f1695 shlwapi!SHCoCreateInstanceAC+0x3a
0b87e864 7c9ef619 shell32!_SHCoCreateInstance+0x127
0b87e8a4 7ca025bc shell32!SHCoCreateInstance+0x40
0b87e8c0 7ca0253f shell32!DCA_CreateInstance+0x2c
0b87ee2c 7ca02379 shell32!CFSIconOverlayManager::_LoadIconOverlayIdentifiers+0xd7
0b87ee3c 7ca02319 shell32!CFSIconOverlayManager::_InitializeHdsaIconOverlays+0x33
0b87ee50 7c9ef3ac shell32!CFSIconOverlayManager::CreateInstance+0x3f
0b87ee64 7c9f29d2 shell32!CCF_CreateInstance+0x2b
0b87ee80 7c9f299c shell32!_CreateFromDllGetClassObject+0x2d
0b87ee9c 7c9f2961 shell32!_CreateFromShell+0x1b
0b87f27c 7c9ef619 shell32!_SHCoCreateInstance+0x4e
0b87f2bc 7ca022b7 shell32!SHCoCreateInstance+0x40
0b87f2dc 7c9f41f2 shell32!IconOverlayManagerInit+0x26 <— Icon Overlay Manager calls
0b87f2e8 7ca02864 shell32!GetIconOverlayManager+0x10
0b87f750 7ca02063 shell32!_ShellImageListInit+0x29b
0b87f794 7ca31618 shell32!FileIconInit+0x19b
0b87f7a0 7ca2d215 shell32!Shell_GetImageLists+0x14
0b87f7f0 7ca2b0cf shell32!_GetFileInfoSections+0xe4
0b87fc78 327403f0 shell32!SHGetFileInfoW+0x13a
WARNING: Stack unwind information not available. Following frames may be wrong.
0b87ff60 3274027b mso!Ordinal6579+0x2c5
0b87ff78 326172d1 mso!Ordinal6579+0x150 <— MSO Calls … most likely “save as”
0b87ffac 3261710f mso!Ordinal577+0x1bc
0b87ffb4 7c80b713 mso!Ordinal320+0x31
0b87ffec 00000000 kernel32!BaseThreadStart+0x37
Looking at this stack, I can tell a few things …
1) Initial mso callstack tells us that excel were doing something with mso, which lead to the call shell32!SHGetFileInfoW, and we know what it could be as we clicked on “Save As”.
2) Then we do something with IconOverlayManager, I don’t know why; yet ..
3) Then we go for creating an instance of something .. I don’t what what for; yet ..
Let’s find out the “don’t knows” ..
0:005> ub <— When ub Address is used, the disassembled range will be the eight or nine byte range ending with Address
ole32!CoCreateInstance+0x1e:
77500599 8945f4 mov dword ptr [ebp-0Ch],eax
7750059c 8d45f4 lea eax,[ebp-0Ch]
7750059f 50 push eax
775005a0 6a01 push 1
775005a2 6a00 push 0
775005a4 ff7510 push dword ptr [ebp+10h]
775005a7 ff750c push dword ptr [ebp+0Ch]
775005aa ff7508 push dword ptr [ebp+8] <— Pushing parameters on the stack
0:005> u <— When u Address is used, the disassembly begins at the current Address and extends eight instructions
ole32!CoCreateInstance+0x32:
775005ad e874ffffff call ole32!CoCreateInstanceEx (77500526) <— Call CoCreateInstanceEx
775005b2 8b4df8 mov ecx,dword ptr [ebp-8]
775005b5 890e mov dword ptr [esi],ecx
775005b7 5e pop esi
775005b8 c9 leave
775005b9 c21400 ret 14h
775005bc 90 nop
775005bd 90 nop
Looked up for the function definition of CoCreateInstanceEx;
HRESULT CoCreateInstanceEx(
REFCLSID rclsid, /*[in] CLSID of the object to be created.*/
IUnknown * punkOuter,
/*[in] When non-NULL, indicates the instance
is being created as part of an aggregate, and punkOuter is to be
used as the new instance's controlling IUnknown. Aggregation is currently
not supported cross-process or cross-machine. When instantiating an object
out of process, CLASS_E_NOAGGREGATION will be returned if punkOuter is non-NULL. */
DWORD dwClsCtx, /*[in] Values taken from the CLSCTX enumeration.*/
COSERVERINFO * pServerInfo,
/*[in] Information about the computer on which to instantiate
the object. May be NULL, in which case the object is instantiated
on the local computer or at the computer specified in the registry
under the class's RemoteServerName named value, according to the
interpretation of the dwClsCtx parameter. See the CLSCTX documentation
for details).*/
ULONG cmq,/*[in] Number of MULTI_QI structures in pResults. Must be greater than zero.*/
MULTI_QI * pResults
/*[in] Array of MULTI_QI structures. Each structure has three members: the
identifier for a requested interface (pIID), the location to return the
interface pointer (pItf) and the return value of the call to QueryInterface (hr).*/
);
Looking at this description, we can surely say that ebp+8 should contain the address of a _GUID structure variable (which contains the clsid of the object to be created) … let’s see what it is!
0:005> dt poi(ebp+8) _GUID
ntdll!_GUID
{3cec3e6d-ecf2-4b49-8a41-3b16df8b9c3f}
+0x000 Data1 : 0x3cec3e6d
+0x004 Data2 : 0xecf2
+0x006 Data3 : 0x4b49
+0x008 Data4 : [8] "???"
We’ve got it!!!
Now, I don’t know what this clsid refers to, I checked in my registry, it wasn’t there. Then I remembered that customer also gave me his procmon log (You might already know about procmon, but if not , please don’t forget to visit Sysinternals ).
I had a look at the procmon log with the following filters
and this is what I see …

So, I got the name of the dll that is being instantiated (name doesn’t matter, it’s the one I removed from the picture above – say foo.dll). I suggested the customer to unregister this dll and see if it helps, he came back with the information that, the path and the dll name we gave him are not present on his machine, he told us that he did install this component (which is basically an Icon Overlay Handler), but they have uninstalled it ….
Next action plan is to know, how did we get the reference to this component, checked again in the promon, changing the filter a bit -
This tells me
Huh, that means this GUID was picked up from a subkey of “HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers”
Then I suggested the customer to take a backup of his registry, and remove this subkey from ShellIconOverlayIdentifiers and remove the GUID key ({3CEC3E6D-ECF2-4B49-8A41-3B16DF8B9C3F}” ) from “HKCR\CLSID”.
This worked like a charm!! The case is resolved!!
(btw, if you remember our first clue, this information is almost similar to our assumptions, we assumed deployments, this was uninstallation … so, maybe in this specific case, uninstaller wasn’t removing all the entries, this in turn rendered SaveAs useless for excel whenever you uninstalled this specific product!
Talk to you latter ..bye!