{"id":111792,"date":"2025-11-14T07:00:00","date_gmt":"2025-11-14T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111792"},"modified":"2025-11-14T07:48:27","modified_gmt":"2025-11-14T15:48:27","slug":"20251114-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251114-00\/?p=111792","title":{"rendered":"I can use <CODE>WM_<WBR>COPY<WBR>DATA<\/CODE> to send a block of data to another window, but how does it send data back?"},"content":{"rendered":"<p>The <code>WM_<wbr \/>COPY\u00adDATA<\/code> message can be used to send a blob of data from one window to another. The window manager does the work of copying the data from the sending process to the receiving process, but how does the receiving process send data back?<\/p>\n<p>If the only information that needs to come back is a success\/failure, the recipient can return <code>TRUE<\/code> on success or <code>FALSE<\/code> on failure.<\/p>\n<p>But if you need to return more information, then you have a few choices.<\/p>\n<p>One is to have the receiving window send the results back to the sending window by sending the <code>WM_<wbr \/>COPY\u00adDATA<\/code> message back to the sending window. (The sending window passes its handle in the <code>wParam<\/code>.) The data blob can contain a transaction ID or some other way to distinguish which <code>WM_<wbr \/>COPY\u00adDATA<\/code> the recipient is responding to.<\/p>\n<p>Another way is for the sending window to create a shared memory block, duplicate the shared handle into the receiving window&#8217;s process,\u00b9 and then pass the duplicated handle in the <code>WM_<wbr \/>COPY\u00adDATA<\/code> payload. The receiving window can use <code>Map\u00adView\u00adOf\u00adFile<\/code> to access the shared memory block and write its results there. Of course, if you&#8217;re going to do it this way, then you don&#8217;t really need <code>WM_<wbr \/>COPY\u00adDATA<\/code>; you can just use a custom message and pass the handle in, say, the <code>wParam<\/code>.<\/p>\n<p>A customer said that if they created a shared memory block with <code>Create\u00adFile\u00adMapping<\/code>, they were worried because memory would become visible to all other processes, not just the two processes trying to talk to each other.<\/p>\n<p>Maybe they were thinking about <i>named<\/i> shared memory blocks, which are accessible to anybody who knows (or can guess) the name, and for whom access is granted by the shared memory block&#8217;s access control list.<\/p>\n<p>So don&#8217;t use a named shared memory block. Use an anonymous one. The only way to get access to an anonymous shared memory block is to get access to its handle.<\/p>\n<p>So your exposure is not to <i>all<\/i> processes but just processes which have &#8220;duplicate handle&#8221; permission. And somebody has &#8220;duplicate handle&#8221; permission on your process, then they already pwn your process: They can duplicate the <code>GetCurrentProcess()<\/code> handle out of your process, and that gives them a handle with full access to your process. Your exposure is only to people who are already on the other side of the airtight hatchway.<\/p>\n<p>\u00b9 This assumes that the sending process is running at equal or higher integrity level than the recipient. If the roles are reversed, with a low integrity process sending to a high integrity process, you can delegate the duplication to the recipient. The low integrity sending process allocates the shared memory and puts the handle into the <code>WM_<wbr \/>COPY\u00adDATA<\/code> memory block. The recipient can then call <code>Duplicate\u00adHandle<\/code> function to duplicate the handle out of the sending process, using <code>Get\u00adWindow\u00adThread\u00adProcess\u00adId<\/code> to get the sender&#8217;s process ID. You can include information in the <code>WM_<wbr \/>COPY\u00adDATA<\/code> memory block to indicate that you are in this reverse case.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>They can send it back with their own <CODE>WM_<WBR>COPY&shy;DATA<\/CODE> message, or they can put it in an agreed-upon shared location.<\/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":[],"class_list":["post-111792","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing"],"acf":[],"blog_post_summary":"<p>They can send it back with their own <CODE>WM_<WBR>COPY&shy;DATA<\/CODE> message, or they can put it in an agreed-upon shared location.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111792","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=111792"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111792\/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=111792"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111792"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111792"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}