Implementation of OLE32.IsAccelerator.
[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 supported 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)
253     {
254       newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
255                                              GMEM_SHARE, 0);
256     }
257     
258     /*
259      * Start the stream at the begining.
260      */
261     newStream->currentPosition.s.HighPart = 0;
262     newStream->currentPosition.s.LowPart = 0;
263     
264     /*
265      * Initialize the size of the stream to the size of the handle.
266      */
267     newStream->streamSize.s.HighPart = 0;
268     newStream->streamSize.s.LowPart  = GlobalSize(newStream->supportHandle);
269   }
270   
271   return newStream;
272 }
273
274 /***
275  * This is the destructor of the HGLOBALStreamImpl class.
276  *
277  * This method will clean-up all the resources used-up by the given HGLOBALStreamImpl 
278  * class. The pointer passed-in to this function will be freed and will not
279  * be valid anymore.
280  */
281 void HGLOBALStreamImpl_Destroy(HGLOBALStreamImpl* This)
282 {
283   TRACE("(%p)\n", This);
284
285   /*
286    * Release the HGlobal if the constructor asked for that.
287    */
288   if (This->deleteOnRelease)
289   {
290     GlobalFree(This->supportHandle);
291     This->supportHandle=0;
292   }
293
294   /*
295    * Finally, free the memory used-up by the class.
296    */
297   HeapFree(GetProcessHeap(), 0, This);  
298 }
299
300 /***
301  * This implements the IUnknown method QueryInterface for this
302  * class
303  */
304 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
305                   IStream*     iface,
306                   REFIID         riid,        /* [in] */          
307                   void**         ppvObject)   /* [iid_is][out] */ 
308 {
309   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
310
311   /*
312    * Perform a sanity check on the parameters.
313    */
314   if (ppvObject==0)
315     return E_INVALIDARG;
316   
317   /*
318    * Initialize the return parameter.
319    */
320   *ppvObject = 0;
321   
322   /*
323    * Compare the riid with the interface IDs implemented by this object.
324    */
325   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) 
326   {
327     *ppvObject = (IStream*)This;
328   }
329   else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0) 
330   {
331     *ppvObject = (IStream*)This;
332   }
333   
334   /*
335    * Check that we obtained an interface.
336    */
337   if ((*ppvObject)==0)
338     return E_NOINTERFACE;
339   
340   /*
341    * Query Interface always increases the reference count by one when it is
342    * successful
343    */
344   HGLOBALStreamImpl_AddRef(iface);
345   
346   return S_OK;;
347 }
348
349 /***
350  * This implements the IUnknown method AddRef for this
351  * class
352  */
353 ULONG WINAPI HGLOBALStreamImpl_AddRef(
354                 IStream* iface)
355 {
356   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
357
358   This->ref++;
359   
360   return This->ref;
361 }
362
363 /***
364  * This implements the IUnknown method Release for this
365  * class
366  */
367 ULONG WINAPI HGLOBALStreamImpl_Release(
368                 IStream* iface)
369 {
370   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
371
372   ULONG newRef;
373   
374   This->ref--;
375   
376   newRef = This->ref;
377   
378   /*
379    * If the reference count goes down to 0, perform suicide.
380    */
381   if (newRef==0)
382   {
383     HGLOBALStreamImpl_Destroy(This);
384   }
385   
386   return newRef;
387 }
388
389 /***
390  * This method is part of the ISequentialStream interface.
391  *
392  * If reads a block of information from the stream at the current
393  * position. It then moves the current position at the end of the
394  * read block
395  *
396  * See the documentation of ISequentialStream for more info.
397  */
398 HRESULT WINAPI HGLOBALStreamImpl_Read( 
399                   IStream*     iface,
400                   void*          pv,        /* [length_is][size_is][out] */
401                   ULONG          cb,        /* [in] */                     
402                   ULONG*         pcbRead)   /* [out] */                    
403 {
404   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
405
406   void* supportBuffer;
407   ULONG bytesReadBuffer;
408   ULONG bytesToReadFromBuffer;
409
410   TRACE("(%p, %p, %ld, %p)\n", iface,
411         pv, cb, pcbRead);
412   
413   /* 
414    * If the caller is not interested in the nubmer of bytes read,
415    * we use another buffer to avoid "if" statements in the code.
416    */
417   if (pcbRead==0)
418     pcbRead = &bytesReadBuffer;
419   
420   /*
421    * Using the known size of the stream, calculate the number of bytes
422    * to read from the block chain
423    */
424   bytesToReadFromBuffer = min( This->streamSize.s.LowPart - This->currentPosition.s.LowPart, cb);
425
426   /*
427    * Lock the buffer in position and copy the data.
428    */
429   supportBuffer = GlobalLock(This->supportHandle);
430
431   memcpy(pv, (char *) supportBuffer+This->currentPosition.s.LowPart, bytesToReadFromBuffer);
432
433   /*
434    * Move the current position to the new position
435    */
436   This->currentPosition.s.LowPart+=bytesToReadFromBuffer;
437
438   /*
439    * Return the number of bytes read.
440    */
441   *pcbRead = bytesToReadFromBuffer;
442
443   /*
444    * Cleanup
445    */
446   GlobalUnlock(This->supportHandle);
447   
448   /*
449    * The function returns S_OK if the buffer was filled completely
450    * it returns S_FALSE if the end of the stream is reached before the
451    * buffer is filled
452    */
453   if(*pcbRead == cb)
454     return S_OK;
455   
456   return S_FALSE;
457 }
458         
459 /***
460  * This method is part of the ISequentialStream interface.
461  *
462  * It writes a block of information to the stream at the current
463  * position. It then moves the current position at the end of the
464  * written block. If the stream is too small to fit the block,
465  * the stream is grown to fit.
466  *
467  * See the documentation of ISequentialStream for more info.
468  */
469 HRESULT WINAPI HGLOBALStreamImpl_Write(
470                   IStream*     iface,
471                   const void*    pv,          /* [size_is][in] */ 
472                   ULONG          cb,          /* [in] */          
473                   ULONG*         pcbWritten)  /* [out] */         
474 {
475   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
476
477   void*          supportBuffer;
478   ULARGE_INTEGER newSize;
479   ULONG          bytesWritten = 0;
480
481   TRACE("(%p, %p, %ld, %p)\n", iface,
482         pv, cb, pcbWritten);
483   
484   /*
485    * If the caller is not interested in the number of bytes written,
486    * we use another buffer to avoid "if" statements in the code.
487    */
488   if (pcbWritten == 0)
489     pcbWritten = &bytesWritten;
490   
491   if (cb == 0)
492   {
493     return S_OK;
494   }
495   else
496   {
497     newSize.s.HighPart = 0;
498     newSize.s.LowPart = This->currentPosition.s.LowPart + cb;
499   }
500   
501   /*
502    * Verify if we need to grow the stream
503    */
504   if (newSize.s.LowPart > This->streamSize.s.LowPart)
505   {
506     /* grow stream */
507    IStream_SetSize(iface, newSize);
508   }
509   
510   /*
511    * Lock the buffer in position and copy the data.
512    */
513   supportBuffer = GlobalLock(This->supportHandle);
514
515   memcpy((char *) supportBuffer+This->currentPosition.s.LowPart, pv, cb);  
516
517   /*
518    * Move the current position to the new position
519    */
520   This->currentPosition.s.LowPart+=cb;
521
522   /*
523    * Return the number of bytes read.
524    */
525   *pcbWritten = cb;
526
527   /*
528    * Cleanup
529    */
530   GlobalUnlock(This->supportHandle);
531   
532   return S_OK;
533 }
534
535 /***
536  * This method is part of the IStream interface.
537  *
538  * It will move the current stream pointer according to the parameters
539  * given.
540  *
541  * See the documentation of IStream for more info.
542  */        
543 HRESULT WINAPI HGLOBALStreamImpl_Seek( 
544                   IStream*      iface,
545                   LARGE_INTEGER   dlibMove,         /* [in] */ 
546                   DWORD           dwOrigin,         /* [in] */ 
547                   ULARGE_INTEGER* plibNewPosition) /* [out] */
548 {
549   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
550
551   ULARGE_INTEGER newPosition;
552
553   TRACE("(%p, %ld, %ld, %p)\n", iface,
554         dlibMove.s.LowPart, dwOrigin, plibNewPosition);
555
556   /* 
557    * The caller is allowed to pass in NULL as the new position return value.
558    * If it happens, we assign it to a dynamic variable to avoid special cases
559    * in the code below.
560    */
561   if (plibNewPosition == 0)
562   {
563     plibNewPosition = &newPosition;
564   }
565
566   /*
567    * The file pointer is moved depending on the given "function"
568    * parameter.
569    */
570   switch (dwOrigin)
571   {
572     case STREAM_SEEK_SET:
573       plibNewPosition->s.HighPart = 0;
574       plibNewPosition->s.LowPart  = 0;
575       break;
576     case STREAM_SEEK_CUR:
577       *plibNewPosition = This->currentPosition;
578       break;
579     case STREAM_SEEK_END:
580       *plibNewPosition = This->streamSize;
581       break;
582     default:
583       return STG_E_INVALIDFUNCTION;
584   }
585
586   /*
587    * We don't support files with offsets of 64 bits.
588    */
589   assert(dlibMove.s.HighPart == 0);
590
591   /*
592    * Check if we end-up before the beginning of the file. That should trigger an
593    * error.
594    */
595   if ( (dlibMove.s.LowPart<0) && (plibNewPosition->s.LowPart < (ULONG)(-dlibMove.s.LowPart)) )
596   {
597     /*
598      * I don't know what error to send there.
599      */
600     return E_FAIL;
601   }
602
603   /*
604    * Move the actual file pointer
605    * If the file pointer ends-up after the end of the stream, the next Write operation will
606    * make the file larger. This is how it is documented.
607    */
608   plibNewPosition->s.LowPart += dlibMove.s.LowPart;
609   This->currentPosition = *plibNewPosition;
610  
611   return S_OK;
612 }
613
614 /***
615  * This method is part of the IStream interface.
616  *
617  * It will change the size of a stream.
618  *
619  * TODO: Switch from small blocks to big blocks and vice versa.
620  *
621  * See the documentation of IStream for more info.
622  */
623 HRESULT WINAPI HGLOBALStreamImpl_SetSize( 
624                                      IStream*      iface,
625                                      ULARGE_INTEGER  libNewSize)   /* [in] */ 
626 {
627   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
628
629   TRACE("(%p, %ld)\n", iface, libNewSize.s.LowPart);
630
631   /*
632    * As documented.
633    */
634   if (libNewSize.s.HighPart != 0)
635     return STG_E_INVALIDFUNCTION;
636   
637   if (This->streamSize.s.LowPart == libNewSize.s.LowPart)
638     return S_OK;
639
640   /*
641    * Re allocate the HGlobal to fit the new size of the stream.
642    */
643   This->supportHandle = GlobalReAlloc(This->supportHandle, 
644                                       libNewSize.s.LowPart,
645                                       0);
646
647   This->streamSize.s.LowPart = libNewSize.s.LowPart;
648   
649   return S_OK;
650 }
651         
652 /***
653  * This method is part of the IStream interface.
654  *
655  * It will copy the 'cb' Bytes to 'pstm' IStream.
656  *
657  * See the documentation of IStream for more info.
658  */
659 HRESULT WINAPI HGLOBALStreamImpl_CopyTo( 
660                                     IStream*      iface,
661                                     IStream*      pstm,         /* [unique][in] */ 
662                                     ULARGE_INTEGER  cb,           /* [in] */         
663                                     ULARGE_INTEGER* pcbRead,      /* [out] */        
664                                     ULARGE_INTEGER* pcbWritten)   /* [out] */        
665 {
666   HRESULT        hr = S_OK;
667   BYTE           tmpBuffer[128];
668   ULONG          bytesRead, bytesWritten, copySize;
669   ULARGE_INTEGER totalBytesRead;
670   ULARGE_INTEGER totalBytesWritten;
671
672   TRACE("(%p, %p, %ld, %p, %p)\n", iface, pstm, 
673         cb.s.LowPart, pcbRead, pcbWritten);
674
675   /*
676    * Sanity check
677    */
678   if ( pstm == 0 )
679     return STG_E_INVALIDPOINTER;
680
681   totalBytesRead.s.LowPart = totalBytesRead.s.HighPart = 0;
682   totalBytesWritten.s.LowPart = totalBytesWritten.s.HighPart = 0;
683
684   /*
685    * use stack to store data temporarly
686    * there is surely more performant way of doing it, for now this basic
687    * implementation will do the job
688    */
689   while ( cb.s.LowPart > 0 )
690   {
691     if ( cb.s.LowPart >= 128 )
692       copySize = 128;
693     else
694       copySize = cb.s.LowPart;
695     
696     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
697
698     totalBytesRead.s.LowPart += bytesRead;
699     
700     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
701
702     totalBytesWritten.s.LowPart += bytesWritten;
703
704     /*
705      * Check that read & write operations were succesfull
706      */
707     if (bytesRead != bytesWritten)
708     {
709       hr = STG_E_MEDIUMFULL;
710       break;
711     }
712     
713     if (bytesRead!=copySize)
714       cb.s.LowPart = 0;
715     else
716       cb.s.LowPart -= bytesRead;
717   }
718
719   /*
720    * Update number of bytes read and written
721    */
722   if (pcbRead)
723   {
724     pcbRead->s.LowPart = totalBytesRead.s.LowPart;
725     pcbRead->s.HighPart = totalBytesRead.s.HighPart;
726   }
727
728   if (pcbWritten)
729   {
730     pcbWritten->s.LowPart = totalBytesWritten.s.LowPart;
731     pcbWritten->s.HighPart = totalBytesWritten.s.HighPart;
732   }
733   return hr;
734 }
735
736 /***
737  * This method is part of the IStream interface.
738  *
739  * For streams supported by HGLOBALS, this function does nothing. 
740  * This is what the documentation tells us.
741  *
742  * See the documentation of IStream for more info.
743  */        
744 HRESULT WINAPI HGLOBALStreamImpl_Commit( 
745                   IStream*      iface,
746                   DWORD         grfCommitFlags)  /* [in] */ 
747 {
748   return S_OK;
749 }
750
751 /***
752  * This method is part of the IStream interface.
753  *
754  * For streams supported by HGLOBALS, this function does nothing. 
755  * This is what the documentation tells us.
756  *
757  * See the documentation of IStream for more info.
758  */        
759 HRESULT WINAPI HGLOBALStreamImpl_Revert( 
760                   IStream* iface)
761 {
762   return S_OK;
763 }
764
765 /***
766  * This method is part of the IStream interface.
767  *
768  * For streams supported by HGLOBALS, this function does nothing. 
769  * This is what the documentation tells us.
770  *
771  * See the documentation of IStream for more info.
772  */        
773 HRESULT WINAPI HGLOBALStreamImpl_LockRegion( 
774                   IStream*       iface,
775                   ULARGE_INTEGER libOffset,   /* [in] */ 
776                   ULARGE_INTEGER cb,          /* [in] */ 
777                   DWORD          dwLockType)  /* [in] */ 
778 {
779   return S_OK;
780 }
781
782 /*
783  * This method is part of the IStream interface.
784  *
785  * For streams supported by HGLOBALS, this function does nothing. 
786  * This is what the documentation tells us.
787  *
788  * See the documentation of IStream for more info.
789  */        
790 HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion( 
791                   IStream*       iface,
792                   ULARGE_INTEGER libOffset,   /* [in] */ 
793                   ULARGE_INTEGER cb,          /* [in] */ 
794                   DWORD          dwLockType)  /* [in] */ 
795 {
796   return S_OK;
797 }
798
799 /***
800  * This method is part of the IStream interface.
801  *
802  * This method returns information about the current
803  * stream.
804  *
805  * See the documentation of IStream for more info.
806  */        
807 HRESULT WINAPI HGLOBALStreamImpl_Stat( 
808                   IStream*     iface,
809                   STATSTG*     pstatstg,     /* [out] */
810                   DWORD        grfStatFlag)  /* [in] */ 
811 {
812   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
813
814   memset(pstatstg, 0, sizeof(STATSTG));
815
816   pstatstg->pwcsName = NULL;
817   pstatstg->type     = STGTY_STREAM;
818   pstatstg->cbSize   = This->streamSize;
819
820   return S_OK;
821 }
822         
823 HRESULT WINAPI HGLOBALStreamImpl_Clone( 
824                   IStream*     iface,
825                   IStream**    ppstm) /* [out] */ 
826 {
827   FIXME("not implemented!\n");
828   return E_NOTIMPL;
829 }