{"id":106778,"date":"2022-06-23T07:00:00","date_gmt":"2022-06-23T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106778"},"modified":"2022-06-23T06:22:50","modified_gmt":"2022-06-23T13:22:50","slug":"20220623-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220623-00\/?p=106778","title":{"rendered":"Writing a marshal-by-value marshaler, part 2"},"content":{"rendered":"<p>When we last left our marshal-by-value marshaler, we had it marshal by value for all cases that didn&#8217;t leave the computer. But it turns out that this is actually more work than necessary: If the object is immutable (as marshal-by-value objects generally are) and is staying inside the same process, then we can just use the free-threaded marshaler, assuming the immutable state does not have thread affinity.<\/p>\n<p>The idea here is that immutable objects are pretty much free-threaded already, seeing as they have no mutable state that requires synchronization. So there&#8217;s no need to create a copy when marshaling between apartments within the same process; we can just have all the apartments access the object directly.<\/p>\n<p>Old and busted:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center; border: solid 1px black;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>client<\/td>\n<td style=\"border-right: dashed 1px black;\" rowspan=\"3\">\u00a0<\/td>\n<td rowspan=\"3\">\u00a0<\/td>\n<td>client<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td><span style=\"border: solid 1px black;\">data<\/span><\/td>\n<td><span style=\"border: solid 1px black;\">data<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>New hotness:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center; border: solid 1px black;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>client<\/td>\n<td style=\"border-right: dashed 1px black;\" rowspan=\"3\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td>client<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>\u2199\ufe0e<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td><span style=\"border: solid 1px black;\">data<\/span><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Clients within the same process don&#8217;t need a separate copy of the object. They can just share the object.<\/p>\n<p>Of course, clients from other processes can take advantage of having their own copy, since that would avoid inter-process calls to access the data. However, we permit this only for other processes on the same computer, because processes on other computers may not have our custom marshaler installed.<\/p>\n<p>This means that we want to treat each of the three categories differently:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Category<\/th>\n<th>Desired marshaler<\/th>\n<\/tr>\n<tr>\n<td>Same-process<\/td>\n<td>Free-threaded marshaler<\/td>\n<\/tr>\n<tr>\n<td>Same-machine<\/td>\n<td>Marshal-by-value marshaler<\/td>\n<\/tr>\n<tr>\n<td>Cross-machine<\/td>\n<td>Standard (marshal-by-reference) marshaler<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>It sounds like we&#8217;re going to have three cases to deal with, but we can collapse it down to two by realizing that the free-threaded marshaler falls back to the standard marshaler when the destination context is cross-machine.<\/p>\n<pre>bool ShouldMarshalByValue(DWORD dwDestContext)\r\n{\r\n    return <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">dwDestContext == MSHCTX_CROSSCTX || dwDestContext == MSHCTX_INPROC ||<\/span><\/span>\r\n           dwDestContext == MSHCTX_LOCAL || dwDestContext == MSHCTX_NOSHAREDMEM;\r\n}\r\n<\/pre>\n<p>We no longer marshal by value in the same-process case, letting the free-threaded marshaler take care of that and the cross-machine case.<\/p>\n<p>None of our marshal-by-value business logic needs to change. What changes is our fallback marshaler. Instead of falling back to the standard marshaler, we fall back to the free-threaded marshaler.<\/p>\n<pre>    STDMETHODIMP GetUnmarshalClass(\r\n        REFIID riid, void* pv, DWORD dwDestContext,\r\n        void* pvDestContext, DWORD mshlflags,\r\n        CLSID *clsid)\r\n    {\r\n        if (ShouldMarshalByValue(dwDestContext)) {\r\n            *clsid = CLSID_MyClass;\r\n            return S_OK;\r\n        }\r\n\r\n        ComPtr&lt;IMarshal&gt; marshal;\r\n        RETURN_IF_FAILED(<span style=\"color: blue;\">CoCreateFreeThreadedMarshaler(nullptr, &amp;marshal)<\/span>);\r\n        RETURN_IF_FAILED(marshal-&gt;GetUnmarshalClass(riid, pv, dwDestContext,\r\n                                              pvDestContext, mshlflags, clsid));\r\n        return S_OK;\r\n    }\r\n<\/pre>\n<p>Identical one-line changes apply to <code>Get\u00adMarshal\u00adSize\u00adMax<\/code> and <code>Marshal\u00adInterface<\/code>; I won&#8217;t write them out.<\/p>\n<p>Next time, we&#8217;ll apply all that we learned to diagnosing a reference counting bug related to marshaling.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Maybe you don&#8217;t need a copy on the other side.<\/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-106778","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Maybe you don&#8217;t need a copy on the other side.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106778","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=106778"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106778\/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=106778"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106778"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106778"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}