PRB: System Image List Does Not Contain Overlay Images

ID: Q192055

The information in this article applies to:

SYMPTOMS

When you are using the system image list in an application, you might want to be able to use the standard overlay images, such as the shared and shortcut overlay images. However, when you have the Internet Explorer 4.0x Desktop Update installed on Windows NT 4.0, the standard overlay images are not contained in the process' copy of the system image list.

CAUSE

You can obtain the handle to the system image list with a function call similar to the following:

   HIMAGELIST  himl;
   SHFILEINFO  sfi;

   himl = (HIMAGELIST)SHGetFileInfo(TEXT("C:\\"), 0, &sfi,
      sizeof(SHFILEINFO), SHGFI_SYSICONINDEX);

On Windows 95 and Windows 98, this function returns the handle of the actual system image list. However, on Windows NT version 4.0 this function returns a handle to a copy of the system image. Each process gets its own copy of the system image list. Windows NT does this to maintain security requirements so that one application cannot destroy the system image list. The proper images are added to the process' copy of the system image list whenever a call to SHGetFileInfo is made to retrieve the icon index for a specific item type.

The copy of the system image list on Windows NT version 4.0 initially contains the standard document and folder icons, as well as the shared and shortcut overlay images. However, when you have the Internet Explorer 4.0x Desktop Update installed then the standard overlay images are not included in the copy of the system image list. You cannot add them by calling SHGetFileInfo because the Internet Explorer 4.0x desktop Update implements the IShellIconOverlay interface to provide callers with the proper overlay index.

RESOLUTION

To add the standard overlay images to the process' copy of the system image list with the Desktop Update, you need to get an IShellIconOverlay interface pointer for a file system folder and call its GetOverlayIndex method for a shortcut (.lnk) file that is contained in the folder. This causes both standard overlay images to be added to the process' copy of the system image list. GetOverlayIndex also registers the overlay images using the ImageList_SetOverlayImage function.

The following function retrieves the handle of the system image list, and, if you are running under Windows NT version 4.0, it creates a temporary .lnk file. Then it gets the IShellIconOverlay interface from the parent folder and calls IShellIconOverlay::GetOverlayIndex, passing the PIDL for the temporary .lnk file. Finally, the handle to the system image list is returned from the function. The fSmall flag indicates if the small or large image list is being requested. If fSmall is zero, the system image list that contains large icons is retrieved. If fSmall is non-zero, the system image list that contains large icons is retrieved. If Internet Explorer 4.0 with the Desktop Update is not installed on your computer, the function cannot retrieve the IShellIconOverlay interface, so the function will simply return the image list handle.

