{"id":98945,"date":"2018-06-08T07:00:00","date_gmt":"2018-06-08T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=98945"},"modified":"2019-03-13T00:41:56","modified_gmt":"2019-03-13T07:41:56","slug":"20180608-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20180608-00\/?p=98945","title":{"rendered":"Why does GetServiceDisplayNameA report a larger required buffer size than actually necessary?"},"content":{"rendered":"<p>If you call the <code>Get&shy;Service&shy;Display&shy;NameA<\/code> function (the <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/\">ANSI<\/a> version), it reports a buffer size far larger than what appears to be necessary. <\/p>\n<p>For example, if the service display name is <code>awesome<\/code>, you would expect that the required buffer size is seven characters, one for each character in <code>\"awesome\"<\/code>. (The <code>Get&shy;Service&shy;Display&shy;NameA<\/code> function does not count the terminating null; it expects you to add one yourself.) <\/p>\n<p>But instead, the <code>Get&shy;Service&shy;Display&shy;NameA<\/code> function says that you need fourteen characters. And then when you give it a buffer that is fifteen characters long, it fills in only eight of them, and then says &#8220;Ha ha, I wrote only seven characters (plus the terminating null). Silly you allocated far more memory than you needed to, sucka!&#8221; <\/p>\n<p>Why is it reporting a required buffer size larger than what it actually needs? <\/p>\n<p>Because character set conversion is hard. <\/p>\n<p>When you call the <code>Get&shy;Service&shy;Display&shy;NameA<\/code> function (ANSI version), it forwards the call to <code>Get&shy;Service&shy;Display&shy;NameW<\/code> function (Unicode version). If the Unicode version says, &#8220;Sorry, that buffer is too small; it needs to be big enough to hold <var>N<\/var> Unicode characters,&#8221; the ANSI version doesn&#8217;t know how many ANSI characters that translates to. A single Unicode character could expand to as many as two ANSI characters in the case where the ANSI code page is DBCS. The <code>Get&shy;Service&shy;Display&shy;NameA<\/code> function plays it safe and takes the worst-case scenario that the service display name consists completely of Unicode characters which require two ANSI characters to represent. <\/p>\n<p>That&#8217;s why it over-reports the buffer size. <\/p>\n<p>When you call it with a buffer that is fifteen characters long, the <code>Get&shy;Service&shy;Display&shy;NameA<\/code> function calls the <code>Get&shy;Service&shy;Display&shy;NameW<\/code> function, which says, &#8220;No problem, here&#8217;s your display name. It&#8217;s seven Unicode characters long.&#8221; The <code>Get&shy;Service&shy;Display&shy;NameA<\/code> function then converts it from Unicode to ANSI, and it turns out that it requires only seven characters plus the terminating null. Hm, how about that. Okay, well here&#8217;s your seven-character string. Sorry about the extra seven characters you allocated. I asked you to allocate them just in case. <\/p>\n<p><b>Bonus chatter<\/b>: These worst-case calculations will break if the ANSI code page were ever UTF-8, because the worst-case expansion becomes three UTF-8 code units for one UTF-16 code unit, rather than just two to one for DBCS code pages. These types of assumptions about the worst-case scenario are buried throughout tens of millions of lines of source code. Finding them is quite a challenge. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s making a conservative guess.<\/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-98945","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It&#8217;s making a conservative guess.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/98945","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=98945"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/98945\/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=98945"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=98945"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=98945"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}