The relationship between module resources and resource-derived objects in 32-bit Windows


Last time, we saw how 16-bit Windows converted resources attached to an EXE or DLL file (which I called module resources for lack of a better term) to user interface resources. As a refresher:

16-bit Resources
Resource type Operation Result
Icon Load­Icon, etc. Reference
Cursor Load­Cursor, etc. Reference
Accelerator Load­Accelerator, etc. Reference
Dialog Create­Dialog, etc. Copy
Menu Load­Menu, etc. Copy
Bitmap Load­Bitmap, etc. Copy
String Load­String Copy
String Find­Resource Reference

During the conversion from 16-bit Windows to 32-bit Windows, some of these rules changed. Specifically, icons, cursors, and accelerator tables are no longer references to the resource. Instead, the resource is treated as a template from which the actual user interface resource is constructed.

32-bit Resources
Resource type Operation Result
Icon Load­Icon, etc. Copy*
Cursor Load­Cursor, etc. Copy*
Accelerator Load­Accelerator, etc. Copy*
Dialog Create­Dialog, etc. Copy
Menu Load­Menu, etc. Copy
Bitmap Load­Bitmap, etc. Copy
String Load­String Copy
String Find­Resource Reference

Uh-oh, what’s up with those asterisks?

Let’s start with accelerator tables. In order to simulate the reference semantics of 16-bit accelerator tables, the copy is cached with a reference count, so that if you ask for the same accelerator table 1000 times, the first request creates a new accelerator table, and the other 999 requests just increment the reference count and return the same handle back. The result is that the window manager emulates reference semantics, but with an initial copy. When the reference count on an accelerator table drops to zero, then the resource is freed.

Icons and cursors are the same, only weirder.

If you pass the LR_SHARED flag, then the window manager simulates reference semantics by creating a copy of the icon or cursor the first time it is requested, and all subsequent requests with the LR_SHARED flag return the same handle back again.¹ The Load­Cursor and Load­Icon functions are just wrappers around Load­Image that pass LR_SHARED, so applications written to the old 16-bit API still work the 16-bit way. (Even today, a lot of applications rely on the old 16-bit behavior.)

If you don’t pass the LR_SHARED flag, then you get a brand new copy of the icon or cursor. Since the only way to get this behavior is to call the new-for-Win32 function Load­Image, there is no compatibility issue.

Based on the above discussion, we can flesh out the table a bit more:

32-bit Resources
Resource type Operation Result
Icon Load­Icon
Load­Image with LR_SHARED
Cached copy
Load­Image without LR_SHARED Copy
Cursor Load­Cursor
Load­Image with LR_SHARED
Cached copy
Load­Image without LR_SHARED Copy
Accelerator Load­Accelerator, etc. Cached copy
Dialog Create­Dialog, etc. Copy
Menu Load­Menu, etc. Copy
Bitmap Load­Bitmap, etc. Copy
String Load­String Copy
String Find­Resource Reference

Another way of looking at the above table is to break it into two tables, one for operations that had a 16-bit equivalent, and one for operations that are unique to Win32:

32-bit Resource Creation Operations with 16-bit Equivalents
Resource type Operation Result
Icon Load­Icon Simulated reference
Cursor Load­Cursor Simulated reference
Accelerator Load­Accelerator, etc. Simulated reference
Dialog Create­Dialog, etc. Copy
Menu Load­Menu, etc. Copy
Bitmap Load­Bitmap, etc. Copy
String Load­String Copy
String Find­Resource Reference



32-bit Resource Creation Operations Without 16-bit Equivalents
Resource type Operation Result
Icon Load­Image with LR_SHARED Simulated reference
Load­Image without LR_SHARED Copy
Cursor Load­Image with LR_SHARED Simulated reference
Load­Image without LR_SHARED Copy

Now we can answer an old question: “Do icons created from resources depend on the underlying resource?

The answer is no, at least not in 32-bit Windows. The bits are extracted from the module resource data and converted into a icon object, and if you passed the LR_SHARED flag, it is added to the cache of previously-created icons.

¹ Update: If you read carefully, you’ll realize that LR_SHARED stores the results in a cache and pays no attention to the size. The cache is keyed only by the resource module and ID; the size is ignored. This is why MSDN says “Do not use LR_SHARED for images that have nonstandard sizes.”

Suppose you load a resource with LR_SHARED and a nonstandard size. If you are the first person to load that resource, then the nonstandard size gets loaded and put into the cache. The next person to ask for that resource and who asks for a LR_SHARED copy will get the nonstandard-sized resource from the cache regardless of what size they actually wanted.