Sample Code

   /********************************************************************

      GetSystemImageList()

   ********************************************************************/ 

   HIMAGELIST GetSystemImageList(BOOL fSmall)
   {
   HIMAGELIST  himl;
   SHFILEINFO  sfi;

   himl = (HIMAGELIST)SHGetFileInfo(TEXT("C:\\"), 0, &sfi,
      sizeof(SHFILEINFO), SHGFI_SYSICONINDEX |
         (fSmall ? SHGFI_SMALLICON : SHGFI_LARGEICON));

   /*
   Do a version check first because you only need to use this code on
   Windows NT version 4.0.
   */ 
   OSVERSIONINFO vi;
   vi.dwOSVersionInfoSize = sizeof(vi);
   GetVersionEx(&vi);
   if(VER_PLATFORM_WIN32_WINDOWS == vi.dwPlatformId)
      return himl;

   /*
   You need to create a temporary, empty .lnk file that you can use to
   pass to IShellIconOverlay::GetOverlayIndex. You could just enumerate
   down from the Start Menu folder to find an existing .lnk file, but
   there is a very remote chance that you will not find one. By creating
   your own, you know this code will always work.
   */ 
   HRESULT           hr;
   IShellFolder      *psfDesktop = NULL;
   IShellFolder      *psfTempDir = NULL;
   IMalloc           *pMalloc = NULL;
   LPITEMIDLIST      pidlTempDir = NULL;
   LPITEMIDLIST      pidlTempFile = NULL;
   TCHAR             szTempDir[MAX_PATH];
   TCHAR             szTempFile[MAX_PATH] = TEXT("");
   TCHAR             szFile[MAX_PATH];
   HANDLE            hFile;
   int               i;
   OLECHAR           szOle[MAX_PATH];
   DWORD             dwAttributes;
   DWORD             dwEaten;
   IShellIconOverlay *psio = NULL;
   int               nIndex;

   // Get the desktop folder.
   hr = SHGetDesktopFolder(&psfDesktop);
   if(FAILED(hr))
      goto exit;

   // Get the shell's allocator.
   hr = SHGetMalloc(&pMalloc);
   if(FAILED(hr))
      goto exit;

   // Get the TEMP directory.
   if(!GetTempPath(MAX_PATH, szTempDir))
      {
      /*
      There might not be a TEMP directory. If this is the case, use the
      Windows directory.
      */ 
      if(!GetWindowsDirectory(szTempDir, MAX_PATH))
         {
         hr = E_FAIL;
         goto exit;
         }
      }

   // Create a temporary .lnk file.
   if(szTempDir[lstrlen(szTempDir) - 1] != '\\')
      lstrcat(szTempDir, TEXT("\\"));
   for(i = 0, hFile = INVALID_HANDLE_VALUE;
      INVALID_HANDLE_VALUE == hFile;
      i++)
      {
      lstrcpy(szTempFile, szTempDir);
      wsprintf(szFile, TEXT("temp%d.lnk"), i);
      lstrcat(szTempFile, szFile);

      hFile = CreateFile(  szTempFile,
                           GENERIC_WRITE,
                           0,
                           NULL,
                           CREATE_NEW,
                           FILE_ATTRIBUTE_NORMAL,
                           NULL);

      // Do not try this more than 100 times.
      if(i > 100)
         {
         hr = E_FAIL;
         goto exit;
         }
      }

   // Close the file you just created.
   CloseHandle(hFile);
   hFile = INVALID_HANDLE_VALUE;

   // Get the PIDL for the directory.
   LocalToWideChar(szOle, szTempDir, MAX_PATH);
   hr = psfDesktop->ParseDisplayName(  NULL,
                                       NULL,
                                       szOle,
                                       &dwEaten,
                                       &pidlTempDir,
                                       &dwAttributes);
   if(FAILED(hr))
      goto exit;

   // Get the IShellFolder for the TEMP directory.
   hr = psfDesktop->BindToObject(   pidlTempDir,
                                    NULL,
                                    IID_IShellFolder,
                                    (LPVOID*)&psfTempDir);
   if(FAILED(hr))
      goto exit;

   /*
   Get the IShellIconOverlay interface for this folder. If this fails,
   it could indicate that you are running on a pre-Internet Explorer 4.0
   shell, which doesn't support this interface. If this is the case, the
   overlay icons are already in the system image list.
   */ 
   hr = psfTempDir->QueryInterface(IID_IShellIconOverlay, (LPVOID*)&psio);
   if(FAILED(hr))
      goto exit;

   // Get the PIDL for the temporary .lnk file.
   LocalToWideChar(szOle, szFile, MAX_PATH);
   hr = psfTempDir->ParseDisplayName(  NULL,
                                       NULL,
                                       szOle,
                                       &dwEaten,
                                       &pidlTempFile,
                                       &dwAttributes);
   if(FAILED(hr))
      goto exit;

   /*
   Get the overlay icon for the .lnk file. This causes the shell
   to put all of the standard overlay icons into your copy of the system
   image list.
   */ 
   hr = psio->GetOverlayIndex(pidlTempFile, &nIndex);

   exit:
   // Delete the temporary file.
   DeleteFile(szTempFile);

   if(psio)
      psio->Release();

   if(INVALID_HANDLE_VALUE != hFile)
      CloseHandle(hFile);

   if(psfTempDir)
      psfTempDir->Release();

   if(pMalloc)
      {
      if(pidlTempFile)
         pMalloc->Free(pidlTempFile);

      if(pidlTempDir)
         pMalloc->Free(pidlTempDir);

      pMalloc->Release();
      }

   if(psfDesktop)
      psfDesktop->Release();

   return himl;
   }

STATUS

This behavior is by design.

MORE INFORMATION

On computers that do not have the Internet Explorer 4.0x Desktop Update installed, the overlay indexes (not to be confused with the image index-- see the documentation for ImageList_SetOverlayImage for more information) for the two standard overlay images are fixed. On these systems, one is the overlay index for the shared overlay and two is the overlay index for the shortcut overlay. However, if the installed shell exposes the IShellIconOverlay interface for folders, you should use IShellIconOverlay::GetOverlayIndex to obtain the overlay index. This will allow you to add additional overlay images in the future and allow applications to retrieve the correct overlay index without having to hard- code these indexes.

To maintain backward compatibility, the overlay indexes for the shared and shortcut overlays remain fixed.

Additional query words:

Keywords          : kbSDKPlatform kbSDKWin32 kbshell kbGrpShell 
Issue type        : kbprb

Last Reviewed: September 4, 1998