{"id":111873,"date":"2025-12-16T07:00:00","date_gmt":"2025-12-16T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111873"},"modified":"2025-12-16T10:59:08","modified_gmt":"2025-12-16T18:59:08","slug":"20251216-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251216-00\/?p=111873","title":{"rendered":"Why is the Windows clipboard taking the scenic route when converting from <CODE>CF_<WBR>TEXT<\/CODE> to <CODE>CF_<WBR>OEM&shy;TEXT<\/CODE>?"},"content":{"rendered":"<p>Our investigation of why the <code>CF_<wbr \/>OEM\u00adTEXT<\/code> clipboard format is not being created from <code>CF_<wbr \/>TEXT<\/code> via <code>Ansi\u00adTo\u00adOem<\/code> led us to the realization that <a title=\"The Windows clipboard automatic text conversion diagram is non-commutative\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251215-00\/?p=111869\"> the Windows clipboard automatic text conversion diagram is non-commutative<\/a>. And the conversion we&#8217;re observing is consistent with Windows deciding not to do the direct conversion of <code>CF_<wbr \/>TEXT<\/code> to <code>CF_<wbr \/>OEM\u00adTEXT<\/code>, but rather converting from <code>CF_<wbr \/>TEXT<\/code> to <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code>, and from there to <code>CF_<wbr \/>OEM\u00adTEXT<\/code>.<\/p>\n<p>It took a day before I realized what was going on. Let&#8217;s look at the graph again.<\/p>\n<table style=\"border-collapse: collapse; text-align: center;\" title=\"See description in body.\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor;\" colspan=\"3\">CF_TEXT<\/td>\n<\/tr>\n<tr>\n<td>(CF_LOCALE)<\/td>\n<td>\u21c5<\/td>\n<td>&nbsp;<\/td>\n<td>\u2191<\/td>\n<td>(LOCALE_<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor;\">CF_UNICODETEXT<\/td>\n<td>&nbsp;<\/td>\n<td>|<\/td>\n<td>USER_<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"width: 2em;\">\u2196\u2198<\/td>\n<td>\u2193<\/td>\n<td>DEFAULT)<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>(CF_LOCALE)<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px currentcolor;\">CF_OEMTEXT<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If we start with <code>CF_<wbr \/>TEXT<\/code> and somebody asks for <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code>, it will be converted via the <code>CF_<wbr \/>LOCALE<\/code> clipboard format to Unicode (which for Windows means UTF-16LE). And then if we ask for <code>CF_<wbr \/>OEM\u00adTEXT<\/code>, the diagram above shows that Windows will prefer to convert from <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code>, so the string ends up being converted through <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code> after all.<\/p>\n<p>The fact that the conversion diagram is path-dependent means that what you get is now influenced by what other applications read from the clipboard. After our test program copied text to the clipboard in ANSI format, the fact that another program requested <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code> influences what a future <code>CF_<wbr \/>OEM\u00adTEXT<\/code> request will produce.<\/p>\n<p>And it&#8217;s not like the interloper program can do anything about it. There is no way to ask, &#8220;Hey, like, I know that you say that you have <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code>, but do you <i>really<\/i> have it? Or are you just pretending to have it?&#8221;<\/p>\n<p>And how do these interloper programs know when you changed the clipboard? Because they have registered clipboard format listeners. But what program do I have that has registered a clipboard format listener?<\/p>\n<p>And then it occurred to me: I have Clipboard History enabled.<\/p>\n<p>Clipboard History is waking up when the first test program copies ANSI text to the clipboard and reading out <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code> to add to the clipboard history. This triggers the conversion <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code>, and the result is cached back onto the clipboard. This means that when you ask for <code>CF_<wbr \/>OEM\u00adTEXT<\/code>, the clipboard sees that it has a choice of <code>CF_<wbr \/>TEXT<\/code> and <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code>, and from the diagram we see that it prefers converting from <code>CF_<wbr \/>UNICODE\u00adTEXT<\/code>.<\/p>\n<p>I turned off Clipboard History, and the query for <code>CF_<wbr \/>OEM\u00adTEXT<\/code> started behaving as expected: The OEM text was generated by applying <code>Ansi\u00adTo\u00adOem<\/code> to the <code>CF_<wbr \/>TEXT<\/code> contents.<\/p>\n<p>So I came to two conclusions.<\/p>\n<p>First, if you care about OEM text, then you should set your <code>CF_<wbr \/>LOCALE<\/code> to <code>LOCALE_<wbr \/>USER_<wbr \/>DEFAULT<\/code> to avoid path-dependent conversions.<\/p>\n<p>Second, pretty much nobody cares about OEM text.<\/p>\n<p>Next time, we&#8217;ll look at another consequence of the above diagram.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Something is forcing it down an alternate path.<\/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-111873","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Something is forcing it down an alternate path.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111873","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=111873"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111873\/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=111873"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111873"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111873"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}