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