In the article, “Getting shell item information,” I explained how to get the icon for shell items and, if applicable, the overlay icon. As it turns out, the overlay icons are not added to the application’s image list on Windows NT with the Internet Explorer 4 (or later) Desktop Update installed. The overlay icons are added to the image list properly when the Desktop Update is not installed.
This article will explain how to work around this problem (which Microsoft has categorized, “as designed”).
The key to adding the overlay icons to the image list is the IShellIconOverlay interface. The following steps are required to add the overlay icons:
Create a temporary shortcut file
Get an IShellFolder interface to the folder where the temporary file was created
Get an IShellIconOverlay interface for the folder
Get a pidl for the temporary file
Call the GetOverlayIndex() method of IShellIconOverlay to add the overlays to the image list
Get the index of NTDETECT.COM to use for DOS executables
Following is the code that performs these steps. For the most part the code is explained by the comments in the code.
LPSHELLFOLDER DesktopFolder;
SHGetDesktopFolder(&DesktopFolder);
if (Win32Platform ==
VER_PLATFORM_WIN32_NT) {
LPMALLOC Malloc;
SHGetMalloc(&Malloc);
LPSHELLFOLDER Folder;
LPITEMIDLIST Pidl;
LPITEMIDLIST ParentPidl;
IShellIconOverlay *IconOverlay;
DWORD Eaten;
// Get the Windows temp directory.
char tempDir[MAX_PATH - 1];
GetTempPath(sizeof(tempDir), tempDir);
// Build a path and filename to the
// temporary file. Note that the file
// has an extension of .lnk.
char tempFile[MAX_PATH - 1];
strcpy(tempFile, tempDir);
strcat(tempFile, "temp.lnk");
// Create the file.
CreateFile(tempFile, GENERIC_WRITE,
0, 0, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, 0);
// Get a pidl for the temp folder.
wchar_t Path[MAX_PATH];
StringToWideChar(
tempDir, Path, MAX_PATH);
DesktopFolder->ParseDisplayName(
Handle, 0, Path,
&Eaten, &ParentPidl, 0);
// Get an IShellFolder for
// the temp folder.
DesktopFolder->BindToObject(
ParentPidl, 0, IID_IShellFolder,
(void**)&Folder);
// Get an IShellIconOverlay interface
// for the folder.
DWORD Result = Folder->QueryInterface(
IID_IShellIconOverlay,
(void**)&IconOverlay);
// If we failed then this version of
// the shell does not have this
// interface. That's OK, though,
// because in that case we already
// have the overlay icons.
if (Result == 0) {
StringToWideChar(
"temp.lnk", Path, MAX_PATH);
// Get a pidl for the temp file.
Folder->ParseDisplayName(
Handle, 0, Path, &Eaten, &Pidl, 0);
int Index;
// Get the overlay icons.
IconOverlay->GetOverlayIndex(
Pidl, &Index);
IconOverlay->Release();
}
// Delete the temporary file.
DeleteFile(tempFile);
// Clean up.
Malloc->Free(Pidl);
Malloc->Free(ParentPidl);
Folder->Release();
Malloc->Release();
// Get the icon index of ntdetect.com. This
// index will be used for dos executables
SHFILEINFO fi;
SHGetFileInfo("c:\\ntdetect.com", 0,
&fi, sizeof(fi), SHGFI_SYSICONINDEX);
DosExeIndex = fi.iIcon;
}
I call GetOverlayIndex() for a shortcut because a shortcut, naturally, has an associated overlay icon. It is better to create a temporary shortcut for this purpose then to try to use a shortcut that may or may not be present on a given system. Calling GetOverlayIndex() adds all overlays to the image list, not just the shortcut overlay.
The call to QueryInterface() obtains the IShellIconOverlay interface from the IShellFolder. This call will fail if the IShellIconOverlay interface is not available on a particular system. If the interface cannot be obtained, the overlay icons are already in the image list so IShellIconOverlay is not required.
Note the last three lines of code in the preceding listing. This code gets the icon index for a file called NTDETECT.COM and saves the index in a class member variable. This is necessary because SHGetFileInfo() will return 2 for the icon index of DOS executables, or for executables that don’t define an icon. For whatever reason, this icon index is incorrect. The result is that the icon shown for this type of file will be wrong (usually the shortcut overlay icon is shown). I use NTDETECT.COM because it has the same icon as DOS executables. It is a hidden system file and should be available on all NT systems. I save the icon index for this file and then later apply it if I determine that the icon index is 2. The following code snippet was taken from Listing A of the preceding article and modified to account for the DOS icon index problem.
if (DosExeIndex != -1 && fi.iIcon == 2)
item->ImageIndex = DosExeIndex;
else
item->ImageIndex = fi.iIcon;
If DosExeIndex is –1 it means that the shell has the correct icons (no Desktop Update installed). If DosExeIndex is not –1, and if the iIcon member of SHFILEINFO is 2, then I assign the value of DosExeIndex to the ImageIndex of the list view rather than the icon index returned by SHGetFileInfo(). I’ll admit that this technique feels like a bit of a hack, but I have yet to find any information on how to properly work around this problem.
This is just one example of how certain combinations of operating system and shell version can cause you headaches. Your application may be working perfectly until a user installs Internet Explorer with the Desktop Update. Understanding the quirks of the Windows shell can help you write programs that work on all system configurations.
The complete example program for the article, “Getting shell item information,” includes the code presented in this article. The code was removed from that article’s listing because it is shown here. Download the example from our Web site to see the code in context.