{"id":105252,"date":"2021-05-26T07:00:00","date_gmt":"2021-05-26T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105252"},"modified":"2021-05-26T07:14:03","modified_gmt":"2021-05-26T14:14:03","slug":"20210526-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210526-00\/?p=105252\/","title":{"rendered":"How ownership of the Windows clipboard is tracked in Win32"},"content":{"rendered":"<p>In Win32, there is the concept of a clipboard <i>owner<\/i>. How does that work?<\/p>\n<p>The intended rule is that the clipboard owner is the window that created the data that is currently on the clipboard.<\/p>\n<p>The intended usage pattern for putting data on the clipboard is<\/p>\n<ul>\n<li>Call <code>Open\u00adClipboard(hwnd)<\/code> passing the window that you want to become the new clipboard owner. (For the purpose of discussion, let&#8217;s say that the <i>clipboard opener<\/i> is the window handle that was passed to the <code>Open\u00adClipboard<\/code> function.)<\/li>\n<li>Call <code>Empty\u00adClipboard()<\/code> to wipe out the previous contents.<\/li>\n<li>Call <code>Set\u00adClipboard\u00adData()<\/code> once for each piece of data you want to put onto the clipboard.<\/li>\n<li>Call <code>Close\u00adClipboard()<\/code> to indicate that you are done setting the clipboard data.<\/li>\n<li>Congratulations, you are now the clipboard owner.<\/li>\n<\/ul>\n<p>The clipboard owner receives a <code>WM_<wbr \/>RENDER\u00adFORMAT<\/code> message when somebody requests data from the clipboard that had been set as delay-rendered. It also receives a <code>WM_<wbr \/>RENDER\u00adALL\u00adFORMATS<\/code> message as part of the window destruction sequence if it is still the owner of the clipboard at the time it is destroyed. Delay-rendering lets you defer the creation of complicated clipboard data until the point it is requested. And if nobody ever requests it, then you saved yourself a lot of work.<\/p>\n<p><b>Bonus reading<\/b>: <a title=\"What is the proper handling of WM_RENDERFORMAT and WM_RENDERALLFORMATS?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20121224-00\/?p=5763\"> What is the proper handling of <code>WM_<wbr \/>RENDER\u00adFORMAT<\/code> and <code>WM_<wbr \/>RENDER\u00adALL\u00adFORMATS<\/code><\/a>?<\/p>\n<p>The clipboard owner also receives a <code>WM_<wbr \/>DESTROY\u00adCLIPBOARD<\/code> message when the clipboard contents are subsequently emptied. This tells the clipboard owner that it can free any memory that it had been using to produce the delay-rendered data.<\/p>\n<p>The intended usage pattern for reading data from the clipboard is<\/p>\n<ul>\n<li>Call <code>Open\u00adClipboard(hwnd)<\/code> passing the window that is reading the clipboard.<\/li>\n<li>Call <code>Get\u00adClipboard\u00adData()<\/code> to retrieve data from the clipboard.<\/li>\n<li>Call <code>Close\u00adClipboard()<\/code> to indicate that you are done reading the clipboard data.<\/li>\n<\/ul>\n<p>If everybody follows the rules, then it all works out.<\/p>\n<p>Spoiler alert: Not everybody follows the rules.<\/p>\n<p>Some programs open the clipboard with the intent of <i>adding<\/i> data to the clipboard, rather than replacing it with new data. Those programs call <code>Open\u00adClipboard<\/code>, followed immediately by <code>Set\u00adClipboard\u00adData<\/code> without an intervening <code>Empty\u00adClipboard<\/code>.<\/p>\n<p>There was historically no enforcement that the caller of <code>Set\u00adClipboard\u00adData<\/code> is the clipboard owner. Back in the days of 16-bit Windows, the system assumed that applications were honest and played by the rules for the common good. (And for compatibility, these shenanigans need to continue to work, even though the world has become a more dangerous place.)<\/p>\n<p>This &#8220;bonus clipboard data&#8221; scenario creates a bit of a problem, since there is only one clipboard owner (which you can interrogate by calling <code>Get\u00adClipboard\u00adOwner()<\/code>), but there are now <i>two<\/i> windows who collaborated to put data onto the clipboard. Who is the owner?<\/p>\n<p>Ownership of the clipboard changes as follows:<\/p>\n<ul>\n<li>When <code>Empty\u00adClipboard()<\/code> is called, the current clipboard opener becomes the clipboard owner.<\/li>\n<li>When the clipboard owner is destroyed, the clipboard owner resets to null.<\/li>\n<\/ul>\n<p>Therefore, the clipboard owner can be summarized as &#8220;the window that most recently called <code>Empty\u00adClipboard<\/code>, if it still exists.&#8221;<\/p>\n<p><b>Bonus chatter<\/b>: There is also special handling of the case where somebody passes <code>NULL<\/code> to <code>Open\u00adClipboard<\/code>, indicating that &#8220;nobody&#8221; is opening the clipboard.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A simple model, complicated by the reality of misuse.<\/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-105252","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A simple model, complicated by the reality of misuse.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105252","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=105252"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105252\/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=105252"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105252"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105252"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}