{"id":110411,"date":"2024-10-23T07:00:00","date_gmt":"2024-10-23T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110411"},"modified":"2024-10-23T10:02:42","modified_gmt":"2024-10-23T17:02:42","slug":"20241023-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241023-00\/?p=110411","title":{"rendered":"How do I create a Windows Runtime <CODE>IRandom&shy;Access&shy;Stream<\/CODE> around a bunch of bytes or a classic COM <CODE>IStream<\/CODE>?"},"content":{"rendered":"<p>There are some Windows Runtime methods which expect a random access stream in the form of a <code>IRandom\u00adAccess\u00adStream<\/code>. On the other hand, you might have your data in the form of a memory block. For example, you might have a memory block that represents a bitmap, and you want to wrap it in a <code>IRandom\u00adAccess\u00adStream<\/code> so you can use it as a <code>Svg\u00adImage\u00adSource<\/code>.<\/p>\n<p>One option is to create an <code>In\u00adMemory\u00adRandom\u00adAccess\u00adStream<\/code> and use the <code>Write\u00adAsync()<\/code> method to write the bytes (in the form of an <code>IBuffer<\/code>), then rewind the stream back to the start.<\/p>\n<pre>\/\/ C++\/WinRT\r\n\r\nwinrt::Buffer BufferFromBytes(winrt::array_view&lt;uint8_t&gt; bytes)\r\n{\r\n    winrt::Buffer buffer(bytes.size());\r\n    memcpy(buffer.data(), bytes.data(), bytes.size());\r\n    buffer.Length(bytes.size());\r\n    return buffer;\r\n}\r\n\r\nwinrt::IAsyncOperation&lt;winrt::IRandomAccessStream&gt;\r\n    BytesToRandomAccessStream(winrt::array_view&lt;uint8_t&gt; bytes)\r\n{\r\n    winrt::InMemoryRandomAccessStream stream;\r\n    co_await stream.WriteAsync(BufferFromBytes(bytes));\r\n\r\n    stream.Seek(0);\r\n    co_return stream;\r\n}\r\n<\/pre>\n<p>This is rather annoying because <code>WriteAsync()<\/code> is an async method, which means we have to <code>co_await<\/code> it, which in turn forces us to be a coroutine as well.<\/p>\n<p>But there&#8217;s a shortcut: <code>Create\u00adRandom\u00adAccess\u00adStream\u00adOver\u00adStream<\/code>.<\/p>\n<p>The <code>Create\u00adRandom\u00adAccess\u00adStream\u00adOver\u00adStream<\/code> function takes a classic COM <code>IStream<\/code> and creates a Windows Runtime <code>IRandom\u00adAccess\u00adStream<\/code> around it.<\/p>\n<p>So this will actually take two steps. First we need to put the bytes into an <code>IStream<\/code>. Then we can wrap the <code>IStream<\/code> inside an <code>IRandom\u00adAccess\u00adStream<\/code>.<\/p>\n<pre>\/\/ C++\/WinRT\r\n\r\nwinrt::com_ptr&lt;IStream&gt; StreamFromBytes(winrt::array_view&lt;uint8_t&gt; bytes)\r\n{\r\n    winrt::com_ptr&lt;IStream&gt; stream{\r\n        SHCreateMemStream(reinterpret_cast&lt;const BYTE*&gt;(bytes.data()),\r\n                          bytes.size()),\r\n        winrt::take_ownership_from_abi };\r\n    winrt::check_pointer(stream.get());\r\n    return stream;\r\n}\r\n\r\nwinrt::IRandomAccessStream\r\n    BytesToRandomAccessStream(winrt::array_view&lt;uint8_t&gt; bytes)\r\n{\r\n    return winrt::capture&lt;winrt::IRandomAccessStream&gt;(\r\n        CreateRandomAccessStreamOverStream,\r\n        StreamFromBytes(bytes).get(),\r\n        BSOS_DEFAULT);\r\n}\r\n<\/pre>\n<p>Bonus reading: <a title=\"The difference between assignment and attachment with ATL smart pointers\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20091120-00\/?p=15953\"> The difference between assignment and attachment with ATL smart pointers<\/a>, exacerbated by <code>SHCreate\u00adMem\u00adStream<\/code> not following standard COM patterns.<\/p>\n<p>For C#, <a href=\"https:\/\/learn.microsoft.com\/dotnet\/standard\/io\/how-to-convert-between-dotnet-streams-and-winrt-streams\"> somebody did part of the work for you<\/a>: <code>Windows\u00adRuntime\u00adStream\u00adExtensions.<wbr \/>As\u00adRandom\u00adAccess\u00adStream<\/code> converts a <code>System.<wbr \/>IO.<wbr \/>Stream<\/code> to a Windows Runtime <code>IRandom\u00adAccess\u00adStream<\/code>.<\/p>\n<pre>\/\/ C#\r\nIRandomAccessStream\r\n    BytesToRandomAccessStream(byte* bytes, long length)\r\n{\r\n    var stream = new UnmanagedMemoryStream(bytes, length);\r\n    return stream.AsRandomAccessStream();\r\n}\r\n<\/pre>\n<p>The above function looks simple, but it&#8217;s also dangerous because you don&#8217;t know when it&#8217;s safe to free the <code>bytes<\/code>. We can copy them into a <code>Memory\u00adStream<\/code> so that its lifetime is properly managed.<\/p>\n<pre>\/\/ C#\r\nMemoryStream\r\n    StreamFromBytes(byte* bytes, int length)\r\n{\r\n    var stream = new MemoryStream(length);\r\n    stream.Write(new ReadOnlySpan&lt;byte&gt;(bytes, length));\r\n    stream.Seek(0, SeekOrigin.Begin);\r\n    return stream;\r\n}\r\n\r\nIRandomAccessStream\r\n    BytesToRandomAccessStream(byte* bytes, int length)\r\n{\r\n    return StreamFromBytes(bytes, length).AsRandomAccessStream();\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Another wrapper function.<\/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-110411","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Another wrapper function.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110411","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=110411"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110411\/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=110411"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110411"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110411"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}