Conversely, suppose a standard-size resource is already in the cache. You pass LR_SHARED and a nonstandard size. The cache returns you the original standard-size resource, ignoring the size you requested.

To avoid this craziness, the rule is that any request for cached resources must use the standard size.

This requirement wasn’t a problem in 16-bit Windows because 16-bit Windows had no way of requesting a resource at a nonstandard size. And since LR_SHARED is a new flag introduced in 32-bit Windows, all code which uses it can be expected to understand the Win32 rules.

Comments (10)
  1. WndSks says:

    If that is all LR_SHARED does, why does MSDN say this: "Do not use LR_SHARED for images that have non-standard sizes, …"?

    [The caching code ignores the size (because in Win16, there was no way to load something at a nonstandard size). If you use LR_SHARED for a nonstandard size and you are the first to request it, then you put a nonstandard size into the cache, and the next person to ask for the resource will get the nonstandard size rather than the size they requested. If you are the second to request it, your nonstandard size will be ignored and the size in the cache will be returned instead. -Raymond]
  2. Joshua says:

    Probably because the cache can't accept nonstandard sizes.

  3. WndSks says:

    …then it should say the limit is 256×256, what standard sizes means has changed over the years as more icon sizes have been added.

  4. Deduplicator says:

    Just read LR_SHARED as "simulate win16" instead of "cache resource", because using it for anything else but its designed role is asking for trouble.

  5. Nick says:

    It seems reasonable to assume that if you're going to share with others (passing LR_SHARED) there are some constraints in place to make sure that everyone participating in the sharing is happy.  If, on the other hand, you want to be a special little snowflake, you can also take care of managing your own resources! :)

  6. WndSks says:

    @Raymond: If the LR_SHARED cache ignores size maybe MSDN say that you must also specify LR_DEFAULTSIZE?

    Win7 seems to cache based on size:

    void dumpico(HICON hIco)

    {

    ICONINFO ii;

    BITMAP bm;

    if (GetIconInfo(hIco, &ii) && GetObject(ii.hbmColor, sizeof(bm), &bm))

    printf("%#xt%ux%un", hIco, bm.bmWidth, bm.bmHeight);

    }

    void test()

    {

    HINSTANCE hSh32 = LoadLibraryA("SHELL32");

    dumpico(LoadImage(hSh32, MAKEINTRESOURCE(1), IMAGE_ICON, 99, 99, LR_SHARED));

    dumpico(LoadImage(hSh32, MAKEINTRESOURCE(1), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_SHARED));

    dumpico(LoadImage(hSh32, MAKEINTRESOURCE(1), IMAGE_ICON, 77, 77, LR_SHARED));

    dumpico(LoadImage(hSh32, MAKEINTRESOURCE(1), IMAGE_ICON, 0, 0, LR_SHARED|LR_DEFAULTSIZE));

    }

    0x461081b 99×99

    0xbb406a7 32×32

    0x7bf0841 77×77

    0xbb406a7 32×32

    And if you load a SM_CXICON icon with LR_SHARED and assign it to a window it seems to pick up the small icon automatically…

    [Congratulations, you found one case where the cache respects the size. But there are other cases where it doesn't. -Raymond]
  7. Fleet Command says:

    Excellent so far but why bitmaps and strings are not reference-counted? It is not surprising if Windows team had discovered that bitmaps are rarely used more than once but strings seem like a candidate for reference-counting. (I just hope it has nothing to do with null-terminated strings.)

    [Bitmaps and strings were not reference-counted because they weren't module resources. See the linked article. -Raymond]
  8. Marc K says:

    The biggest problem I've had with LR_SHARED and "non-standard" sizes is wanting to load a predefined icon in a 16×16 size.  Without LR_SHARED, I'd get NULL back.  With it, I'd get the 32×32 icon.  Then I had to go on to write a lot of code to manually load the 16×16 version that I really wanted.

  9. SuperKoko says:

    What about LoadResource?

    Your article tells that FindResource returns a reference, but it looks like it's returning a handle to the resource (which is a sort of reference).

    I would expect LoadResource to return a reference since FreeResource is obsolete and may be a dummy function.

  10. Your post caused some confusion to me. I always assumed the LR_SHARED means really "reference", not "simulated reference" (in your terminology). So what about the lifetime of such objects in 32-bit Windows? Should app. call e.g. DestroyIcon() for the shared icon? I.e. is DestroyIcon() smart enough to just decrease the counter in the cache, and destroy it only when it drops to zero? If app does not do that, is the cached copy destroyed on unload of the module it originated from?

Comments are closed.