{"id":106767,"date":"2022-06-21T07:00:00","date_gmt":"2022-06-21T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106767"},"modified":"2024-01-24T15:13:33","modified_gmt":"2024-01-24T23:13:33","slug":"20220621-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220621-00\/?p=106767","title":{"rendered":"Writing a compound marshaler"},"content":{"rendered":"<p>We left off our discussion of marshaling with a discussion of the recursive nature of marshaling. Let&#8217;s demonstrate with a simple object that in turn contains other objects.<\/p>\n<pre>class CompoundObject : public IMarshal \/* ... and other interfaces *\/\r\n{\r\npublic:\r\n  \/\/ QueryInterface, AddRef, and Release left as an exercise\r\n\r\nprivate:\r\n  int32_t m_value;\r\n  ComPtr&lt;IThing&gt; thing;\r\n};\r\n<\/pre>\n<p>Maybe you decide that this object should be marshaled by shallow copy, so you want to copy the <code>int32_t<\/code> and copy the reference to the <code>thing<\/code>. Therefore, the marshal size is <code>sizeof(m_value)<\/code> plus whatever the marshal size of <code>thing<\/code> turns out to be.\u00b9<\/p>\n<pre>  STDMETHODIMP GetMarshalSizeMax(\r\n    REFIID riid, void* pv, DWORD dwDestContext,\r\n    void* pvDestContext, DWORD mshlflags,\r\n    LPDWORD size)\r\n  {\r\n    if (ShouldMarshalByValue(dwDestContext)) {\r\n      <span style=\"border: solid 1px currentcolor; border-bottom: none;\">DWORD thingSize;                                                                    <\/span>\r\n      <span style=\"border: 1px currentcolor; border-style: none solid;\">RETURN_IF_FAILED(CoGetMarshalSizeMax(&amp;thingSize, __uuidof(thing.Get()), thing.Get(),<\/span>\r\n      <span style=\"border: 1px currentcolor; border-style: none solid;\">                                     dwDestContext, pvDestContext, mshlflags));     <\/span>\r\n      <span style=\"border: solid 1px currentcolor; border-top: none;\">*size = sizeof(m_value) + thingSize;                                                <\/span>\r\n      return S_OK;\r\n    }\r\n\r\n    ComPtr&lt;IMarshal&gt; marshal;\r\n    RETURN_IF_FAILED(CoGetStandardMarshal(riid, CastToUnknown(), dwDestContext,\r\n                                          pvDestContext, mshlflags, &amp;marshal));\r\n    RETURN_IF_FAILED(marshal-&gt;GetMarshalSizeMax(riid, pv, dwDestContext,\r\n                                          pvDestContext, mshlflags, size));\r\n    return S_OK;\r\n  }\r\n<\/pre>\n<p>Marshaling the interface copies the integer and then marshals the <code>thing<\/code>:<\/p>\n<pre>  STDMETHODIMP MarshalInterface(\r\n    IStream* pstm,\r\n    REFIID riid, void* pv, DWORD dwDestContext,\r\n    void* pvDestContext, DWORD mshlflags)\r\n  {\r\n    if (ShouldMarshalByValue(dwDestContext)) {\r\n      RETURN_IF_FAILED(pstm-&gt;Write(&amp;m_value, sizeof(m_value), nullptr));\r\n      <span style=\"border: solid 1px currentcolor; border-bottom: none;\">return CoMarshalInterface(pstm, __uuidof(thing.Get()), thing.Get(),<\/span>\r\n      <span style=\"border: solid 1px currentcolor; border-top: none;\">                          dwDestContext, pvDestContext, mshlflags);<\/span>\r\n    }\r\n\r\n    ComPtr&lt;IMarshal&gt; marshal;\r\n    RETURN_IF_FAILED(CoGetStandardMarshal(riid, CastToUnknown(), dwDestContext,\r\n                                          pvDestContext, mshlflags, &amp;marshal));\r\n    RETURN_IF_FAILED(marshal-&gt;MarshalInterface(pstm, riid, pv, dwDestContext,\r\n                                          pvDestContext, mshlflags));\r\n    return S_OK;\r\n  }\r\n<\/pre>\n<p>Unmarshaling the interface recovers the integer and then unmarshals the <code>thing<\/code>:<\/p>\n<pre>  STDMETHODIMP UnmarshalInterface(IStream* pstm, REFIID riid, void** ppv)\r\n  {\r\n    *ppv = nullptr;\r\n    ULONG actual;\r\n    RETURN_IF_FAILED(pstm-&gt;Read(&amp;m_value, sizeof(m_value), &amp;actual));\r\n    RETURN_HR_IF(E_FAIL, actual != sizeof(m_value));\r\n    <span style=\"border: solid 1px currentcolor;\">RETURN_IF_FAILED(CoUnmarshalInterface(pstm, IID_PPV_ARGS(&amp;thing));<\/span>\r\n    return QueryInterface(riid, ppv);\r\n  }\r\n<\/pre>\n<p>And releasing the marshal data skips over the integer and then releases the marshal data for the <code>thing<\/code>:<\/p>\n<pre>  STDMETHODIMP ReleaseMarshalData(IStream* pstm)\r\n  {\r\n    RETURN_IF_FAILED(pstm-&gt;Seek({ sizeof(m_value), 0 }, STREAM_SEEK_CUR, nullptr);\r\n    <span style=\"border: solid 1px currentcolor;\">RETURN_IF_FAILED(CoReleaseMarshalData(pstm));<\/span>\r\n    return S_OK;\r\n  }\r\n<\/pre>\n<p>Each of the methods that operate on the marshal data must leave the stream pointer at the end of the current marshaler&#8217;s data, so that the next method can resume where the previous one left off.<\/p>\n<p>So far, we haven&#8217;t been using the <code>mshlflags<\/code>. That will come into play when our marshal data requires cleanup. We&#8217;ll investigate that next time.<\/p>\n<p>\u00b9 In practice, I probably would have avoided the temporary variable:<\/p>\n<pre>      RETURN_IF_FAILED(CoGetMarshalSizeMax(size, __uuidof(thing.Get()), thing.Get(),\r\n                                           dwDestContext, pvDestContext, mshlflags));\r\n      *size += sizeof(m_value);\r\n<\/pre>\n<p>For expository purposes, I calculated the size by calculate the size of each piece separately and adding them together at the end. This makes the code look a bit more consistent with the other cases that marshal and unmarshal the integer before the inner object.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>On the nature of streams.<\/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-106767","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>On the nature of streams.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106767","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=106767"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106767\/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=106767"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106767"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}