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