Creating HICON Directly from Memory

[updated] 

 How many times you had to do something trivial and you knew there ought to be a decent solution out there?

That’s another common problem that I smashed my head against the wall and came up with a surprising simple solution. For example you might archive icons in a single file, or you download them from the Internet to a buffer in memory. And then you want to display them. That sounds an easy task ah? But most people will just write the memory block back to a temporary file and only then will use LoadImage to load the icon from the disk. That’s lame and hits performance big time. To spare you with the research I had done to solve it, here’s the code:

HICON createIcon(PBYTE iconData, int iconSize)     

{     

   HICON hIcon = NULL;     

   // Ahhh, this is the magic API.     

   int offset = LookupIconIdFromDirectoryEx(iconData, TRUE, iconSize, iconSize, LR_DEFAULTCOLOR);     

   if (offset != 0) {     

      hIcon = CreateIconFromResourceEx(iconData + offset, 0, TRUE, 0x30000, iconSize, iconSize, LR_DEFAULTCOLOR);     

   }     

   return hIcon;     

}

You should read the documenation about LookupIconIdFromDirectoryEx, it says the call should be wrapped with SEH…for malformed files. Probably you ask yourself why I treat the ID as an offset. But that’s because if you look at the structures of ICON in-file and in-memory you’ll notice that’s the same field. So it fits for our purpose here and gives us the offset to the single icon’s data rather than the whole directory as it is saved in file or resource data.

Of course, you have to call DestroyIcon when you’re done messing with the icon…

 For more information about icons format and Win32 implementation and relevant API,  check this out. It really helped me in understanding how it all works. Funny that it even hints about that LookupIconIdFromDirectoryEx. ;) Too late.

4 Responses to “Creating HICON Directly from Memory”

  1. Janine says:

    I hoped, this would be the answer to all my prayers…
    But I have the problem, that I always get the same icon (the first one in the file), no matter which size I want. Do you know, why?
    Have you tried that example with an icon that supports different sizes (16, 32, 48)?

    Here’s a part of my code:

    CFile f(strFilePath, CFile::modeRead);
    DWORD dwSize = f.GetLength();
    PBYTE pbuf = new BYTE[dwSize];
    UINT bytesRead = f.Read( pbuf, dwSize );

    HICON hIcon = NULL;
    int nSize = 48;
    int offset = LookupIconIdFromDirectoryEx(pbuf, TRUE, nSize, nSize, LR_DEFAULTCOLOR);

    if (offset != 0)
    {
    hIcon = CreateIconFromResourceEx(pbuf + offset, 0, TRUE, 0x00030000, nSize, nSize, LR_DEFAULTCOLOR);
    }

    The value of “offset” is always the same, no matter if “nSize” is 16, 32 or 48 (yes, the icon I wanted to load supports all these sizes). For sure, the created icon is the same, too…

    Thanks for answers…

    Greets,

    “SmashedHerHeadAgainstTheWallToo” J.

  2. SmashedHisHeadToo says:

    1. You *have* to convert the ICONDIR to a GRPICONDIR. LookupIconIdFromDirectoryEx *really* does not work with ICONDIR. It returns id not offset. You are seeing offset because of a side effect of the similar structures but the lookup selection will not work correctly!

    2. GRPICONDIR struct *must* be DWORD alligned.

    One or both of these will be causing your lookup to always give the same (and/or) wrong icon.

    Once you have the id, then lookup the icon from your GRPICONDIR struct yourself to get the data to pass to CreateIconFromResourceEx.

  3. 轩辕虞舜 says:

    sizeof(ICONDIRENTRY) == 16
    LookupIconIdFromDirectoryEx -> RtlGetIdFromDirectory -> GetBestImage

    GetBestImage(ICONDIRENTRY* lpEntry, DWORD dwCount, INT* cxDesird, INT* cyDesired, ….) {
    for (DWORD i = 0; i < dwCount; ++i) {
    Result = MatchImage(lpEntry, …..);
    lpEntry += 14; // Not lpEntry += 16, i think this is a microsoft's mistake.
    …..
    }
    }

  4. Linda says:

    Hey Janine,

    The author made a mistake.

    The last member of ICONDIRENTRY (as in ico file) and GRPICONDIRENTRY (as in PE resource) is not identical. The former is DWORD dwImageOffset, and the latter is WORD nID. Look, they have different bytes in length.

    As MSDN said, LookupIconIdFromDirectoryEx function operates on PE resource structures, so using this function to operate on ico files ***more than one icon*** will certainly get wrong results.

    Greets,

    Linda Zhang

Leave a Reply