New tests, update
[mono.git] / mcs / class / System.Drawing / System.Drawing / ComIStreamMarshaler.cs
1 //
2 // System.Drawing.ComIStreamMarshaler.cs
3 //
4 // Author:
5 //   Kornél Pál <http://www.kornelpal.hu/>
6 //
7 // Copyright (C) 2005-2006 Kornél Pál
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 // Undefine to debug the protected blocks
32 #define MAP_EX_TO_HR
33
34 // Define to debug wrappers recursively
35 // #define RECURSIVE_WRAPPING
36
37 using System;
38 using System.IO;
39 using System.Reflection;
40 using System.Runtime.InteropServices;
41 #if NET_2_0
42 using System.Runtime.InteropServices.ComTypes;
43 using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
44 #else
45 using IStream = System.Runtime.InteropServices.UCOMIStream;
46 #endif
47
48 namespace System.Drawing
49 {
50         // Mono does not implement COM interface marshaling
51         // This custom marshaler should be replaced with UnmanagedType.Interface
52         // Provides identical behaviour under Mono and .NET Framework
53         internal sealed class ComIStreamMarshaler : ICustomMarshaler
54         {
55                 private const int S_OK = 0x00000000;
56                 private const int E_NOINTERFACE = unchecked((int)0x80004002);
57
58                 private delegate int QueryInterfaceDelegate(IntPtr @this, [In()] ref Guid riid, IntPtr ppvObject);
59                 private delegate int AddRefDelegate(IntPtr @this);
60                 private delegate int ReleaseDelegate(IntPtr @this);
61                 private delegate int ReadDelegate(IntPtr @this, [Out(), MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pv, int cb, IntPtr pcbRead);
62                 private delegate int WriteDelegate(IntPtr @this, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pv, int cb, IntPtr pcbWritten);
63                 private delegate int SeekDelegate(IntPtr @this, long dlibMove, int dwOrigin, IntPtr plibNewPosition);
64                 private delegate int SetSizeDelegate(IntPtr @this, long libNewSize);
65                 private delegate int CopyToDelegate(IntPtr @this, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(ComIStreamMarshaler))] IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten);
66                 private delegate int CommitDelegate(IntPtr @this, int grfCommitFlags);
67                 private delegate int RevertDelegate(IntPtr @this);
68                 private delegate int LockRegionDelegate(IntPtr @this, long libOffset, long cb, int dwLockType);
69                 private delegate int UnlockRegionDelegate(IntPtr @this, long libOffset, long cb, int dwLockType);
70                 private delegate int StatDelegate(IntPtr @this, out STATSTG pstatstg, int grfStatFlag);
71                 private delegate int CloneDelegate(IntPtr @this, out IntPtr ppstm);
72
73                 [StructLayout(LayoutKind.Sequential)]
74                 private sealed class IStreamInterface
75                 {
76                         internal IntPtr lpVtbl;
77                         internal IntPtr gcHandle;
78                 }
79
80                 [StructLayout(LayoutKind.Sequential)]
81                 private sealed class IStreamVtbl
82                 {
83                         internal QueryInterfaceDelegate QueryInterface;
84                         internal AddRefDelegate AddRef;
85                         internal ReleaseDelegate Release;
86                         internal ReadDelegate Read;
87                         internal WriteDelegate Write;
88                         internal SeekDelegate Seek;
89                         internal SetSizeDelegate SetSize;
90                         internal CopyToDelegate CopyTo;
91                         internal CommitDelegate Commit;
92                         internal RevertDelegate Revert;
93                         internal LockRegionDelegate LockRegion;
94                         internal UnlockRegionDelegate UnlockRegion;
95                         internal StatDelegate Stat;
96                         internal CloneDelegate Clone;
97                 }
98
99                 // Managed COM Callable Wrapper implementation
100                 // Reference counting is thread safe
101                 private sealed class ManagedToNativeWrapper
102                 {
103                         // Mono does not implement Marshal.Release
104                         [StructLayout(LayoutKind.Sequential)]
105                         private sealed class ReleaseSlot
106                         {
107                                 internal ReleaseDelegate Release;
108                         }
109
110                         private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046");
111                         private static readonly Guid IID_IStream = new Guid("0000000C-0000-0000-C000-000000000046");
112                         private static readonly MethodInfo exceptionGetHResult = typeof(Exception).GetProperty("HResult", BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding, null, typeof(int), new Type[] {}, null).GetGetMethod(true);
113                         // Keeps delegates alive while they are marshaled
114                         private static readonly IStreamVtbl managedVtable;
115                         private static IntPtr comVtable;
116                         private static int vtableRefCount;
117
118                         private IStream managedInterface;
119                         private IntPtr comInterface;
120                         // Keeps the object alive when it has no managed references
121                         private GCHandle gcHandle;
122                         private int refCount = 1;
123
124                         static ManagedToNativeWrapper()
125                         {
126                                 EventHandler onShutdown;
127                                 AppDomain currentDomain;
128                                 IStreamVtbl newVtable;
129
130                                 onShutdown = new EventHandler(OnShutdown);
131                                 currentDomain = AppDomain.CurrentDomain;
132                                 currentDomain.DomainUnload += onShutdown;
133                                 currentDomain.ProcessExit += onShutdown;
134
135                                 newVtable = new IStreamVtbl();
136                                 newVtable.QueryInterface = new QueryInterfaceDelegate(QueryInterface);
137                                 newVtable.AddRef = new AddRefDelegate(AddRef);
138                                 newVtable.Release = new ReleaseDelegate(Release);
139                                 newVtable.Read = new ReadDelegate(Read);
140                                 newVtable.Write = new WriteDelegate(Write);
141                                 newVtable.Seek = new SeekDelegate(Seek);
142                                 newVtable.SetSize = new SetSizeDelegate(SetSize);
143                                 newVtable.CopyTo = new CopyToDelegate(CopyTo);
144                                 newVtable.Commit = new CommitDelegate(Commit);
145                                 newVtable.Revert = new RevertDelegate(Revert);
146                                 newVtable.LockRegion = new LockRegionDelegate(LockRegion);
147                                 newVtable.UnlockRegion = new UnlockRegionDelegate(UnlockRegion);
148                                 newVtable.Stat = new StatDelegate(Stat);
149                                 newVtable.Clone = new CloneDelegate(Clone);
150                                 managedVtable = newVtable;
151
152                                 CreateVtable();
153                         }
154
155                         private ManagedToNativeWrapper(IStream managedInterface)
156                         {
157                                 IStreamInterface newInterface;
158
159                                 lock (managedVtable)
160                                 {
161                                         // Vtable may have been disposed when shutting down
162                                         if (vtableRefCount == 0 && comVtable == IntPtr.Zero)
163                                                 CreateVtable();
164                                         vtableRefCount++;
165                                 }
166
167                                 try
168                                 {
169                                         this.managedInterface = managedInterface;
170                                         gcHandle = GCHandle.Alloc(this);
171
172                                         newInterface = new IStreamInterface();
173                                         newInterface.lpVtbl = comVtable;
174                                         newInterface.gcHandle = (IntPtr)gcHandle;
175                                         comInterface = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IStreamInterface)));
176                                         Marshal.StructureToPtr(newInterface, comInterface, false);
177                                 }
178                                 catch
179                                 {
180                                         this.Dispose();
181                                         throw;
182                                 }
183                         }
184
185                         private void Dispose()
186                         {
187                                 if (gcHandle.IsAllocated)
188                                         gcHandle.Free();
189
190                                 if (comInterface != IntPtr.Zero)
191                                 {
192                                         Marshal.FreeHGlobal(comInterface);
193                                         comInterface = IntPtr.Zero;
194                                 }
195
196                                 managedInterface = null;
197
198                                 lock (managedVtable)
199                                 {
200                                         // Dispose vtable when shutting down
201                                         if (--vtableRefCount == 0 && Environment.HasShutdownStarted)
202                                                 DisposeVtable();
203                                 }
204                         }
205
206                         private static void OnShutdown(object sender, EventArgs e)
207                         {
208                                 lock (managedVtable)
209                                 {
210                                         // There may be object instances when shutting down
211                                         if (vtableRefCount == 0 && comVtable != IntPtr.Zero)
212                                                 DisposeVtable();
213                                 }
214                         }
215
216                         private static void CreateVtable()
217                         {
218                                 comVtable = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IStreamVtbl)));
219                                 Marshal.StructureToPtr(managedVtable, comVtable, false);
220                         }
221
222                         private static void DisposeVtable()
223                         {
224                                 Marshal.DestroyStructure(comVtable, typeof(IStreamVtbl));
225                                 Marshal.FreeHGlobal(comVtable);
226                                 comVtable = IntPtr.Zero;
227                         }
228
229                         internal static IStream GetUnderlyingInterface(IntPtr comInterface, bool outParam)
230                         {
231                                 if (Marshal.ReadIntPtr(comInterface) == comVtable)
232                                 {
233                                         IStream managedInterface = GetObject(comInterface).managedInterface;
234
235                                         if (outParam)
236                                                 Release(comInterface);
237
238                                         return managedInterface;
239                                 }
240                                 else
241                                         return null;
242                         }
243
244                         internal static IntPtr GetInterface(IStream managedInterface)
245                         {
246                                 IntPtr comInterface;
247
248                                 if (managedInterface == null)
249                                         return IntPtr.Zero;
250 #if !RECURSIVE_WRAPPING
251                                 else if ((comInterface = NativeToManagedWrapper.GetUnderlyingInterface(managedInterface)) == IntPtr.Zero)
252 #endif
253                                         comInterface = new ManagedToNativeWrapper(managedInterface).comInterface;
254
255                                 return comInterface;
256                         }
257
258                         internal static void ReleaseInterface(IntPtr comInterface)
259                         {
260                                 if (comInterface != IntPtr.Zero)
261                                 {
262                                         IntPtr vtable = Marshal.ReadIntPtr(comInterface);
263
264                                         if (vtable == comVtable)
265                                                 Release(comInterface);
266                                         else
267                                         {
268                                                 ReleaseSlot releaseSlot = (ReleaseSlot)Marshal.PtrToStructure((IntPtr)((long)vtable + (long)(IntPtr.Size * 2)), typeof(ReleaseSlot));
269                                                 releaseSlot.Release(comInterface);
270                                         }
271                                 }
272                         }
273
274                         // Mono does not implement Marshal.GetHRForException
275                         private static int GetHRForException(Exception e)
276                         {
277                                 return (int)exceptionGetHResult.Invoke(e, null);
278                         }
279
280                         private static ManagedToNativeWrapper GetObject(IntPtr @this)
281                         {
282                                 return (ManagedToNativeWrapper)((GCHandle)Marshal.ReadIntPtr(@this, IntPtr.Size)).Target;
283                         }
284
285                         private static int QueryInterface(IntPtr @this, ref Guid riid, IntPtr ppvObject)
286                         {
287 #if MAP_EX_TO_HR
288                                 try
289                                 {
290 #endif
291                                         if (IID_IUnknown.Equals(riid) || IID_IStream.Equals(riid))
292                                         {
293                                                 Marshal.WriteIntPtr(ppvObject, @this);
294                                                 AddRef(@this);
295                                                 return S_OK;
296                                         }
297                                         else
298                                         {
299                                                 Marshal.WriteIntPtr(ppvObject, IntPtr.Zero);
300                                                 return E_NOINTERFACE;
301                                         }
302 #if MAP_EX_TO_HR
303                                 }
304                                 catch (Exception e)
305                                 {
306                                         return GetHRForException(e);
307                                 }
308 #endif
309                         }
310
311                         private static int AddRef(IntPtr @this)
312                         {
313 #if MAP_EX_TO_HR
314                                 try
315                                 {
316 #endif
317                                         ManagedToNativeWrapper thisObject = GetObject(@this);
318
319                                         lock (thisObject)
320                                         {
321                                                 return ++thisObject.refCount;
322                                         }
323 #if MAP_EX_TO_HR
324                                 }
325                                 catch
326                                 {
327                                         return 0;
328                                 }
329 #endif
330                         }
331
332                         private static int Release(IntPtr @this)
333                         {
334 #if MAP_EX_TO_HR
335                                 try
336                                 {
337 #endif
338                                         ManagedToNativeWrapper thisObject = GetObject(@this);
339
340                                         lock (thisObject)
341                                         {
342                                                 if ((thisObject.refCount != 0) && (--thisObject.refCount == 0))
343                                                         thisObject.Dispose();
344
345                                                 return thisObject.refCount;
346                                         }
347 #if MAP_EX_TO_HR
348                                 }
349                                 catch
350                                 {
351                                         return 0;
352                                 }
353 #endif
354                         }
355
356                         private static int Read(IntPtr @this, byte[] pv, int cb, IntPtr pcbRead)
357                         {
358 #if MAP_EX_TO_HR
359                                 try
360                                 {
361 #endif
362                                         GetObject(@this).managedInterface.Read(pv, cb, pcbRead);
363                                         return S_OK;
364 #if MAP_EX_TO_HR
365                                 }
366                                 catch (Exception e)
367                                 {
368                                         return GetHRForException(e);
369                                 }
370 #endif
371                         }
372
373                         private static int Write(IntPtr @this, byte[] pv, int cb, IntPtr pcbWritten)
374                         {
375 #if MAP_EX_TO_HR
376                                 try
377                                 {
378 #endif
379                                         GetObject(@this).managedInterface.Write(pv, cb, pcbWritten);
380                                         return S_OK;
381 #if MAP_EX_TO_HR
382                                 }
383                                 catch (Exception e)
384                                 {
385                                         return GetHRForException(e);
386                                 }
387 #endif
388                         }
389
390                         private static int Seek(IntPtr @this, long dlibMove, int dwOrigin, IntPtr plibNewPosition)
391                         {
392 #if MAP_EX_TO_HR
393                                 try
394                                 {
395 #endif
396                                         GetObject(@this).managedInterface.Seek(dlibMove, dwOrigin, plibNewPosition);
397                                         return S_OK;
398 #if MAP_EX_TO_HR
399                                 }
400                                 catch (Exception e)
401                                 {
402                                         return GetHRForException(e);
403                                 }
404 #endif
405                         }
406
407                         private static int SetSize(IntPtr @this, long libNewSize)
408                         {
409 #if MAP_EX_TO_HR
410                                 try
411                                 {
412 #endif
413                                         GetObject(@this).managedInterface.SetSize(libNewSize);
414                                         return S_OK;
415 #if MAP_EX_TO_HR
416                                 }
417                                 catch (Exception e)
418                                 {
419                                         return GetHRForException(e);
420                                 }
421 #endif
422                         }
423
424                         private static int CopyTo(IntPtr @this, IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
425                         {
426 #if MAP_EX_TO_HR
427                                 try
428                                 {
429 #endif
430                                         GetObject(@this).managedInterface.CopyTo(pstm, cb, pcbRead, pcbWritten);
431                                         return S_OK;
432 #if MAP_EX_TO_HR
433                                 }
434                                 catch (Exception e)
435                                 {
436                                         return GetHRForException(e);
437                                 }
438 #endif
439                         }
440
441                         private static int Commit(IntPtr @this, int grfCommitFlags)
442                         {
443 #if MAP_EX_TO_HR
444                                 try
445                                 {
446 #endif
447                                         GetObject(@this).managedInterface.Commit(grfCommitFlags);
448                                         return S_OK;
449 #if MAP_EX_TO_HR
450                                 }
451                                 catch (Exception e)
452                                 {
453                                         return GetHRForException(e);
454                                 }
455 #endif
456                         }
457
458                         private static int Revert(IntPtr @this)
459                         {
460 #if MAP_EX_TO_HR
461                                 try
462                                 {
463 #endif
464                                         GetObject(@this).managedInterface.Revert();
465                                         return S_OK;
466 #if MAP_EX_TO_HR
467                                 }
468                                 catch (Exception e)
469                                 {
470                                         return GetHRForException(e);
471                                 }
472 #endif
473                         }
474
475                         private static int LockRegion(IntPtr @this, long libOffset, long cb, int dwLockType)
476                         {
477 #if MAP_EX_TO_HR
478                                 try
479                                 {
480 #endif
481                                         GetObject(@this).managedInterface.LockRegion(libOffset, cb, dwLockType);
482                                         return S_OK;
483 #if MAP_EX_TO_HR
484                                 }
485                                 catch (Exception e)
486                                 {
487                                         return GetHRForException(e);
488                                 }
489 #endif
490                         }
491
492                         private static int UnlockRegion(IntPtr @this, long libOffset, long cb, int dwLockType)
493                         {
494 #if MAP_EX_TO_HR
495                                 try
496                                 {
497 #endif
498                                         GetObject(@this).managedInterface.UnlockRegion(libOffset, cb, dwLockType);
499                                         return S_OK;
500 #if MAP_EX_TO_HR
501                                 }
502                                 catch (Exception e)
503                                 {
504                                         return GetHRForException(e);
505                                 }
506 #endif
507                         }
508
509                         private static int Stat(IntPtr @this, out STATSTG pstatstg, int grfStatFlag)
510                         {
511 #if MAP_EX_TO_HR
512                                 try
513                                 {
514 #endif
515                                         GetObject(@this).managedInterface.Stat(out pstatstg, grfStatFlag);
516                                         return S_OK;
517 #if MAP_EX_TO_HR
518                                 }
519                                 catch (Exception e)
520                                 {
521                                         pstatstg = new STATSTG();
522                                         return GetHRForException(e);
523                                 }
524 #endif
525                         }
526
527                         private static int Clone(IntPtr @this, out IntPtr ppstm)
528                         {
529                                 ppstm = IntPtr.Zero;
530 #if MAP_EX_TO_HR
531                                 try
532                                 {
533 #endif
534                                         IStream newInterface;
535
536                                         GetObject(@this).managedInterface.Clone(out newInterface);
537                                         ppstm = ManagedToNativeWrapper.GetInterface(newInterface);
538                                         return S_OK;
539 #if MAP_EX_TO_HR
540                                 }
541                                 catch (Exception e)
542                                 {
543                                         return GetHRForException(e);
544                                 }
545 #endif
546                         }
547                 }
548
549                 // Managed Runtime Callable Wrapper implementation
550                 private sealed class NativeToManagedWrapper : IStream
551                 {
552                         private IntPtr comInterface;
553                         private IStreamVtbl managedVtable;
554
555                         private NativeToManagedWrapper(IntPtr comInterface, bool outParam)
556                         {
557                                 this.comInterface = comInterface;
558                                 managedVtable = (IStreamVtbl)Marshal.PtrToStructure(Marshal.ReadIntPtr(comInterface), typeof(IStreamVtbl));
559                                 if (!outParam)
560                                         managedVtable.AddRef(comInterface);
561                         }
562
563                         ~NativeToManagedWrapper()
564                         {
565                                 Dispose(false);
566                         }
567
568                         private void Dispose(bool disposing)
569                         {
570                                 managedVtable.Release(comInterface);
571                                 if (disposing)
572                                 {
573                                         comInterface = IntPtr.Zero;
574                                         managedVtable = null;
575                                         GC.SuppressFinalize(this);
576                                 }
577                         }
578
579                         internal static IntPtr GetUnderlyingInterface(IStream managedInterface)
580                         {
581                                 if (managedInterface is NativeToManagedWrapper)
582                                 {
583                                         NativeToManagedWrapper wrapper = (NativeToManagedWrapper)managedInterface;
584
585                                         wrapper.managedVtable.AddRef(wrapper.comInterface);
586                                         return wrapper.comInterface;
587                                 }
588                                 else
589                                         return IntPtr.Zero;
590                         }
591
592                         internal static IStream GetInterface(IntPtr comInterface, bool outParam)
593                         {
594                                 IStream managedInterface;
595
596                                 if (comInterface == IntPtr.Zero)
597                                         return null;
598 #if !RECURSIVE_WRAPPING
599                                 else if ((managedInterface = ManagedToNativeWrapper.GetUnderlyingInterface(comInterface, outParam)) == null)
600 #endif
601                                         managedInterface = (IStream)new NativeToManagedWrapper(comInterface, outParam);
602
603                                 return managedInterface;
604                         }
605
606                         internal static void ReleaseInterface(IStream managedInterface)
607                         {
608                                 if (managedInterface is NativeToManagedWrapper)
609                                         ((NativeToManagedWrapper)managedInterface).Dispose(true);
610                         }
611
612                         // Mono does not implement Marshal.ThrowExceptionForHR
613                         private static void ThrowExceptionForHR(int result)
614                         {
615                                 if (result < 0)
616                                         throw new COMException(null, result);
617                         }
618
619                         public void Read(byte[] pv, int cb, IntPtr pcbRead)
620                         {
621                                 ThrowExceptionForHR(managedVtable.Read(comInterface, pv, cb, pcbRead));
622                         }
623
624                         public void Write(byte[] pv, int cb, IntPtr pcbWritten)
625                         {
626                                 ThrowExceptionForHR(managedVtable.Write(comInterface, pv, cb, pcbWritten));
627                         }
628
629                         public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
630                         {
631                                 ThrowExceptionForHR(managedVtable.Seek(comInterface, dlibMove, dwOrigin, plibNewPosition));
632                         }
633
634                         public void SetSize(long libNewSize)
635                         {
636                                 ThrowExceptionForHR(managedVtable.SetSize(comInterface, libNewSize));
637                         }
638
639                         public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
640                         {
641                                 ThrowExceptionForHR(managedVtable.CopyTo(comInterface, pstm, cb, pcbRead, pcbWritten));
642                         }
643
644                         public void Commit(int grfCommitFlags)
645                         {
646                                 ThrowExceptionForHR(managedVtable.Commit(comInterface, grfCommitFlags));
647                         }
648
649                         public void Revert()
650                         {
651                                 ThrowExceptionForHR(managedVtable.Revert(comInterface));
652                         }
653
654                         public void LockRegion(long libOffset, long cb, int dwLockType)
655                         {
656                                 ThrowExceptionForHR(managedVtable.LockRegion(comInterface, libOffset, cb, dwLockType));
657                         }
658
659                         public void UnlockRegion(long libOffset, long cb, int dwLockType)
660                         {
661                                 ThrowExceptionForHR(managedVtable.UnlockRegion(comInterface, libOffset, cb, dwLockType));
662                         }
663
664                         public void Stat(out STATSTG pstatstg, int grfStatFlag)
665                         {
666                                 ThrowExceptionForHR(managedVtable.Stat(comInterface, out pstatstg, grfStatFlag));
667                         }
668
669                         public void Clone(out IStream ppstm)
670                         {
671                                 IntPtr newInterface;
672
673                                 ThrowExceptionForHR(managedVtable.Clone(comInterface, out newInterface));
674                                 ppstm = NativeToManagedWrapper.GetInterface(newInterface, true);
675                         }
676                 }
677
678                 private static readonly ComIStreamMarshaler defaultInstance = new ComIStreamMarshaler();
679
680                 private ComIStreamMarshaler()
681                 {
682                 }
683
684                 private static ICustomMarshaler GetInstance(string cookie)
685                 {
686                         return defaultInstance;
687                 }
688
689                 public IntPtr MarshalManagedToNative(object managedObj)
690                 {
691 #if RECURSIVE_WRAPPING
692                         managedObj = NativeToManagedWrapper.GetInterface(ManagedToNativeWrapper.GetInterface((IStream)managedObj), true);
693 #endif
694                         return ManagedToNativeWrapper.GetInterface((IStream)managedObj);
695                 }
696
697                 public void CleanUpNativeData(IntPtr pNativeData)
698                 {
699                         ManagedToNativeWrapper.ReleaseInterface(pNativeData);
700                 }
701
702                 public object MarshalNativeToManaged(IntPtr pNativeData)
703                 {
704 #if RECURSIVE_WRAPPING
705                         pNativeData = ManagedToNativeWrapper.GetInterface(NativeToManagedWrapper.GetInterface(pNativeData, true));
706 #endif
707                         return NativeToManagedWrapper.GetInterface(pNativeData, false);
708                 }
709
710                 public void CleanUpManagedData(object managedObj)
711                 {
712                         NativeToManagedWrapper.ReleaseInterface((IStream)managedObj);
713                 }
714
715                 public int GetNativeDataSize()
716                 {
717                         return -1;
718                 }
719         }
720 }