Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p><strong>EDITED</strong>, according to <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb773831%28v=vs.85%29.aspx" rel="nofollow">MSDN</a>:</p> <blockquote> <p>Thread safety. The stream created by SHCreateMemStream is thread-safe as of Windows 8. On earlier systems, the stream is not thread-safe. <strong>The stream created by CreateStreamOnHGlobal is thread-safe</strong>.</p> </blockquote> <p>I've got two opposite answers, so I decided to verify it. <strike>It looks like @HansPassant is right, and @IInspectable is wrong. At least, a proxy is created on another thread for the original <code>IStream</code> object.</strike></p> <p>The test case shows the following:</p> <ul> <li><p>Even if both threads belong to different apartments, a direct reference to <code>IStream</code> can still be used across threads. It just works.</p></li> <li><p>If both threads are MTA threads, the <code>IStream</code> from <code>thread</code> gets unmarshaled on <code>thread2</code> to exactly the same <code>IUnknown</code> pointer, no proxy.</p></li> <li><p>If <code>thread1</code> is STA, and the <code>thread2</code> is MTA, there is a proxy. But the direct reference, created on <code>thread</code>, still works on <code>thread2</code>.</p></li> </ul> <p>Note how reads and writes are performed on <code>stream1</code> concurrently, inside tight loops from different threads. Of course, it makes very little sense, normally there are locks to synchronize reads/writes. Yet, <strong>it proves that <code>IStream</code> object as returned by <code>CreateStreamOnHGlobal</code> is truly thread-safe.</strong></p> <p>I am not sure if that is an official COM requirement to <em>any</em> <code>IStream</code> implementation, as <a href="http://www.drdobbs.com/marshaling-com-interfaces/184416838" rel="nofollow">that Dr. Dobb's article</a> suggests - <strong>most likely it is just specific to <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa378980%28v=vs.85%29.aspx" rel="nofollow"><code>CreateStreamOnHGlobal</code></a></strong>.</p> <pre><code>using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; namespace ConsoleApplication1 { class Program { static void TestStream() { // start thread1 var thread1 = new Thread(() =&gt; { // create stream1 on thread1 System.Runtime.InteropServices.ComTypes.IStream stream1; CreateStreamOnHGlobal(IntPtr.Zero, true, out stream1); IntPtr unkStream1 = Marshal.GetIUnknownForObject(stream1); // marshal stream1, to be unmarshalled on thread2 Guid iid = typeof(System.Runtime.InteropServices.ComTypes.IStream).GUID; System.Runtime.InteropServices.ComTypes.IStream marshallerStream; CoMarshalInterThreadInterfaceInStream(ref iid, stream1, out marshallerStream); // write to stream1 var buf1 = new byte[] { 1, 2, 3, 4 }; stream1.Write(buf1, buf1.Length, IntPtr.Zero); // start thread2 var thread2 = new Thread(() =&gt; { // read from stream1 (the direct reference) on thread2 var buf2 = new byte[buf1.Length]; for (var i = 0; i &lt; 10000; i++) { stream1.Seek(0, 0, IntPtr.Zero); stream1.Read(buf2, buf2.Length, IntPtr.Zero); // trule thread safe, this always works! for (var j = 0; j &lt; buf2.Length; j++) Debug.Assert(buf1[j] == buf2[j]); } // Unmarshal and compare IUnknown pointers object stream2; CoGetInterfaceAndReleaseStream(marshallerStream, ref iid, out stream2); IntPtr unkStream2 = Marshal.GetIUnknownForObject(stream2); // Bangs if thread1 is STA, works OK if thread1 is MTA Debug.Assert(unkStream1 == unkStream2); Marshal.Release(unkStream2); }); for (var i = 0; i &lt; 10000; i++) { stream1.Seek(0, 0, IntPtr.Zero); stream1.Write(buf1, buf1.Length, IntPtr.Zero); } thread2.SetApartmentState(ApartmentState.MTA); thread2.Start(); thread2.Join(); Marshal.Release(unkStream1); }); thread1.SetApartmentState(ApartmentState.STA); thread1.Start(); thread1.Join(); } static void Main(string[] args) { TestStream(); } [DllImport("ole32.dll", PreserveSig = false)] public static extern void CreateStreamOnHGlobal( IntPtr hGlobal, bool fDeleteOnRelease, [Out] out System.Runtime.InteropServices.ComTypes.IStream pStream); [DllImport("ole32.dll", PreserveSig = false)] public static extern void CoMarshalInterThreadInterfaceInStream( [In] ref Guid riid, [MarshalAs(UnmanagedType.IUnknown)] object unk, out System.Runtime.InteropServices.ComTypes.IStream stream); [DllImport("ole32.dll", PreserveSig = false)] public static extern void CoGetInterfaceAndReleaseStream( [In] System.Runtime.InteropServices.ComTypes.IStream stream, [In] ref Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object unk); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload