Fix the case of product and company names.
[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 <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "windef.h"
35 #include "winbase.h"
36 #include "objbase.h"
37 #include "ole2.h"
38 #include "winerror.h"
39 #include "winreg.h"
40 #include "winternl.h"
41
42 #include "wine/debug.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(storage);
45
46 /****************************************************************************
47  * HGLOBALStreamImpl definition.
48  *
49  * This class imlements the IStream inteface and represents a stream
50  * supported by an HGLOBAL pointer.
51  */
52 struct HGLOBALStreamImpl
53 {
54   ICOM_VFIELD(IStream);  /* Needs to be the first item in the stuct
55                           * since we want to cast this in a IStream pointer */
56
57   /*
58    * Reference count
59    */
60   ULONG              ref;
61
62   /*
63    * Support for the stream
64    */
65   HGLOBAL supportHandle;
66
67   /*
68    * This flag is TRUE if the HGLOBAL is destroyed when the stream
69    * is finally released.
70    */
71   BOOL    deleteOnRelease;
72
73   /*
74    * Helper variable that contains the size of the stream
75    */
76   ULARGE_INTEGER     streamSize;
77
78   /*
79    * This is the current position of the cursor in the stream
80    */
81   ULARGE_INTEGER     currentPosition;
82 };
83
84 typedef struct HGLOBALStreamImpl HGLOBALStreamImpl;
85
86 /*
87  * Method definition for the StgStreamImpl class.
88  */
89 HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
90                 HGLOBAL  hGlobal,
91                 BOOL     fDeleteOnRelease);
92
93 void HGLOBALStreamImpl_Destroy(
94                 HGLOBALStreamImpl* This);
95
96 void HGLOBALStreamImpl_OpenBlockChain(
97                 HGLOBALStreamImpl* This);
98
99 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
100                 IStream*      iface,
101                 REFIID         riid,            /* [in] */
102                 void**         ppvObject);  /* [iid_is][out] */
103
104 ULONG WINAPI HGLOBALStreamImpl_AddRef(
105                 IStream*      iface);
106
107 ULONG WINAPI HGLOBALStreamImpl_Release(
108                 IStream*      iface);
109
110 HRESULT WINAPI HGLOBALStreamImpl_Read(
111                 IStream*      iface,
112                 void*          pv,        /* [length_is][size_is][out] */
113                 ULONG          cb,        /* [in] */
114                 ULONG*         pcbRead);  /* [out] */
115
116 HRESULT WINAPI HGLOBALStreamImpl_Write(
117                 IStream*      iface,
118                 const void*    pv,          /* [size_is][in] */
119                 ULONG          cb,          /* [in] */
120                 ULONG*         pcbWritten); /* [out] */
121
122 HRESULT WINAPI HGLOBALStreamImpl_Seek(
123                 IStream*      iface,
124                 LARGE_INTEGER   dlibMove,         /* [in] */
125                 DWORD           dwOrigin,         /* [in] */
126                 ULARGE_INTEGER* plibNewPosition); /* [out] */
127
128 HRESULT WINAPI HGLOBALStreamImpl_SetSize(
129                 IStream*      iface,
130                 ULARGE_INTEGER  libNewSize);  /* [in] */
131
132 HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
133                 IStream*      iface,
134                 IStream*      pstm,         /* [unique][in] */
135                 ULARGE_INTEGER  cb,           /* [in] */
136                 ULARGE_INTEGER* pcbRead,      /* [out] */
137                 ULARGE_INTEGER* pcbWritten);  /* [out] */
138
139 HRESULT WINAPI HGLOBALStreamImpl_Commit(
140                 IStream*      iface,
141                 DWORD           grfCommitFlags); /* [in] */
142
143 HRESULT WINAPI HGLOBALStreamImpl_Revert(
144                 IStream*  iface);
145
146 HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
147                 IStream*     iface,
148                 ULARGE_INTEGER libOffset,   /* [in] */
149                 ULARGE_INTEGER cb,          /* [in] */
150                 DWORD          dwLockType); /* [in] */
151
152 HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
153                 IStream*     iface,
154                 ULARGE_INTEGER libOffset,   /* [in] */
155                 ULARGE_INTEGER cb,          /* [in] */
156                 DWORD          dwLockType); /* [in] */
157
158 HRESULT WINAPI HGLOBALStreamImpl_Stat(
159                 IStream*     iface,
160                 STATSTG*       pstatstg,     /* [out] */
161                 DWORD          grfStatFlag); /* [in] */
162
163 HRESULT WINAPI HGLOBALStreamImpl_Clone(
164                 IStream*     iface,
165                 IStream**    ppstm);       /* [out] */
166
167
168 /*
169  * Virtual function table for the HGLOBALStreamImpl class.
170  */
171 static ICOM_VTABLE(IStream) HGLOBALStreamImpl_Vtbl =
172 {
173     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
174     HGLOBALStreamImpl_QueryInterface,
175     HGLOBALStreamImpl_AddRef,
176     HGLOBALStreamImpl_Release,
177     HGLOBALStreamImpl_Read,
178     HGLOBALStreamImpl_Write,
179     HGLOBALStreamImpl_Seek,
180     HGLOBALStreamImpl_SetSize,
181     HGLOBALStreamImpl_CopyTo,
182     HGLOBALStreamImpl_Commit,
183     HGLOBALStreamImpl_Revert,
184     HGLOBALStreamImpl_LockRegion,
185     HGLOBALStreamImpl_UnlockRegion,
186     HGLOBALStreamImpl_Stat,
187     HGLOBALStreamImpl_Clone
188 };
189
190 /***********************************************************************
191  *           CreateStreamOnHGlobal     [OLE32.61]
192  */
193 HRESULT WINAPI CreateStreamOnHGlobal(
194                 HGLOBAL   hGlobal,
195                 BOOL      fDeleteOnRelease,
196                 LPSTREAM* ppstm)
197 {
198   HGLOBALStreamImpl* newStream;
199
200   newStream = HGLOBALStreamImpl_Construct(hGlobal,
201                                           fDeleteOnRelease);
202
203   if (newStream!=NULL)
204   {
205     return IUnknown_QueryInterface((IUnknown*)newStream,
206                                    &IID_IStream,
207                                    (void**)ppstm);
208   }
209
210   return E_OUTOFMEMORY;
211 }
212
213 /***********************************************************************
214  *           GetHGlobalFromStream     [OLE32.71]
215  */
216 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
217 {
218   HGLOBALStreamImpl* pStream;
219
220   if (pstm == NULL)
221     return E_INVALIDARG;
222
223   pStream = (HGLOBALStreamImpl*) pstm;
224
225   /*
226    * Verify that the stream object was created with CreateStreamOnHGlobal.
227    */
228   if (pStream->lpVtbl == &HGLOBALStreamImpl_Vtbl)
229     *phglobal = pStream->supportHandle;
230   else
231   {
232     *phglobal = 0;
233     return E_INVALIDARG;
234   }
235
236   return S_OK;
237 }
238
239 /******************************************************************************
240 ** HGLOBALStreamImpl implementation
241 */
242
243 /***
244  * This is the constructor for the HGLOBALStreamImpl class.
245  *
246  * Params:
247  *    hGlobal          - Handle that will support the stream. can be NULL.
248  *    fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released
249  *                       when the IStream object is destroyed.
250  */
251 HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
252                 HGLOBAL  hGlobal,
253                 BOOL     fDeleteOnRelease)
254 {
255   HGLOBALStreamImpl* newStream;
256
257   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
258
259   if (newStream!=0)
260   {
261     /*
262      * Set-up the virtual function table and reference count.
263      */
264     newStream->lpVtbl = &HGLOBALStreamImpl_Vtbl;
265     newStream->ref    = 0;
266
267     /*
268      * Initialize the support.
269      */
270     newStream->supportHandle = hGlobal;
271     newStream->deleteOnRelease = fDeleteOnRelease;
272
273     /*
274      * This method will allocate a handle if one is not supplied.
275      */
276     if (!newStream->supportHandle)
277     {
278       newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
279                                              GMEM_SHARE, 0);
280     }
281
282     /*
283      * Start the stream at the beginning.
284      */
285     newStream->currentPosition.s.HighPart = 0;
286     newStream->currentPosition.s.LowPart = 0;
287
288     /*
289      * Initialize the size of the stream to the size of the handle.
290      */
291     newStream->streamSize.s.HighPart = 0;
292     newStream->streamSize.s.LowPart  = GlobalSize(newStream->supportHandle);
293   }
294
295   return newStream;
296 }
297
298 /***
299  * This is the destructor of the HGLOBALStreamImpl class.
300  *
301  * This method will clean-up all the resources used-up by the given HGLOBALStreamImpl
302  * class. The pointer passed-in to this function will be freed and will not
303  * be valid anymore.
304  */
305 void HGLOBALStreamImpl_Destroy(HGLOBALStreamImpl* This)
306 {
307   TRACE("(%p)\n", This);
308
309   /*
310    * Release the HGlobal if the constructor asked for that.
311    */
312   if (This->deleteOnRelease)
313   {
314     GlobalFree(This->supportHandle);
315     This->supportHandle=0;
316   }
317
318   /*
319    * Finally, free the memory used-up by the class.
320    */
321   HeapFree(GetProcessHeap(), 0, This);
322 }
323
324 /***
325  * This implements the IUnknown method QueryInterface for this
326  * class
327  */
328 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
329                   IStream*     iface,
330                   REFIID         riid,        /* [in] */
331                   void**         ppvObject)   /* [iid_is][out] */
332 {
333   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
334
335   /*
336    * Perform a sanity check on the parameters.
337    */
338   if (ppvObject==0)
339     return E_INVALIDARG;
340
341   /*
342    * Initialize the return parameter.
343    */
344   *ppvObject = 0;
345
346   /*
347    * Compare the riid with the interface IDs implemented by this object.
348    */
349   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
350   {
351     *ppvObject = (IStream*)This;
352   }
353   else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
354   {
355     *ppvObject = (IStream*)This;
356   }
357
358   /*
359    * Check that we obtained an interface.
360    */
361   if ((*ppvObject)==0)
362     return E_NOINTERFACE;
363
364   /*
365    * Query Interface always increases the reference count by one when it is
366    * successful
367    */
368   HGLOBALStreamImpl_AddRef(iface);
369
370   return S_OK;
371 }
372
373 /***
374  * This implements the IUnknown method AddRef for this
375  * class
376  */
377 ULONG WINAPI HGLOBALStreamImpl_AddRef(
378                 IStream* iface)
379 {
380   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
381
382   This->ref++;
383
384   return This->ref;
385 }
386
387 /***
388  * This implements the IUnknown method Release for this
389  * class
390  */
391 ULONG WINAPI HGLOBALStreamImpl_Release(
392                 IStream* iface)
393 {
394   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
395
396   ULONG newRef;
397
398   This->ref--;
399
400   newRef = This->ref;
401
402   /*
403    * If the reference count goes down to 0, perform suicide.
404    */
405   if (newRef==0)
406   {
407     HGLOBALStreamImpl_Destroy(This);
408   }
409
410   return newRef;
411 }
412
413 /***
414  * This method is part of the ISequentialStream interface.
415  *
416  * If reads a block of information from the stream at the current
417  * position. It then moves the current position at the end of the
418  * read block
419  *
420  * See the documentation of ISequentialStream for more info.
421  */
422 HRESULT WINAPI HGLOBALStreamImpl_Read(
423                   IStream*     iface,
424                   void*          pv,        /* [length_is][size_is][out] */
425                   ULONG          cb,        /* [in] */
426                   ULONG*         pcbRead)   /* [out] */
427 {
428   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
429
430   void* supportBuffer;
431   ULONG bytesReadBuffer;
432   ULONG bytesToReadFromBuffer;
433
434   TRACE("(%p, %p, %ld, %p)\n", iface,
435         pv, cb, pcbRead);
436
437   /*
438    * If the caller is not interested in the nubmer of bytes read,
439    * we use another buffer to avoid "if" statements in the code.
440    */
441   if (pcbRead==0)
442     pcbRead = &bytesReadBuffer;
443
444   /*
445    * Using the known size of the stream, calculate the number of bytes
446    * to read from the block chain
447    */
448   bytesToReadFromBuffer = min( This->streamSize.s.LowPart - This->currentPosition.s.LowPart, cb);
449
450   /*
451    * Lock the buffer in position and copy the data.
452    */
453   supportBuffer = GlobalLock(This->supportHandle);
454
455   memcpy(pv, (char *) supportBuffer+This->currentPosition.s.LowPart, bytesToReadFromBuffer);
456
457   /*
458    * Move the current position to the new position
459    */
460   This->currentPosition.s.LowPart+=bytesToReadFromBuffer;
461
462   /*
463    * Return the number of bytes read.
464    */
465   *pcbRead = bytesToReadFromBuffer;
466
467   /*
468    * Cleanup
469    */
470   GlobalUnlock(This->supportHandle);
471
472   /*
473    * The function returns S_OK if the buffer was filled completely
474    * it returns S_FALSE if the end of the stream is reached before the
475    * buffer is filled
476    */
477   if(*pcbRead == cb)
478     return S_OK;
479
480   return S_FALSE;
481 }
482
483 /***
484  * This method is part of the ISequentialStream interface.
485  *
486  * It writes a block of information to the stream at the current
487  * position. It then moves the current position at the end of the
488  * written block. If the stream is too small to fit the block,
489  * the stream is grown to fit.
490  *
491  * See the documentation of ISequentialStream for more info.
492  */
493 HRESULT WINAPI HGLOBALStreamImpl_Write(
494                   IStream*     iface,
495                   const void*    pv,          /* [size_is][in] */
496                   ULONG          cb,          /* [in] */
497                   ULONG*         pcbWritten)  /* [out] */
498 {
499   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
500
501   void*          supportBuffer;
502   ULARGE_INTEGER newSize;
503   ULONG          bytesWritten = 0;
504
505   TRACE("(%p, %p, %ld, %p)\n", iface,
506         pv, cb, pcbWritten);
507
508   /*
509    * If the caller is not interested in the number of bytes written,
510    * we use another buffer to avoid "if" statements in the code.
511    */
512   if (pcbWritten == 0)
513     pcbWritten = &bytesWritten;
514
515   if (cb == 0)
516   {
517     return S_OK;
518   }
519   else
520   {
521     newSize.s.HighPart = 0;
522     newSize.s.LowPart = This->currentPosition.s.LowPart + cb;
523   }
524
525   /*
526    * Verify if we need to grow the stream
527    */
528   if (newSize.s.LowPart > This->streamSize.s.LowPart)
529   {
530     /* grow stream */
531    IStream_SetSize(iface, newSize);
532   }
533
534   /*
535    * Lock the buffer in position and copy the data.
536    */
537   supportBuffer = GlobalLock(This->supportHandle);
538
539   memcpy((char *) supportBuffer+This->currentPosition.s.LowPart, pv, cb);
540
541   /*
542    * Move the current position to the new position
543    */
544   This->currentPosition.s.LowPart+=cb;
545
546   /*
547    * Return the number of bytes read.
548    */
549   *pcbWritten = cb;
550
551   /*
552    * Cleanup
553    */
554   GlobalUnlock(This->supportHandle);
555
556   return S_OK;
557 }
558
559 /***
560  * This method is part of the IStream interface.
561  *
562  * It will move the current stream pointer according to the parameters
563  * given.
564  *
565  * See the documentation of IStream for more info.
566  */
567 HRESULT WINAPI HGLOBALStreamImpl_Seek(
568                   IStream*      iface,
569                   LARGE_INTEGER   dlibMove,         /* [in] */
570                   DWORD           dwOrigin,         /* [in] */
571                   ULARGE_INTEGER* plibNewPosition) /* [out] */
572 {
573   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
574
575   ULARGE_INTEGER newPosition;
576
577   TRACE("(%p, %ld, %ld, %p)\n", iface,
578         dlibMove.s.LowPart, dwOrigin, plibNewPosition);
579
580   /*
581    * The file pointer is moved depending on the given "function"
582    * parameter.
583    */
584   switch (dwOrigin)
585   {
586     case STREAM_SEEK_SET:
587       newPosition.s.HighPart = 0;
588       newPosition.s.LowPart = 0;
589       break;
590     case STREAM_SEEK_CUR:
591       newPosition = This->currentPosition;
592       break;
593     case STREAM_SEEK_END:
594       newPosition = This->streamSize;
595       break;
596     default:
597       return STG_E_INVALIDFUNCTION;
598   }
599
600   /*
601    * Move the actual file pointer
602    * If the file pointer ends-up after the end of the stream, the next Write operation will
603    * make the file larger. This is how it is documented.
604    */
605   newPosition.QuadPart = RtlLargeIntegerAdd(newPosition.QuadPart, dlibMove.QuadPart);
606   if (newPosition.QuadPart < 0) return STG_E_INVALIDFUNCTION;
607
608   if (plibNewPosition) *plibNewPosition = newPosition;
609   This->currentPosition = newPosition;
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   ULARGE_INTEGER dummy;
828   LARGE_INTEGER offset;
829   HRESULT hr;
830   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
831   TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface,This->deleteOnRelease,(long)This->currentPosition.QuadPart);
832   hr=CreateStreamOnHGlobal(This->supportHandle, FALSE, ppstm);
833   if(FAILED(hr))
834     return hr;
835   offset.QuadPart=(LONGLONG)This->currentPosition.QuadPart;
836   HGLOBALStreamImpl_Seek(*ppstm,offset,STREAM_SEEK_SET,&dummy);
837   return S_OK;
838 }