Implementation of OLE32.IsAccelerator.
[wine] / dlls / ole32 / stg_stream.c
1 /*
2  * Compound Storage (32 bit version)
3  * Stream implementation
4  *
5  * This file contains the implementation of the stream interface
6  * for streams contained in a compound storage.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Thuy Nguyen
10  */
11 #include <assert.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15
16 #include "winbase.h"
17 #include "winerror.h"
18 #include "ntddk.h"
19 #include "debugtools.h"
20
21 #include "storage32.h"
22
23 DEFAULT_DEBUG_CHANNEL(storage);
24
25
26 /*
27  * Virtual function table for the StgStreamImpl class.
28  */
29 static ICOM_VTABLE(IStream) StgStreamImpl_Vtbl =
30 {
31     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
32     StgStreamImpl_QueryInterface,
33     StgStreamImpl_AddRef,
34     StgStreamImpl_Release,
35     StgStreamImpl_Read,
36     StgStreamImpl_Write,
37     StgStreamImpl_Seek,
38     StgStreamImpl_SetSize,
39     StgStreamImpl_CopyTo,
40     StgStreamImpl_Commit,
41     StgStreamImpl_Revert,
42     StgStreamImpl_LockRegion,
43     StgStreamImpl_UnlockRegion,
44     StgStreamImpl_Stat,
45     StgStreamImpl_Clone
46 };
47
48 /******************************************************************************
49 ** StgStreamImpl implementation
50 */
51
52 /***
53  * This is the constructor for the StgStreamImpl class.
54  *
55  * Params:
56  *    parentStorage - Pointer to the storage that contains the stream to open
57  *    ownerProperty - Index of the property that points to this stream.
58  */
59 StgStreamImpl* StgStreamImpl_Construct(
60                 StorageBaseImpl* parentStorage,
61     DWORD            grfMode,
62     ULONG            ownerProperty)
63 {
64   StgStreamImpl* newStream;
65
66   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
67   
68   if (newStream!=0)
69   {
70     /*
71      * Set-up the virtual function table and reference count.
72      */
73     ICOM_VTBL(newStream) = &StgStreamImpl_Vtbl;
74     newStream->ref       = 0;
75     
76     /*
77      * We want to nail-down the reference to the storage in case the
78      * stream out-lives the storage in the client application.
79      */
80     newStream->parentStorage = parentStorage;
81     IStorage_AddRef((IStorage*)newStream->parentStorage);
82
83     newStream->grfMode = grfMode;    
84     newStream->ownerProperty = ownerProperty;
85     
86     /*
87      * Start the stream at the begining.
88      */
89     newStream->currentPosition.s.HighPart = 0;
90     newStream->currentPosition.s.LowPart = 0;
91     
92     /*
93      * Initialize the rest of the data.
94      */
95     newStream->streamSize.s.HighPart = 0;
96     newStream->streamSize.s.LowPart  = 0;
97     newStream->bigBlockChain       = 0;
98     newStream->smallBlockChain     = 0;
99     
100     /*
101      * Read the size from the property and determine if the blocks forming
102      * this stream are large or small.
103      */
104     StgStreamImpl_OpenBlockChain(newStream);
105   }
106   
107   return newStream;
108 }
109
110 /***
111  * This is the destructor of the StgStreamImpl class.
112  *
113  * This method will clean-up all the resources used-up by the given StgStreamImpl 
114  * class. The pointer passed-in to this function will be freed and will not
115  * be valid anymore.
116  */
117 void StgStreamImpl_Destroy(StgStreamImpl* This)
118 {
119   TRACE("(%p)\n", This);
120
121   /*
122    * Release the reference we are holding on the parent storage.
123    */
124   IStorage_Release((IStorage*)This->parentStorage);
125   This->parentStorage = 0;
126
127   /*
128    * Make sure we clean-up the block chain stream objects that we were using.
129    */
130   if (This->bigBlockChain != 0)
131   {
132     BlockChainStream_Destroy(This->bigBlockChain);
133     This->bigBlockChain = 0;
134   }
135
136   if (This->smallBlockChain != 0)
137   {
138     SmallBlockChainStream_Destroy(This->smallBlockChain);
139     This->smallBlockChain = 0;
140   }
141
142   /*
143    * Finally, free the memory used-up by the class.
144    */
145   HeapFree(GetProcessHeap(), 0, This);  
146 }
147
148 /***
149  * This implements the IUnknown method QueryInterface for this
150  * class
151  */
152 HRESULT WINAPI StgStreamImpl_QueryInterface(
153                   IStream*     iface,
154                   REFIID         riid,        /* [in] */          
155                   void**         ppvObject)   /* [iid_is][out] */ 
156 {
157   StgStreamImpl* const This=(StgStreamImpl*)iface;
158
159   /*
160    * Perform a sanity check on the parameters.
161    */
162   if (ppvObject==0)
163     return E_INVALIDARG;
164   
165   /*
166    * Initialize the return parameter.
167    */
168   *ppvObject = 0;
169   
170   /*
171    * Compare the riid with the interface IDs implemented by this object.
172    */
173   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) 
174   {
175     *ppvObject = (IStream*)This;
176   }
177   else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0) 
178   {
179     *ppvObject = (IStream*)This;
180   }
181   
182   /*
183    * Check that we obtained an interface.
184    */
185   if ((*ppvObject)==0)
186     return E_NOINTERFACE;
187   
188   /*
189    * Query Interface always increases the reference count by one when it is
190    * successful
191    */
192   StgStreamImpl_AddRef(iface);
193   
194   return S_OK;;
195 }
196
197 /***
198  * This implements the IUnknown method AddRef for this
199  * class
200  */
201 ULONG WINAPI StgStreamImpl_AddRef(
202                 IStream* iface)
203 {
204   StgStreamImpl* const This=(StgStreamImpl*)iface;
205
206   This->ref++;
207   
208   return This->ref;
209 }
210
211 /***
212  * This implements the IUnknown method Release for this
213  * class
214  */
215 ULONG WINAPI StgStreamImpl_Release(
216                 IStream* iface)
217 {
218   StgStreamImpl* const This=(StgStreamImpl*)iface;
219
220   ULONG newRef;
221   
222   This->ref--;
223   
224   newRef = This->ref;
225   
226   /*
227    * If the reference count goes down to 0, perform suicide.
228    */
229   if (newRef==0)
230   {
231     StgStreamImpl_Destroy(This);
232   }
233   
234   return newRef;
235 }
236
237 /***
238  * This method will open the block chain pointed by the property
239  * that describes the stream.
240  * If the stream's size is null, no chain is opened.
241  */
242 void StgStreamImpl_OpenBlockChain(
243         StgStreamImpl* This)
244 {
245   StgProperty    curProperty;
246   BOOL         readSucessful;
247
248   /*
249    * Make sure no old object is staying behind.
250    */
251   if (This->smallBlockChain != 0)
252   {
253     SmallBlockChainStream_Destroy(This->smallBlockChain);
254     This->smallBlockChain = 0;
255   }
256
257   if (This->bigBlockChain != 0)
258   {
259     BlockChainStream_Destroy(This->bigBlockChain);
260     This->bigBlockChain = 0;
261   }
262
263   /*
264    * Read the information from the property.
265    */
266   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
267                                              This->ownerProperty,
268                                              &curProperty);
269   
270   if (readSucessful)
271   {
272     This->streamSize = curProperty.size;
273     
274     /*
275      * This code supports only streams that are <32 bits in size.
276      */
277     assert(This->streamSize.s.HighPart == 0);
278     
279     if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
280     {
281       assert( (This->streamSize.s.HighPart == 0) && (This->streamSize.s.LowPart == 0) );
282     }
283     else
284     {
285       if ( (This->streamSize.s.HighPart == 0) &&
286            (This->streamSize.s.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
287       {
288         This->smallBlockChain = SmallBlockChainStream_Construct(
289                                                                 This->parentStorage->ancestorStorage,   
290                                                                 This->ownerProperty);
291       }
292       else
293       {
294         This->bigBlockChain = BlockChainStream_Construct(
295                                                          This->parentStorage->ancestorStorage,
296                                                          NULL,
297                                                          This->ownerProperty);
298       }
299     }
300   }
301 }
302
303 /***
304  * This method is part of the ISequentialStream interface.
305  *
306  * If reads a block of information from the stream at the current
307  * position. It then moves the current position at the end of the
308  * read block
309  *
310  * See the documentation of ISequentialStream for more info.
311  */
312 HRESULT WINAPI StgStreamImpl_Read( 
313                   IStream*     iface,
314                   void*          pv,        /* [length_is][size_is][out] */
315                   ULONG          cb,        /* [in] */                     
316                   ULONG*         pcbRead)   /* [out] */                    
317 {
318   StgStreamImpl* const This=(StgStreamImpl*)iface;
319
320   ULONG bytesReadBuffer;
321   ULONG bytesToReadFromBuffer;
322
323   TRACE("(%p, %p, %ld, %p)\n",
324         iface, pv, cb, pcbRead);
325
326   /* 
327    * If the caller is not interested in the nubmer of bytes read,
328    * we use another buffer to avoid "if" statements in the code.
329    */
330   if (pcbRead==0)
331     pcbRead = &bytesReadBuffer;
332   
333   /*
334    * Using the known size of the stream, calculate the number of bytes
335    * to read from the block chain
336    */
337   bytesToReadFromBuffer = min( This->streamSize.s.LowPart - This->currentPosition.s.LowPart, cb);
338   
339   /*
340    * Depending on the type of chain that was opened when the stream was constructed,
341    * we delegate the work to the method that read the block chains.
342    */
343   if (This->smallBlockChain!=0)
344   {
345     SmallBlockChainStream_ReadAt(This->smallBlockChain,
346                                  This->currentPosition,
347                                  bytesToReadFromBuffer,
348                                  pv,
349                                  pcbRead);
350     
351   }
352   else if (This->bigBlockChain!=0)
353   {
354     BlockChainStream_ReadAt(This->bigBlockChain,
355                             This->currentPosition,
356                             bytesToReadFromBuffer,
357                             pv,
358                             pcbRead);
359   }
360   else
361   {
362     /*
363      * Small and big block chains are both NULL. This case will happen
364      * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
365      */
366
367     *pcbRead = 0;
368     return S_OK;
369   }
370
371   /*
372    * We should always be able to read the proper amount of data from the
373    * chain.
374    */
375   assert(bytesToReadFromBuffer == *pcbRead);
376
377   /*
378    * Advance the pointer for the number of positions read.
379    */
380   This->currentPosition.s.LowPart += *pcbRead;
381   
382   /*
383    * The function returns S_OK if the buffer was filled completely
384    * it returns S_FALSE if the end of the stream is reached before the
385    * buffer is filled
386    */
387   if(*pcbRead == cb)
388     return S_OK;
389   
390   return S_FALSE;
391 }
392         
393 /***
394  * This method is part of the ISequentialStream interface.
395  *
396  * It writes a block of information to the stream at the current
397  * position. It then moves the current position at the end of the
398  * written block. If the stream is too small to fit the block,
399  * the stream is grown to fit.
400  *
401  * See the documentation of ISequentialStream for more info.
402  */
403 HRESULT WINAPI StgStreamImpl_Write(
404                   IStream*     iface,
405                   const void*    pv,          /* [size_is][in] */ 
406                   ULONG          cb,          /* [in] */          
407                   ULONG*         pcbWritten)  /* [out] */         
408 {
409   StgStreamImpl* const This=(StgStreamImpl*)iface;
410
411   ULARGE_INTEGER newSize;
412   ULONG bytesWritten = 0;
413
414   TRACE("(%p, %p, %ld, %p)\n",
415         iface, pv, cb, pcbWritten);
416  
417   /*
418    * Do we have permission to write to this stream?
419    */
420   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE))) {
421       return STG_E_ACCESSDENIED;
422   }
423
424   /*
425    * If the caller is not interested in the number of bytes written,
426    * we use another buffer to avoid "if" statements in the code.
427    */
428   if (pcbWritten == 0)
429     pcbWritten = &bytesWritten;
430   
431   /*
432    * Initialize the out parameter
433    */
434   *pcbWritten = 0;
435
436   if (cb == 0)
437   {
438     return S_OK;
439   }
440   else
441   {
442     newSize.s.HighPart = 0;
443     newSize.s.LowPart = This->currentPosition.s.LowPart + cb;
444   }
445   
446   /*
447    * Verify if we need to grow the stream
448    */
449   if (newSize.s.LowPart > This->streamSize.s.LowPart)
450   {
451     /* grow stream */
452     IStream_SetSize(iface, newSize);
453   }
454   
455   /*
456    * Depending on the type of chain that was opened when the stream was constructed,
457    * we delegate the work to the method that readwrites to the block chains.
458    */
459   if (This->smallBlockChain!=0)
460   {
461     SmallBlockChainStream_WriteAt(This->smallBlockChain,
462                                   This->currentPosition,
463                                   cb,
464                                   pv,
465                                   pcbWritten);
466     
467   }
468   else if (This->bigBlockChain!=0)
469   {
470     BlockChainStream_WriteAt(This->bigBlockChain,
471                              This->currentPosition,
472                              cb,
473                              pv,
474                              pcbWritten);
475   }
476   else
477     assert(FALSE);
478   
479   /*
480    * Advance the position pointer for the number of positions written.
481    */
482   This->currentPosition.s.LowPart += *pcbWritten;
483   
484   return S_OK;
485 }
486
487 /***
488  * This method is part of the IStream interface.
489  *
490  * It will move the current stream pointer according to the parameters
491  * given.
492  *
493  * See the documentation of IStream for more info.
494  */        
495 HRESULT WINAPI StgStreamImpl_Seek( 
496                   IStream*      iface,
497                   LARGE_INTEGER   dlibMove,         /* [in] */ 
498                   DWORD           dwOrigin,         /* [in] */ 
499                   ULARGE_INTEGER* plibNewPosition) /* [out] */
500 {
501   StgStreamImpl* const This=(StgStreamImpl*)iface;
502
503   ULARGE_INTEGER newPosition;
504
505   TRACE("(%p, %ld, %ld, %p)\n",
506         iface, dlibMove.s.LowPart, dwOrigin, plibNewPosition);
507
508   /* 
509    * The caller is allowed to pass in NULL as the new position return value.
510    * If it happens, we assign it to a dynamic variable to avoid special cases
511    * in the code below.
512    */
513   if (plibNewPosition == 0)
514   {
515     plibNewPosition = &newPosition;
516   }
517
518   /*
519    * The file pointer is moved depending on the given "function"
520    * parameter.
521    */
522   switch (dwOrigin)
523   {
524     case STREAM_SEEK_SET:
525       plibNewPosition->s.HighPart = 0;
526       plibNewPosition->s.LowPart  = 0;
527       break;
528     case STREAM_SEEK_CUR:
529       *plibNewPosition = This->currentPosition;
530       break;
531     case STREAM_SEEK_END:
532       *plibNewPosition = This->streamSize;
533       break;
534     default:
535       return STG_E_INVALIDFUNCTION;
536   }
537
538   plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
539
540   /*
541    * tell the caller what we calculated
542    */
543   This->currentPosition = *plibNewPosition;
544  
545   return S_OK;
546 }
547
548 /***
549  * This method is part of the IStream interface.
550  *
551  * It will change the size of a stream.
552  *
553  * TODO: Switch from small blocks to big blocks and vice versa.
554  *
555  * See the documentation of IStream for more info.
556  */
557 HRESULT WINAPI StgStreamImpl_SetSize( 
558                                      IStream*      iface,
559                                      ULARGE_INTEGER  libNewSize)   /* [in] */ 
560 {
561   StgStreamImpl* const This=(StgStreamImpl*)iface;
562
563   StgProperty    curProperty;
564   BOOL         Success;
565
566   TRACE("(%p, %ld)\n", iface, libNewSize.s.LowPart);
567
568   /*
569    * As documented.
570    */
571   if (libNewSize.s.HighPart != 0)
572     return STG_E_INVALIDFUNCTION;
573
574   /*
575    * Do we have permission?
576    */
577   if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
578     return STG_E_ACCESSDENIED;
579
580   if (This->streamSize.s.LowPart == libNewSize.s.LowPart)
581     return S_OK;
582
583   /*
584    * This will happen if we're creating a stream
585    */
586   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
587   {
588     if (libNewSize.s.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
589     {
590       This->smallBlockChain = SmallBlockChainStream_Construct(
591                                     This->parentStorage->ancestorStorage,
592                                     This->ownerProperty);
593     }
594     else
595     {
596       This->bigBlockChain = BlockChainStream_Construct(
597                                 This->parentStorage->ancestorStorage,
598                                 NULL,
599                                 This->ownerProperty);
600     }
601   }
602
603   /*
604    * Read this stream's property to see if it's small blocks or big blocks
605    */
606   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
607                                        This->ownerProperty,
608                                        &curProperty); 
609   /*
610    * Determine if we have to switch from small to big blocks or vice versa
611    */  
612   if ( (This->smallBlockChain!=0) && 
613        (curProperty.size.s.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
614   {
615     if (libNewSize.s.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
616     {
617       /*
618        * Transform the small block chain into a big block chain
619        */
620       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
621                                 This->parentStorage->ancestorStorage,
622                                 &This->smallBlockChain);
623     }
624   }
625
626   if (This->smallBlockChain!=0)
627   {
628     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
629   }
630   else
631   {
632     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
633   }
634
635   /*
636    * Write to the property the new information about this stream
637    */
638   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
639                                        This->ownerProperty,
640                                        &curProperty);
641
642   curProperty.size.s.HighPart = libNewSize.s.HighPart;
643   curProperty.size.s.LowPart = libNewSize.s.LowPart;
644   
645   if (Success)
646   {
647     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
648                                 This->ownerProperty,
649                                 &curProperty);
650   }
651   
652   This->streamSize = libNewSize;
653   
654   return S_OK;
655 }
656         
657 /***
658  * This method is part of the IStream interface.
659  *
660  * It will copy the 'cb' Bytes to 'pstm' IStream.
661  *
662  * See the documentation of IStream for more info.
663  */
664 HRESULT WINAPI StgStreamImpl_CopyTo( 
665                                     IStream*      iface,
666                                     IStream*      pstm,         /* [unique][in] */ 
667                                     ULARGE_INTEGER  cb,           /* [in] */         
668                                     ULARGE_INTEGER* pcbRead,      /* [out] */        
669                                     ULARGE_INTEGER* pcbWritten)   /* [out] */        
670 {
671   HRESULT        hr = S_OK;
672   BYTE           tmpBuffer[128];
673   ULONG          bytesRead, bytesWritten, copySize;
674   ULARGE_INTEGER totalBytesRead;
675   ULARGE_INTEGER totalBytesWritten;
676
677   TRACE("(%p, %p, %ld, %p, %p)\n", 
678         iface, pstm, cb.s.LowPart, pcbRead, pcbWritten);
679
680   /*
681    * Sanity check
682    */
683   if ( pstm == 0 )
684     return STG_E_INVALIDPOINTER;
685
686   totalBytesRead.s.LowPart = totalBytesRead.s.HighPart = 0;
687   totalBytesWritten.s.LowPart = totalBytesWritten.s.HighPart = 0;
688
689   /*
690    * use stack to store data temporarly
691    * there is surely more performant way of doing it, for now this basic
692    * implementation will do the job
693    */
694   while ( cb.s.LowPart > 0 )
695   {
696     if ( cb.s.LowPart >= 128 )
697       copySize = 128;
698     else
699       copySize = cb.s.LowPart;
700     
701     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
702
703     totalBytesRead.s.LowPart += bytesRead;
704     
705     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
706
707     totalBytesWritten.s.LowPart += bytesWritten;
708
709     /*
710      * Check that read & write operations were succesfull
711      */
712     if (bytesRead != bytesWritten)
713     {
714       hr = STG_E_MEDIUMFULL;
715       break;
716     }
717     
718     if (bytesRead!=copySize)
719       cb.s.LowPart = 0;
720     else
721       cb.s.LowPart -= bytesRead;
722   }
723
724   /*
725    * Update number of bytes read and written
726    */
727   if (pcbRead)
728   {
729     pcbRead->s.LowPart = totalBytesRead.s.LowPart;
730     pcbRead->s.HighPart = totalBytesRead.s.HighPart;
731   }
732
733   if (pcbWritten)
734   {
735     pcbWritten->s.LowPart = totalBytesWritten.s.LowPart;
736     pcbWritten->s.HighPart = totalBytesWritten.s.HighPart;
737   }
738   return hr;
739 }
740
741 /***
742  * This method is part of the IStream interface.
743  *
744  * For streams contained in structured storages, this method
745  * does nothing. This is what the documentation tells us.
746  *
747  * See the documentation of IStream for more info.
748  */        
749 HRESULT WINAPI StgStreamImpl_Commit( 
750                   IStream*      iface,
751                   DWORD           grfCommitFlags)  /* [in] */ 
752 {
753   return S_OK;
754 }
755
756 /***
757  * This method is part of the IStream interface.
758  *
759  * For streams contained in structured storages, this method
760  * does nothing. This is what the documentation tells us.
761  *
762  * See the documentation of IStream for more info.
763  */        
764 HRESULT WINAPI StgStreamImpl_Revert( 
765                   IStream* iface)
766 {
767   return S_OK;
768 }
769
770 HRESULT WINAPI StgStreamImpl_LockRegion( 
771                                         IStream*     iface,
772                                         ULARGE_INTEGER libOffset,   /* [in] */ 
773                                         ULARGE_INTEGER cb,          /* [in] */ 
774                                         DWORD          dwLockType)  /* [in] */ 
775 {
776   FIXME("not implemented!\n");
777   return E_NOTIMPL;
778 }
779
780 HRESULT WINAPI StgStreamImpl_UnlockRegion( 
781                                           IStream*     iface,
782                                           ULARGE_INTEGER libOffset,   /* [in] */ 
783                                           ULARGE_INTEGER cb,          /* [in] */ 
784                                           DWORD          dwLockType)  /* [in] */ 
785 {
786   FIXME("not implemented!\n");
787   return E_NOTIMPL;
788 }
789
790 /***
791  * This method is part of the IStream interface.
792  *
793  * This method returns information about the current
794  * stream.
795  *
796  * See the documentation of IStream for more info.
797  */        
798 HRESULT WINAPI StgStreamImpl_Stat( 
799                   IStream*     iface,
800                   STATSTG*       pstatstg,     /* [out] */
801                   DWORD          grfStatFlag)  /* [in] */ 
802 {
803   StgStreamImpl* const This=(StgStreamImpl*)iface;
804
805   StgProperty    curProperty;
806   BOOL         readSucessful;
807   
808   /*
809    * Read the information from the property.
810    */
811   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
812                                              This->ownerProperty,
813                                              &curProperty);
814   
815   if (readSucessful)
816   {
817     StorageUtl_CopyPropertyToSTATSTG(pstatstg, 
818                                      &curProperty, 
819                                      grfStatFlag);
820
821     pstatstg->grfMode = This->grfMode;
822     
823     return S_OK;
824   }
825   
826   return E_FAIL;
827 }
828         
829 HRESULT WINAPI StgStreamImpl_Clone( 
830                                    IStream*     iface,
831                                    IStream**    ppstm) /* [out] */ 
832 {
833   FIXME("not implemented!\n");
834   return E_NOTIMPL;
835 }