{"id":27533,"date":"2007-03-22T10:00:00","date_gmt":"2007-03-22T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2007\/03\/22\/excursions-in-composition-sequential-stream-concatenation\/"},"modified":"2007-03-22T10:00:00","modified_gmt":"2007-03-22T10:00:00","slug":"excursions-in-composition-sequential-stream-concatenation","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20070322-00\/?p=27533","title":{"rendered":"Excursions in composition: Sequential stream concatenation"},"content":{"rendered":"<p>\nAs we&#8217;ve seen a few times already\n(when\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/10\/07\/239197.aspx\">\nbuilding context menus<\/a> and exploring\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2005\/01\/04\/346274.aspx\">\nfiber-based enumeration<\/a>),\ncomposition is an important concept in object-oriented programming.\nToday, we&#8217;re going to compose two sequential streams by concatenation.\n<\/p>\n<p>\nThere really isn&#8217;t much to it.\nThe idea is to take two streams and start by reading\nfrom the first one.\nWhen that runs out, switch to reading from the second one.\nMost of this is just typing.\n(As usual, I am using plain C++;\nin real life, you can save yourselves a lot of typing by\nusing a class library of your choosing.)\n<\/p>\n<p>\nWe&#8217;ll start with a base class that does all the boring typing\nrelated to implementing a read-only sequential stream.\n<\/p>\n<pre>\nclass CROSequentialStreamBase : public ISequentialStream\n{\npublic:\n \/\/ *** IUnknown ***\n STDMETHODIMP QueryInterface(REFIID riid, void **ppv)\n {\n  if (riid == IID_IUnknown || riid == IID_ISequentialStream) {\n    *ppv = static_cast&lt;IUnknown*&gt;(this);\n    AddRef();\n    return S_OK;\n  }\n  *ppv = NULL;\n  return E_NOINTERFACE;\n }\n STDMETHODIMP_(ULONG) AddRef()\n {\n  return InterlockedIncrement(&amp;m_cRef);\n }\n STDMETHODIMP_(ULONG) Release()\n {\n  LONG cRef = InterlockedDecrement(&amp;m_cRef);\n  if (cRef == 0) delete this;\n  return cRef;\n }\n \/\/ *** ISequentialStream ***\n STDMETHODIMP Write(const void *pv, ULONG cb, ULONG *pcbWritten)\n {\n  if (pcbWritten) *pcbWritten = 0;\n  return STG_E_ACCESSDENIED;\n }\nprotected:\n CROSequentialStreamBase() : m_cRef(1) { }\n virtual ~CROSequentialStreamBase() { }\n LONG m_cRef;\n};\n<\/pre>\n<p>\nThere&#8217;s nothing exciting here at all.\nIn addition to the boring <code>IUnknown<\/code> methods,\nwe also implement <code>ISequentialStream::Write<\/code>\nby saying, &#8220;Sorry, you can&#8217;t write to a read-only stream.&#8221;\nThe <code>ISequentialStream::Read<\/code> method\nis left unimplemented.\n<\/p>\n<p>\nWe can now cook up our\n<code>CConcatStream<\/code> class:\n<\/p>\n<pre>\nclass CConcatStream : public CROSequentialStreamBase\n{\npublic:\n CConcatStream(ISequentialStream *pstm1,\n               ISequentialStream *pstm2);\n \/\/ *** ISequentialStream ***\n STDMETHODIMP Read(void *pv, ULONG cb, ULONG *pcbRead);\nprotected:\n ~CConcatStream();\n bool m_fFirst;\n ISequentialStream *m_pstm1;\n ISequentialStream *m_pstm2;\n};\nCConcatStream::CConcatStream(ISequentialStream *pstm1,\n                             ISequentialStream *pstm2)\n : m_pstm1(pstm1), m_pstm2(pstm2), m_fFirst(true)\n{\n assert(pstm1 != pstm2);\n m_pstm1-&gt;AddRef();\n m_pstm2-&gt;AddRef();\n}\nCConcatStream::~CConcatStream()\n{\n m_pstm1-&gt;Release();\n m_pstm2-&gt;Release();\n}\n<\/pre>\n<p>\nOur <code>CConcatStream<\/code> takes two sequential streams\nand saves them in member variables\n<code>m_pstm1<\/code> and <code>m_pstm2<\/code>.\nThe real work happens in\n<code>ISequentialStream::Read<\/code>\nmethod:\n<\/p>\n<pre>\nHRESULT CConcatStream::Read(void *pv, ULONG cb, ULONG *pcbRead)\n{\n ULONG cbRead;\n HRESULT hr;\n if (m_fFirst) {\n  hr = m_pstm1-&gt;Read(pv, cb, &amp;cbRead);\n } else {\n  hr = m_pstm2-&gt;Read(pv, cb, &amp;cbRead);\n }\n if ((FAILED(hr) || cbRead == 0) &amp;&amp; m_fFirst) {\n  m_fFirst = false;\n  hr = m_pstm2-&gt;Read(pv, cb, &amp;cbRead);\n }\n if (pcbRead) *pcbRead = cbRead;\n return hr;\n}\n<\/pre>\n<p>\nIf we are still reading the first stream, then read from the first\nstream.\nOtherwise, read from the second stream.\nIf the first stream reaches the end, then switch to the second stream.\n(Checking whether the end of the stream has been reached is very\nannoying since <code>ISequentialStream<\/code>\nimplementations are inconsistent in the way they report\nthe condition.\nSome return <code>S_FALSE<\/code> on a\npartial read;\nothers return <code>S_OK<\/code>;\nstill others return an error code.\nWe need to check for all of these possibilities.)\n<\/p>\n<p>\nAnd that&#8217;s all there is.\nIf you give this object two sequential streams, it will compose\nthose two streams and act like one giant stream that is the\nconcatenation of the two.\n<\/p>\n<p>\nLet&#8217;s illustrate with a simple program:\n<\/p>\n<pre>\n#include &lt;stdio.h&gt;\n#include &lt;windows.h&gt;\n#include &lt;ole2.h&gt;\n#include &lt;assert.h&gt;\n#include &lt;shlwapi.h&gt;\n#include &lt;tchar.h&gt;\n... insert CConcatStream class here ...\nvoid PrintStream(ISequentialStream *pstm)\n{\n ULONG cb;\n BYTE buf[256];\n while (SUCCEEDED(pstm-&gt;Read(buf, 255, &amp;cb)) &amp;&amp; cb) {\n  buf[cb] = 0;\n  printf(\"%s\", buf);\n }\n}\nint __cdecl _tmain(int argc, TCHAR **argv)\n{\n if(SUCCEEDED(CoInitialize(NULL)) {\n  IStream *pstm1;\n  if (SUCCEEDED(SHCreateStreamOnFile(argv[1], STGM_READ, &amp;pstm1))) {\n   IStream *pstm2;\n   if (SUCCEEDED(SHCreateStreamOnFile(argv[2], STGM_READ, &amp;pstm2))) {\n    CConcatStream *pstm = new CConcatStream(pstm1, pstm2);\n    if (pstm) {\n     PrintStream(pstm);\n     pstm-&gt;Release();\n    }\n    pstm2-&gt;Release();\n   }\n   pstm1-&gt;Release();\n  }\n  CoUninitialize();\n }\n return 0;\n}\n<\/pre>\n<p>\nThis program takes two file names on the command line and\ncreates a stream for each one,\nthen creates a <code>CConcatStream<\/code> out of them both,\nresulting in a composite stream that produces the contents of\nthe first file, followed by the contents of the second file.\nWhen you run this program, you should see both files printed\nto the screen, one after the other.\n<\/p>\n<p>\nOkay, there really wasn&#8217;t much going on here,\nbut we&#8217;ll use this as groundwork for next time.\n<\/p>\n<p>\nExercise: What is the significance of the assertion\nin the constructor?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As we&#8217;ve seen a few times already (when building context menus and exploring fiber-based enumeration), composition is an important concept in object-oriented programming. Today, we&#8217;re going to compose two sequential streams by concatenation. There really isn&#8217;t much to it. The idea is to take two streams and start by reading from the first one. When [&hellip;]<\/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-27533","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>As we&#8217;ve seen a few times already (when building context menus and exploring fiber-based enumeration), composition is an important concept in object-oriented programming. Today, we&#8217;re going to compose two sequential streams by concatenation. There really isn&#8217;t much to it. The idea is to take two streams and start by reading from the first one. When [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/27533","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=27533"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/27533\/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=27533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=27533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=27533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}