{"id":108925,"date":"2023-10-25T07:00:00","date_gmt":"2023-10-25T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108925"},"modified":"2023-10-25T08:54:34","modified_gmt":"2023-10-25T15:54:34","slug":"20231025-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20231025-00\/?p=108925","title":{"rendered":"The format of icon resources, revisited"},"content":{"rendered":"<p>Some time ago, I explained <a title=\"The format of icon resources\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20120720-00\/?p=7083\"> that a logical icon is represented in resources as a group icon directory and a series of individual icon directory entries<\/a>, each of which in turn refers to another icon image resource.<\/p>\n<p>But I forgot to discuss the format of those second-level resources.<\/p>\n<p>If the icon image resource represents a cursor, then the resource data begins with two 16-bit signed integers representing the x- and y-coordinates of the cursor hotspot.<\/p>\n<p>Next, after the optional hotspot information, comes one of various image formats.<\/p>\n<p>One possibility is the legacy <code>BITMAPCOREHEADER<\/code>, followed by the color table (possibly empty), and then the bitmap pixels.<\/p>\n<p>Another possibility is a <code>BITMAPINFOHEADER<\/code>, again followed by the possibly-empty color table, and then the bitmap pixels.<\/p>\n<p><a title=\"The evolution of the ICO file format, part 4: PNG images\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20101022-00\/?p=12473\"> And a third possibility<\/a> (starting in Windows Vista) is a PNG-compressed image.<\/p>\n<p>Here&#8217;s a diagram (not to scale):<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Legacy format<\/th>\n<th>Common format<\/th>\n<th>PNG format<\/th>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\" colspan=\"3\">\n<div style=\"display: inline-block;\"><code>int16_t xHotspot;<\/code><br \/>\n<code>int16_t yHotspot;<\/code><\/div>\n<div style=\"display: inline-block;\">(Present only for cursors.)<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\"><code>BITMAPCOREHEADER<\/code><br \/>\ncolor table (possibly empty)<br \/>\npixels<\/td>\n<td valign=\"top\"><code>BITMAPINFOHEADER<\/code><br \/>\ncolor table (possibly empty)<br \/>\npixels<\/td>\n<td valign=\"top\">PNG image<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You can access this raw icon image resource data by finding, loading, and locking the icon image resource ID with a resource of type <code>RT_ICON<\/code>. You can then pass a pointer to the raw icon image resource data to <code>Create\u00adIcon\u00adFrom\u00adResource<\/code> or <code>Create\u00adIcon\u00adFrom\u00adResource\u00adEx<\/code> to convert it to a proper <code>HICON<\/code>.<\/p>\n<p>Alternatively, you can synthesize one of these formats in memory and pass it to <code>Create\u00adIcon\u00adFrom\u00adResource\u00ad(Ex)<\/code>.<\/p>\n<p>Note that in practice, you will want to use <code>Create\u00adIcon\u00adFrom\u00adResource\u00adEx<\/code> because <code>Create\u00adIcon\u00adFrom\u00adResource<\/code> creates icons at the system default icon size, rather than respecting the size reported by the the icon image data.<\/p>\n<p>The fact that the icon image source data can take the form of a PNG image gives you a sneaky way to load a PNG image as an icon: Load the PNG image into memory and pass it directly to <code>Create\u00adIcon\u00adFrom\u00adResource\u00adEx<\/code>!<\/p>\n<pre>wil::unique_hicon LoadPngAsIcon(PCWSTR fileName)\r\n{\r\n    auto file = wil::unique_hfile{\r\n        CreateFileW(fileName, GENERIC_READ, 0, nullptr,\r\n                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr) };\r\n    THROW_LAST_ERROR_IF(!file);\r\n\r\n    LARGE_INTEGER size;\r\n    THROW_IF_WIN32_BOOL_FALSE(GetFileSizeEx(file.get(), &amp;size));\r\n    THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE),\r\n                size.HighPart != 0);\r\n    auto bits = std::make_unique&lt;BYTE[]&gt;(size.LowPart);\r\n\r\n    DWORD actual;\r\n    THROW_IF_WIN32_BOOL_FALSE(ReadFile(file.get(), bits.get(),\r\n            size.LowPart, &amp;actual, nullptr));\r\n    auto icon = wil::unique_hicon{\r\n            CreateIconFromResourceEx(bits.get(), actual, true,\r\n                0x00030000, 0, 0, 0) };\r\n    THROW_LAST_ERROR_IF(!icon);\r\n\r\n    return icon;\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Filling in some gaps.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108925","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Filling in some gaps.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108925","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108925"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108925\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108925"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108925"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108925"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}