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