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