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