- renamed file_operation_delete and to shfileops_delete
[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 COBJMACROS
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winuser.h"
39 #include "objbase.h"
40 #include "ole2.h"
41 #include "winerror.h"
42 #include "winreg.h"
43 #include "winternl.h"
44
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(storage);
48
49 /****************************************************************************
50  * HGLOBALStreamImpl definition.
51  *
52  * This class imlements the IStream inteface and represents a stream
53  * supported by an HGLOBAL pointer.
54  */
55 struct HGLOBALStreamImpl
56 {
57   IStreamVtbl *lpVtbl;   /* Needs to be the first item in the stuct
58                           * since we want to cast this in a IStream pointer */
59
60   /*
61    * Reference count
62    */
63   ULONG              ref;
64
65   /*
66    * Support for the stream
67    */
68   HGLOBAL supportHandle;
69
70   /*
71    * This flag is TRUE if the HGLOBAL is destroyed when the stream
72    * is finally released.
73    */
74   BOOL    deleteOnRelease;
75
76   /*
77    * Helper variable that contains the size of the stream
78    */
79   ULARGE_INTEGER     streamSize;
80
81   /*
82    * This is the current position of the cursor in the stream
83    */
84   ULARGE_INTEGER     currentPosition;
85 };
86
87 typedef struct HGLOBALStreamImpl HGLOBALStreamImpl;
88
89 /*
90  * Method definition for the StgStreamImpl class.
91  */
92 HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
93                 HGLOBAL  hGlobal,
94                 BOOL     fDeleteOnRelease);
95
96 void HGLOBALStreamImpl_Destroy(
97                 HGLOBALStreamImpl* This);
98
99 void HGLOBALStreamImpl_OpenBlockChain(
100                 HGLOBALStreamImpl* This);
101
102 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
103                 IStream*      iface,
104                 REFIID         riid,            /* [in] */
105                 void**         ppvObject);  /* [iid_is][out] */
106
107 ULONG WINAPI HGLOBALStreamImpl_AddRef(
108                 IStream*      iface);
109
110 ULONG WINAPI HGLOBALStreamImpl_Release(
111                 IStream*      iface);
112
113 HRESULT WINAPI HGLOBALStreamImpl_Read(
114                 IStream*      iface,
115                 void*          pv,        /* [length_is][size_is][out] */
116                 ULONG          cb,        /* [in] */
117                 ULONG*         pcbRead);  /* [out] */
118
119 HRESULT WINAPI HGLOBALStreamImpl_Write(
120                 IStream*      iface,
121                 const void*    pv,          /* [size_is][in] */
122                 ULONG          cb,          /* [in] */
123                 ULONG*         pcbWritten); /* [out] */
124
125 HRESULT WINAPI HGLOBALStreamImpl_Seek(
126                 IStream*      iface,
127                 LARGE_INTEGER   dlibMove,         /* [in] */
128                 DWORD           dwOrigin,         /* [in] */
129                 ULARGE_INTEGER* plibNewPosition); /* [out] */
130
131 HRESULT WINAPI HGLOBALStreamImpl_SetSize(
132                 IStream*      iface,
133                 ULARGE_INTEGER  libNewSize);  /* [in] */
134
135 HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
136                 IStream*      iface,
137                 IStream*      pstm,         /* [unique][in] */
138                 ULARGE_INTEGER  cb,           /* [in] */
139                 ULARGE_INTEGER* pcbRead,      /* [out] */
140                 ULARGE_INTEGER* pcbWritten);  /* [out] */
141
142 HRESULT WINAPI HGLOBALStreamImpl_Commit(
143                 IStream*      iface,
144                 DWORD           grfCommitFlags); /* [in] */
145
146 HRESULT WINAPI HGLOBALStreamImpl_Revert(
147                 IStream*  iface);
148
149 HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
150                 IStream*     iface,
151                 ULARGE_INTEGER libOffset,   /* [in] */
152                 ULARGE_INTEGER cb,          /* [in] */
153                 DWORD          dwLockType); /* [in] */
154
155 HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
156                 IStream*     iface,
157                 ULARGE_INTEGER libOffset,   /* [in] */
158                 ULARGE_INTEGER cb,          /* [in] */
159                 DWORD          dwLockType); /* [in] */
160
161 HRESULT WINAPI HGLOBALStreamImpl_Stat(
162                 IStream*     iface,
163                 STATSTG*       pstatstg,     /* [out] */
164                 DWORD          grfStatFlag); /* [in] */
165
166 HRESULT WINAPI HGLOBALStreamImpl_Clone(
167                 IStream*     iface,
168                 IStream**    ppstm);       /* [out] */
169
170
171 /*
172  * Virtual function table for the HGLOBALStreamImpl class.
173  */
174 static IStreamVtbl HGLOBALStreamImpl_Vtbl =
175 {
176     HGLOBALStreamImpl_QueryInterface,
177     HGLOBALStreamImpl_AddRef,
178     HGLOBALStreamImpl_Release,
179     HGLOBALStreamImpl_Read,
180     HGLOBALStreamImpl_Write,
181     HGLOBALStreamImpl_Seek,
182     HGLOBALStreamImpl_SetSize,
183     HGLOBALStreamImpl_CopyTo,
184     HGLOBALStreamImpl_Commit,
185     HGLOBALStreamImpl_Revert,
186     HGLOBALStreamImpl_LockRegion,
187     HGLOBALStreamImpl_UnlockRegion,
188     HGLOBALStreamImpl_Stat,
189     HGLOBALStreamImpl_Clone
190 };
191
192 /***********************************************************************
193  *           CreateStreamOnHGlobal     [OLE32.@]
194  */
195 HRESULT WINAPI CreateStreamOnHGlobal(
196                 HGLOBAL   hGlobal,
197                 BOOL      fDeleteOnRelease,
198                 LPSTREAM* ppstm)
199 {
200   HGLOBALStreamImpl* newStream;
201
202   newStream = HGLOBALStreamImpl_Construct(hGlobal,
203                                           fDeleteOnRelease);
204
205   if (newStream!=NULL)
206   {
207     return IUnknown_QueryInterface((IUnknown*)newStream,
208                                    &IID_IStream,
209                                    (void**)ppstm);
210   }
211
212   return E_OUTOFMEMORY;
213 }
214
215 /***********************************************************************
216  *           GetHGlobalFromStream     [OLE32.@]
217  */
218 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
219 {
220   HGLOBALStreamImpl* pStream;
221
222   if (pstm == NULL)
223     return E_INVALIDARG;
224
225   pStream = (HGLOBALStreamImpl*) pstm;
226
227   /*
228    * Verify that the stream object was created with CreateStreamOnHGlobal.
229    */
230   if (pStream->lpVtbl == &HGLOBALStreamImpl_Vtbl)
231     *phglobal = pStream->supportHandle;
232   else
233   {
234     *phglobal = 0;
235     return E_INVALIDARG;
236   }
237
238   return S_OK;
239 }
240
241 /******************************************************************************
242 ** HGLOBALStreamImpl implementation
243 */
244
245 /***
246  * This is the constructor for the HGLOBALStreamImpl class.
247  *
248  * Params:
249  *    hGlobal          - Handle that will support the stream. can be NULL.
250  *    fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released
251  *                       when the IStream object is destroyed.
252  */
253 HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
254                 HGLOBAL  hGlobal,
255                 BOOL     fDeleteOnRelease)
256 {
257   HGLOBALStreamImpl* newStream;
258
259   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
260
261   if (newStream!=0)
262   {
263     /*
264      * Set-up the virtual function table and reference count.
265      */
266     newStream->lpVtbl = &HGLOBALStreamImpl_Vtbl;
267     newStream->ref    = 0;
268
269     /*
270      * Initialize the support.
271      */
272     newStream->supportHandle = hGlobal;
273     newStream->deleteOnRelease = fDeleteOnRelease;
274
275     /*
276      * This method will allocate a handle if one is not supplied.
277      */
278     if (!newStream->supportHandle)
279     {
280       newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
281                                              GMEM_SHARE, 0);
282     }
283
284     /*
285      * Start the stream at the beginning.
286      */
287     newStream->currentPosition.u.HighPart = 0;
288     newStream->currentPosition.u.LowPart = 0;
289
290     /*
291      * Initialize the size of the stream to the size of the handle.
292      */
293     newStream->streamSize.u.HighPart = 0;
294     newStream->streamSize.u.LowPart  = GlobalSize(newStream->supportHandle);
295   }
296
297   return newStream;
298 }
299
300 /***
301  * This is the destructor of the HGLOBALStreamImpl class.
302  *
303  * This method will clean-up all the resources used-up by the given HGLOBALStreamImpl
304  * class. The pointer passed-in to this function will be freed and will not
305  * be valid anymore.
306  */
307 void HGLOBALStreamImpl_Destroy(HGLOBALStreamImpl* This)
308 {
309   TRACE("(%p)\n", This);
310
311   /*
312    * Release the HGlobal if the constructor asked for that.
313    */
314   if (This->deleteOnRelease)
315   {
316     GlobalFree(This->supportHandle);
317     This->supportHandle=0;
318   }
319
320   /*
321    * Finally, free the memory used-up by the class.
322    */
323   HeapFree(GetProcessHeap(), 0, This);
324 }
325
326 /***
327  * This implements the IUnknown method QueryInterface for this
328  * class
329  */
330 HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
331                   IStream*     iface,
332                   REFIID         riid,        /* [in] */
333                   void**         ppvObject)   /* [iid_is][out] */
334 {
335   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
336
337   /*
338    * Perform a sanity check on the parameters.
339    */
340   if (ppvObject==0)
341     return E_INVALIDARG;
342
343   /*
344    * Initialize the return parameter.
345    */
346   *ppvObject = 0;
347
348   /*
349    * Compare the riid with the interface IDs implemented by this object.
350    */
351   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
352   {
353     *ppvObject = (IStream*)This;
354   }
355   else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
356   {
357     *ppvObject = (IStream*)This;
358   }
359
360   /*
361    * Check that we obtained an interface.
362    */
363   if ((*ppvObject)==0)
364     return E_NOINTERFACE;
365
366   /*
367    * Query Interface always increases the reference count by one when it is
368    * successful
369    */
370   HGLOBALStreamImpl_AddRef(iface);
371
372   return S_OK;
373 }
374
375 /***
376  * This implements the IUnknown method AddRef for this
377  * class
378  */
379 ULONG WINAPI HGLOBALStreamImpl_AddRef(
380                 IStream* iface)
381 {
382   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
383   return InterlockedIncrement(&This->ref);
384 }
385
386 /***
387  * This implements the IUnknown method Release for this
388  * class
389  */
390 ULONG WINAPI HGLOBALStreamImpl_Release(
391                 IStream* iface)
392 {
393   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
394   ULONG newRef;
395
396   newRef = InterlockedDecrement(&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.u.LowPart - This->currentPosition.u.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.u.LowPart, bytesToReadFromBuffer);
452
453   /*
454    * Move the current position to the new position
455    */
456   This->currentPosition.u.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.u.HighPart = 0;
518     newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
519   }
520
521   /*
522    * Verify if we need to grow the stream
523    */
524   if (newSize.u.LowPart > This->streamSize.u.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.u.LowPart, pv, cb);
536
537   /*
538    * Move the current position to the new position
539    */
540   This->currentPosition.u.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, %lx%08lx, %ld, %p)\n", iface, dlibMove.u.HighPart,
574         dlibMove.u.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.u.HighPart = 0;
584       newPosition.u.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   if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart) return STG_E_INVALIDFUNCTION;
602
603   newPosition.QuadPart = RtlLargeIntegerAdd(newPosition.QuadPart, dlibMove.QuadPart);
604
605   if (plibNewPosition) *plibNewPosition = newPosition;
606   This->currentPosition = newPosition;
607
608   return S_OK;
609 }
610
611 /***
612  * This method is part of the IStream interface.
613  *
614  * It will change the size of a stream.
615  *
616  * TODO: Switch from small blocks to big blocks and vice versa.
617  *
618  * See the documentation of IStream for more info.
619  */
620 HRESULT WINAPI HGLOBALStreamImpl_SetSize(
621                                      IStream*      iface,
622                                      ULARGE_INTEGER  libNewSize)   /* [in] */
623 {
624   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
625   HGLOBAL supportHandle;
626
627   TRACE("(%p, %ld)\n", iface, libNewSize.u.LowPart);
628
629   /*
630    * As documented.
631    */
632   if (libNewSize.u.HighPart != 0)
633     return STG_E_INVALIDFUNCTION;
634
635   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
636     return S_OK;
637
638   /*
639    * Re allocate the HGlobal to fit the new size of the stream.
640    */
641   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
642
643   if (supportHandle == 0)
644     return STG_E_MEDIUMFULL;
645
646   This->supportHandle = supportHandle;
647   This->streamSize.u.LowPart = libNewSize.u.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.u.LowPart, pcbRead, pcbWritten);
674
675   /*
676    * Sanity check
677    */
678   if ( pstm == 0 )
679     return STG_E_INVALIDPOINTER;
680
681   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
682   totalBytesWritten.u.LowPart = totalBytesWritten.u.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.u.LowPart > 0 )
690   {
691     if ( cb.u.LowPart >= 128 )
692       copySize = 128;
693     else
694       copySize = cb.u.LowPart;
695
696     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
697
698     totalBytesRead.u.LowPart += bytesRead;
699
700     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
701
702     totalBytesWritten.u.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.u.LowPart = 0;
715     else
716       cb.u.LowPart -= bytesRead;
717   }
718
719   /*
720    * Update number of bytes read and written
721    */
722   if (pcbRead)
723   {
724     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
725     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
726   }
727
728   if (pcbWritten)
729   {
730     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
731     pcbWritten->u.HighPart = totalBytesWritten.u.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 }