{"id":111129,"date":"2025-04-30T07:00:00","date_gmt":"2025-04-30T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111129"},"modified":"2025-04-30T12:18:50","modified_gmt":"2025-04-30T19:18:50","slug":"20250430-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250430-00\/?p=111129","title":{"rendered":"Why does Windows have trouble finding my Win32 resource if it contains an accented character?"},"content":{"rendered":"<p>Maurice Kayser reported <a title=\"Issue with Win32 API Loading of PE Resources Containing Lowercase Letters\" href=\"https:\/\/learn.microsoft.com\/en-us\/answers\/questions\/2155202\/issue-with-win32-api-loading-of-pe-resources-conta\"> an issue with Win32 API loading of PE resources containing lowercase letters<\/a>. Maurice did some experiments adding resources named <tt>MyIcon<\/tt>, <tt>MyIc\u00d6n<\/tt>, and <tt>MyIc\u00f6n<\/tt>, then trying to load them using various names, and built up a table of results. I&#8217;ve broken it up into three tables depending on the nature of the accented character.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Arg<\/th>\n<th>Can load <tt>MyIcon<\/tt><\/th>\n<th>Can load <tt>MyIc\u00d6n<\/tt><\/th>\n<th>Can load <tt>MyIc\u00f6n<\/tt><\/th>\n<\/tr>\n<tr>\n<td><tt>myicon<\/tt><\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td><tt>MyIcon<\/tt><\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td><tt>mYiCoN<\/tt><\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td><tt>MYICON<\/tt><\/td>\n<td>Yes<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This table shouldn&#8217;t be surprising. The argument passed to <code>LoadResource<\/code> is compared case-insensitively with the name of the resource, treating accented characters as different from their unaccented versions.<\/p>\n<p>Here&#8217;s the next batch.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Arg<\/th>\n<th>Can load <tt>MyIcon<\/tt><\/th>\n<th>Can load <tt>MyIc\u00d6n<\/tt><\/th>\n<th>Can load <tt>MyIc\u00f6n<\/tt><\/th>\n<\/tr>\n<tr>\n<td><tt>myic\u00d6n<\/tt><\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<td>No (!)<\/td>\n<\/tr>\n<tr>\n<td><tt>MyIc\u00d6n<\/tt><\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<td>No (!)<\/td>\n<\/tr>\n<tr>\n<td><tt>mYiC\u00d6N<\/tt><\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<td>No (!)<\/td>\n<\/tr>\n<tr>\n<td><tt>MYIC\u00d6N<\/tt><\/td>\n<td>No<\/td>\n<td>Yes<\/td>\n<td>No (!)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The first column is consistent with our previous result, namely that unaccented characters are treated as not the same as accented characters.<\/p>\n<p>The second column is not surprising either, since the strings do match according to a case-insensitive comparison.<\/p>\n<p>The third column is surprising. It seems that accented characters are case-sensitive, even though the documentation says that the comparison is case-insensitive.<\/p>\n<p>Okay, here&#8217;s the third block.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Arg<\/th>\n<th>Can load <tt>MyIcon<\/tt><\/th>\n<th>Can load <tt>MyIc\u00d6n<\/tt><\/th>\n<th>Can load <tt>MyIc\u00f6n<\/tt><\/th>\n<\/tr>\n<tr>\n<td><tt>myic\u00f6n<\/tt><\/td>\n<td>No<\/td>\n<td>Yes (?)<\/td>\n<td>No (!)<\/td>\n<\/tr>\n<tr>\n<td><tt>MyIc\u00f6n<\/tt><\/td>\n<td>No<\/td>\n<td>Yes (?)<\/td>\n<td>No (!)<\/td>\n<\/tr>\n<tr>\n<td><tt>mYiC\u00f6N<\/tt><\/td>\n<td>No<\/td>\n<td>Yes (?)<\/td>\n<td>No (!)<\/td>\n<\/tr>\n<tr>\n<td><tt>MYIC\u00f6N<\/tt><\/td>\n<td>No<\/td>\n<td>Yes (?)<\/td>\n<td>No (!)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The PE specification says that the resources are sorted &#8220;in ascending order&#8221;, and the names are sorted &#8220;by case-sensitive string.&#8221;\u00b9<\/p>\n<p>That&#8217;s all it says. The rest is left to interpretation.<\/p>\n<p>First of all, even though the file format specification says that the resource names can be in any case, the <code>FindResource<\/code> function converts all names to uppercase before searching, so any names with lowercase characters are effectively unfindable. Fortunately, the Resource Compiler also converts names to uppercase before storing them in the resources, so it all cancels out, right?<\/p>\n<p>Well, it cancels out only if the Resource Compiler and the <code>FindResource<\/code> function agree on how the names are converted to uppercase.<\/p>\n<p>The Resource Compiler uses <code>_wcsupr<\/code> to convert the names to uppercase, and <code>_wcsupr<\/code> uses the default C locale,\u00b2 which as we noted before, <a title=\"The default C locale is not a very interesting one\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250206-00\/?p=110846\"> is not a very interesting locale<\/a>. It converts Latin unaccented lowercase letters a-z to Latin unaccented uppercase letters A-Z, and that&#8217;s all.<\/p>\n<p>Let&#8217;s update the top row of the table by converting the names to uppercase according to the C locale.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Arg<\/th>\n<th>Can load <tt>MYICON<\/tt><\/th>\n<th>Can load <tt>MYIC\u00d6N<\/tt><\/th>\n<th>Can load <tt>MYIC\u00f6N<\/tt><\/th>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>How does the <code>FindResource<\/code> function convert strings to uppercase? It uses the uppercase table corresponding to the system default language. It is almost certain that <tt>\u00d6<\/tt> and <tt>\u00f6<\/tt> are uppercase and lowercase partners in the system default language. That means that the left columns are all effectively <tt>MYICON<\/tt> in the first table, and that they are all effectively <tt>MYIC\u00d6N<\/tt> in the second and third tables.<\/p>\n<p>With these adjustments, the tables make more sense.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th rowspan=\"2\">Arg<\/th>\n<th rowspan=\"2\">Loaded<br \/>\nas<\/th>\n<th>Can load <tt>MyIcon<\/tt><\/th>\n<th>Can load <tt>MyIc\u00d6n<\/tt><\/th>\n<th>Can load <tt>MyIc\u00f6n<\/tt><\/th>\n<\/tr>\n<tr>\n<th>Stored as <tt>MYICON<\/tt><\/th>\n<th>Stored as <tt>MYIC\u00d6N<\/tt><\/th>\n<th>Stored as load <tt>MYIC\u00f6N<\/tt><\/th>\n<\/tr>\n<tr>\n<td><tt>myicon<\/tt><\/td>\n<td rowspan=\"4\"><tt>MYICON<\/tt><\/td>\n<td rowspan=\"4\">Yes<\/td>\n<td rowspan=\"4\">No<\/td>\n<td rowspan=\"4\">No<\/td>\n<\/tr>\n<tr>\n<td><tt>MyIcon<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>mYiCoN<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>MYICON<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>myic\u00d6n<\/tt><\/td>\n<td rowspan=\"8\"><tt>MYIC\u00d6N<\/tt><\/td>\n<td rowspan=\"8\">No<\/td>\n<td rowspan=\"8\">Yes<\/td>\n<td rowspan=\"8\">No<\/td>\n<\/tr>\n<tr>\n<td><tt>MyIc\u00d6n<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>mYiC\u00d6N<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>MYIC\u00d6N<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>myic\u00f6n<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>MyIc\u00f6n<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>mYiC\u00f6N<\/tt><\/td>\n<\/tr>\n<tr>\n<td><tt>MYIC\u00f6N<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Okay, so after we have accounted for how the Resource Compiler stores names and how <code>FindResource<\/code> searches for names, the table looks less bonkers.<\/p>\n<p>The moral of the story, I think, is that you should just stick to ASCII characters for resource names. Everybody agrees on that subset.<\/p>\n<p>\u00b9 Note that the specification is incomplete: It doesn&#8217;t say what collation to use for sorting. Does it use a locale-sensitive sort, so that <tt>\u00d6<\/tt> comes before <tt>P<\/tt> in German, but after <tt>P<\/tt> in Swedish?\u00b3 Does it use a case-sensitive sort where all punctuation come before all alphabetics? The <code>FindResource<\/code> function assumes that the resources are sorted lexicographically by code unit (not code point) numerical value. Which is a good thing, because you don&#8217;t want a file compiled on a German system to be considered corrupted by a Swedish system.<\/p>\n<p>\u00b2 But what about the <code>#pragme code_page()<\/code> directive? That directive tells the Resource Compiler how to convert quoted strings to Unicode, but it does not affect character mapping or collation.<\/p>\n<p>\u00b3 In German dictionary sorting, the letter <tt>\u00d6<\/tt> is sorted as if it had no accent mark. But in German phone book sorting, the letter <tt>\u00d6<\/tt> is sorted as if it were two characters <tt>O<\/tt> + <tt>e<\/tt>. And in <a href=\"https:\/\/de.wikipedia.org\/wiki\/Alphabetische_Sortierung#.C3.96sterreich\"> Austrian phone book sorting<\/a>, the letter <tt>\u00d6<\/tt> is sorted as if it were two characters <tt>O<\/tt> + <tt>\u00a8<\/tt>, where the <tt>\u00a8<\/tt> is treated as a character that comes after <tt>Z<\/tt>. And <a href=\"https:\/\/en.wikipedia.org\/wiki\/Swedish_alphabet#Letters\"> in Swedish<\/a>, the letter <tt>\u00d6<\/tt> is treated as one of the three accented characters that come after <tt>Z<\/tt>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Disagreements over the fine print.<\/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-111129","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Disagreements over the fine print.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111129","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=111129"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111129\/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=111129"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111129"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111129"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}