{"id":5763,"date":"2012-12-24T07:00:00","date_gmt":"2012-12-24T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2012\/12\/24\/what-is-the-proper-handling-of-wm_renderformat-and-wm_renderallformats\/"},"modified":"2024-07-15T07:37:07","modified_gmt":"2024-07-15T14:37:07","slug":"20121224-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20121224-00\/?p=5763","title":{"rendered":"What is the proper handling of WM_RENDERFORMAT and WM_RENDERALLFORMATS?"},"content":{"rendered":"<p>Jeremy points out that the documentation for <code>Set\u00adClipboard\u00adData<\/code> says that the clipboard owner must not call <code>Open\u00adClipboard<\/code> when responding to the <code>WM_<wbr \/>RENDER\u00adFORMAT<\/code> and <code>WM_<wbr \/>RENDER\u00adALL\u00adFORMATS<\/code> messages. On the other hand, the documentation for <code>WM_<wbr \/>RENDER\u00adALL\u00adFORMATS<\/code> says that the owner must call <code>Open\u00adClipboard<\/code> and <code>Empty\u00adClipboard<\/code>. <a href=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/07\/20\/10040074.aspx#10040446\"> Which is it<\/a>?<\/p>\n<p>It&#8217;s none of them!<\/p>\n<p>Let&#8217;s start with <code>WM_<wbr \/>RENDER\u00adFORMAT<\/code>. The reference implementation for a <code>WM_<wbr \/>RENDER\u00adFORMAT<\/code> handler goes like this, with all error handling deleted for expository purposes:<\/p>\n<pre>case WM_RENDERFORMAT:\r\n CLIPFORMAT cf = (CLIPFORMAT)wParam;\r\n hData = GenerateFormat(cf);\r\n SetClipboardData(cf, hData);\r\n return 0;\r\n<\/pre>\n<p>In response to <code>WM_<wbr \/>RENDER\u00adFORMAT<\/code>, you simply place the format on the clipboard. No opening is required. In fact, attempting to open will <i>fail<\/i> because the clipboard is already open: It has been opened by the application whose call to <code>Get\u00adClipboard\u00adData<\/code> triggered the delay-render!<\/p>\n<p>Next comes <code>WM_<wbr \/>RENDER\u00adALL\u00adFORMATS<\/code>. The original reference implementation goes like this, again with error checking deleted:<\/p>\n<pre>\/\/ code in italics is wrong -- see discussion below\r\ncase WM_RENDERALLFORMATS:\r\n <i>OpenClipboard(hwnd);\r\n SendMessage(hwnd, WM_RENDERFORMAT, CF_FORMAT1, 0);\r\n SendMessage(hwnd, WM_RENDERFORMAT, CF_FORMAT2, 0);\r\n CloseClipboard();<\/i>\r\n return 0;\r\n<\/pre>\n<p>In response to <code>WM_<wbr \/>RENDER\u00adALL\u00adFORMATS<\/code>, you open the clipboard, then render all your formats into it, and then close the clipboard. And one way to render your formats is simply to send yourself a fake <code>WM_<wbr \/>RENDER\u00adFORMAT<\/code> message, which gets the code in the earlier code block to generate the format and place it on the clipboard.<\/p>\n<p>So you see that everybody is wrong!<\/p>\n<p>The <code>WM_<wbr \/>RENDER\u00adALL\u00adFORMATS<\/code> handler <i>does<\/i> call <code>Open\u00adClipboard<\/code>\u2014if you tried it without the <code>Open\u00adClipboard<\/code> call, you&#8217;d notice that the data never made it to the clipboard\u2014and it <i>doesn&#8217;t<\/i> call <code>Empty\u00adClipboard<\/code>. (If you did, you&#8217;d notice that the <code>Empty\u00adClipboard<\/code> would have wiped out your non-delay-rendered data!)<\/p>\n<p>Where did I get these reference implementations from? I got them from the <i>Windows 3.1 SDK<\/i>. (And that explains the bug; read on.)<\/p>\n<p>In real life, you probably would also listen for the <code>WM_<wbr \/>DESTROY\u00adCLIPBOARD<\/code> message so you would know that you are no longer the clipboard owner, in which case you wouldn&#8217;t bother rendering anything.<\/p>\n<p>I haven&#8217;t written code in a while, so let&#8217;s write some code. Start with our <a title=\"The scratch program\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20030723-00\/?p=43073\"> scratch program<\/a> and make these changes. We&#8217;ll start by writing it incorrectly:<\/p>\n<pre>BOOL\r\nOnCreate(HWND hwnd, LPCREATESTRUCT lpcs)\r\n{\r\n <span style=\"border: solid 1px currentcolor; border-bottom: none;\">if (OpenClipboard(hwnd)) {              <\/span>\r\n <span style=\"border: 1px currentcolor; border-style: none solid;\"> EmptyClipboard();                      <\/span>\r\n <span style=\"border: 1px currentcolor; border-style: none solid;\"> SetClipboardData(CF_UNICODETEXT, NULL);<\/span>\r\n <span style=\"border: 1px currentcolor; border-style: none solid;\"> CloseClipboard();                      <\/span>\r\n <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                       <\/span>\r\n return TRUE;\r\n}\r\n\r\n<span style=\"border: solid 1px currentcolor; border-bottom: none;\">const WCHAR c_szText[] = L\"hello\";                            <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">HANDLE                                                        <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">OnRenderFormat(HWND hwnd, UINT fmt)                           <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> if (fmt == CF_UNICODETEXT)                                   <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> {                                                            <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">  HGLOBAL hglob;                                              <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">  if (SUCCEEDED(<a title=\"What a drag: Dragging text\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20080311-00\/?p=23153\">CreateHGlobalFromBlob<\/a>(                        <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">                            c_szText, sizeof(c_szText),       <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">                            GMEM_MOVEABLE, &amp;hglob))) {        <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">   if (!SetClipboardData(fmt, hglob)) GlobalFree(hglob);      <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">  }                                                           <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> }                                                            <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> return 0;                                                    <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">}                                                             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">void                                                          <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">OnRenderAllFormats(HWND hwnd)                                 <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> if (OpenClipboard(hwnd)) {                                   <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">  <i>OnRenderFormat(hwnd, CF_UNICODETEXT);<\/i>                       <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">  CloseClipboard();                                           <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\"> }                                                            <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">}                                                             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                             <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    HANDLE_MSG(hwnd, WM_RENDERFORMAT, OnRenderFormat);        <\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">    HANDLE_MSG(hwnd, WM_RENDERALLFORMATS, OnRenderAllFormats);<\/span>\r\n<\/pre>\n<p>This program puts delay-rendered text on the clipboard <a title=\"Why can programs empty the clipboard when they start up?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20100510-00\/?p=14093\"> when it starts up<\/a>, When the request for text arrives, we just return the word <tt>hello<\/tt>. If we are asked to render all our formats, we render all our formats by calling our internal function once for each format we support. (All one of them.)<\/p>\n<p>There&#8217;s a tiny race condition in that implementation above, though. What if somebody takes ownership of the clipboard <i>while you&#8217;re trying to render all your formats<\/i>? Let&#8217;s force the race condition. Set a breakpoint on the <code>On\u00adRender\u00adAll\u00adFormats<\/code> function, run the program, and close the window. The breakpoint will hit.<\/p>\n<p>Switch away from the debugger and open Notepad. Type <tt>123<\/tt> into Notepad, then select it and type <kbd>Ctrl<\/kbd>+<kbd>C<\/kbd> to copy it to the clipboard.<\/p>\n<p>Notepad will hang for a while, since the window manager is trying to send a <code>WM_<wbr \/>DESTROY\u00adCLIPBOARD<\/code> message to tell the previous clipboard owner that it is no longer responsible for the data on the clipboard. Let the call time out, at which point Notepad will wake back up and put <tt>123<\/tt> text on the clipboard. Now resume execution of the scratch program, so that it puts the Unicode word <tt>hello<\/tt> onto the clipboard.<\/p>\n<p>Okay, go back to Notepad and hit <kbd>Ctrl<\/kbd>+<kbd>V<\/kbd>. Look, it pasted <tt>hello<\/tt> instead of <tt>123<\/tt>. Oops, our delay-rendering program destroyed the clipboard as it exited. If the application had put something more complicated on the clipboard, then our scratch program would have created a mishmash of old and new data.<\/p>\n<p>To protect against this race condition, make the following small change:<\/p>\n<pre>void\r\nOnRenderAllFormats(HWND hwnd)\r\n{\r\n if (OpenClipboard(hwnd)) {\r\n  <span style=\"border: solid 1px currentcolor;\">if (GetClipboardOwner() == hwnd) {<\/span>\r\n    OnRenderFormat(hwnd, CF_UNICODETEXT);\r\n  <span style=\"border: solid 1px currentcolor;\">}                                 <\/span>\r\n  CloseClipboard();\r\n }\r\n}\r\n<\/pre>\n<p>After opening the clipboard, we check if we are still the window responsible for the clipboard contents. Only if so do we render our delay-rendered formats.<\/p>\n<p><b>Exercise<\/b>: Why is the <code>Get\u00adClipboard\u00adOwner<\/code> test done <i>after<\/i> the <code>Open\u00adClipboard<\/code>? Wouldn&#8217;t it be better to bail out quickly if we are not the clipboard owner and avoid opening the clipboard in the first place?<\/p>\n<p><b>Answer to exercise<\/b>: Moving the <code>Get\u00adClipboard\u00adOwner<\/code> test ahead of the <code>Open\u00adClipboard<\/code> reintroduces the race condition we are trying to fix. The program might be the clipboard owner at the start of the function, but then it gets pre-empted after <code>Get\u00adClipboard\u00adOwner<\/code> and before <code>Open\u00adClipboard<\/code> and takes a long time to page in; say, because it&#8217;s running over the network or some other slow medium. During that time, the same scenario occurs that we are trying to defend against.<\/p>\n<p>But all is not lost. We can do a <code>Get\u00adClipboard\u00adOwner<\/code> as an early-out, but do another one after the <code>Open\u00adClipboard<\/code> just to be sure.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Contradictory documentation.<\/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-5763","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Contradictory documentation.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/5763","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=5763"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/5763\/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=5763"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=5763"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=5763"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}