{"id":16423,"date":"2009-10-09T10:00:00","date_gmt":"2009-10-09T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2009\/10\/09\/loadstring-can-load-strings-with-embedded-nulls-but-your-wrapper-function-might-not\/"},"modified":"2009-10-09T10:00:00","modified_gmt":"2009-10-09T10:00:00","slug":"loadstring-can-load-strings-with-embedded-nulls-but-your-wrapper-function-might-not","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20091009-00\/?p=16423","title":{"rendered":"LoadString can load strings with embedded nulls, but your wrapper function might not"},"content":{"rendered":"<p>\nWhenever somebody reports that the\n<code>SHFileOperation<\/code> function\nor the <code>lpstrFilter<\/code> member of the\n<code>OPENFILENAME<\/code> structure\nis not working,\nmy psychic powers tell me that they\n<a href=\"http:\/\/shellrevealed.com\/blogs\/shellblog\/archive\/2006\/09\/28\/Common-Questions-Concerning-the-SHFileOperation-API_3A00_-Part-2.aspx\">\nfailed to manage\nthe double-null-terminated strings<\/a>.\n<\/p>\n<p>\nSince\n<a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/01\/30\/65013.aspx\">\nstring resources take the form of a counted string<\/a>,\nthey can contain embedded null characters,\nsince the null character is not being used as the string terminator.\nThe <code>LoadString<\/code> function knows about this,\nbut other functions might not.\n<\/p>\n<p>\nHere&#8217;s one example:\n<\/p>\n<pre>TCHAR szFilters[80];<br \/><i>strcpy_s(szFilters, 80, \"Text files\\0*.txt\\0All files\\0*.*\\0\");<br \/>\/\/ ... or ...<br \/>strlcpy(szFilters, \"Text files\\0*.txt\\0All files\\0*.*\\0\", 80);<\/i><br \/><\/pre>\n<p>\nThe problem is that you&#8217;re using a function which operates\non null-terminated strings\nbut you&#8217;re giving it a double-null-terminated string.\nOf course, it will stop copying at the first null terminator,\nand the result is that <code>szFilters<\/code> is not a valid\ndouble-null-terminated string.\n<\/p>\n<p>\nHere&#8217;s another example:\n<\/p>\n<pre><i>sprintf_s(szFilters, 80, \"%s\\0*.txt\\0%s\\0*.*\\0\", \"Text files\", \"All files\");<\/i><br \/><\/pre>\n<p>\nSame thing here.\nFunctions from the\n<code>sprintf<\/code> family take a null-terminated\nstring as the format string.\nIf you &#8220;embed&#8221; a null character into the format string,\nthe <code>sprintf<\/code> function will treat it as the end of the\nformat string and stop processing.\n<\/p>\n<p>\nHere&#8217;s a more subtle example:\n<\/p>\n<pre>CString strFilter;<br \/>strFilter.LoadString(g_hinst, IDS_FILE_FILTER);<br \/><\/pre>\n<p>\nThere is no obvious double-null-termination bug here,\nbut there is if you look deeper.\n<\/p>\n<pre>BOOL CString::LoadString(UINT nID)<br \/>{<br \/>  \/\/ try fixed buffer first (to avoid wasting space in the heap)<br \/>  TCHAR szTemp[256];<br \/>  int nCount =  sizeof(szTemp) \/ sizeof(szTemp[0]);<br \/>  int nLen = _LoadString(nID, szTemp, nCount);<br \/>  if (nCount - nLen &gt; CHAR_FUDGE)<br \/>  {<br \/>    *this = szTemp;<br \/>    return nLen &gt; 0;<br \/>  }<br \/> <br \/>  \/\/ try buffer size of 512, then larger size until entire string is retrieved<br \/>  int nSize = 256;<br \/>  do<br \/>  {<br \/>    nSize += 256;<br \/>    nLen = _LoadString(nID, GetBuffer(nSize - 1), nSize);<br \/>  } while (nSize - nLen &lt;= CHAR_FUDGE);<br \/>  ReleaseBuffer();<br \/> <br \/>  return nLen &gt; 0;<br \/>}<br \/><\/pre>\n<p>\nObserve that this function loads the string into a temporary\nbuffer, and then if it succeeds, stores the result via the\n<code>operator=<\/code> operator,\nwhich assumes a null-terminated string.\nIf your string resource contains embedded nulls,\nthe <code>operator=<\/code> operator will stop at the first null.\n<\/p>\n<p>\nThe mistake here was taking a class designed for null-terminated strings\nand using it for something that isn&#8217;t a null-terminated string.\nAfter all, it&#8217;s called a <code>CString<\/code> and not a\n<code>CDoubleNullTerminatedString<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Whenever somebody reports that the SHFileOperation function or the lpstrFilter member of the OPENFILENAME structure is not working, my psychic powers tell me that they failed to manage the double-null-terminated strings. Since string resources take the form of a counted string, they can contain embedded null characters, since the null character is not being used [&hellip;]<\/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-16423","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Whenever somebody reports that the SHFileOperation function or the lpstrFilter member of the OPENFILENAME structure is not working, my psychic powers tell me that they failed to manage the double-null-terminated strings. Since string resources take the form of a counted string, they can contain embedded null characters, since the null character is not being used [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/16423","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=16423"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/16423\/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=16423"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=16423"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=16423"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}