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