quartz: Exclude unused headers.
[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 = (IStream*)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 nubmer 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
237   memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
238
239   /*
240    * Move the current position to the new position
241    */
242   This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
243
244   /*
245    * Return the number of bytes read.
246    */
247   *pcbRead = bytesToReadFromBuffer;
248
249   /*
250    * Cleanup
251    */
252   GlobalUnlock(This->supportHandle);
253
254   /*
255    * Always returns S_OK even if the end of the stream is reached before the
256    * buffer is filled
257    */
258
259   return S_OK;
260 }
261
262 /***
263  * This method is part of the ISequentialStream interface.
264  *
265  * It writes a block of information to the stream at the current
266  * position. It then moves the current position at the end of the
267  * written block. If the stream is too small to fit the block,
268  * the stream is grown to fit.
269  *
270  * See the documentation of ISequentialStream for more info.
271  */
272 static HRESULT WINAPI HGLOBALStreamImpl_Write(
273                   IStream*     iface,
274                   const void*    pv,          /* [size_is][in] */
275                   ULONG          cb,          /* [in] */
276                   ULONG*         pcbWritten)  /* [out] */
277 {
278   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
279
280   void*          supportBuffer;
281   ULARGE_INTEGER newSize;
282   ULONG          bytesWritten = 0;
283
284   TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbWritten);
285
286   /*
287    * If the caller is not interested in the number of bytes written,
288    * we use another buffer to avoid "if" statements in the code.
289    */
290   if (pcbWritten == 0)
291     pcbWritten = &bytesWritten;
292
293   if (cb == 0)
294     goto out;
295
296   newSize.u.HighPart = 0;
297   newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
298
299   /*
300    * Verify if we need to grow the stream
301    */
302   if (newSize.u.LowPart > This->streamSize.u.LowPart)
303   {
304     /* grow stream */
305     HRESULT hr = IStream_SetSize(iface, newSize);
306     if (FAILED(hr))
307     {
308       ERR("IStream_SetSize failed with error 0x%08x\n", hr);
309       return hr;
310     }
311   }
312
313   /*
314    * Lock the buffer in position and copy the data.
315    */
316   supportBuffer = GlobalLock(This->supportHandle);
317
318   memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
319
320   /*
321    * Move the current position to the new position
322    */
323   This->currentPosition.u.LowPart+=cb;
324
325   /*
326    * Cleanup
327    */
328   GlobalUnlock(This->supportHandle);
329
330 out:
331   /*
332    * Return the number of bytes read.
333    */
334   *pcbWritten = cb;
335
336   return S_OK;
337 }
338
339 /***
340  * This method is part of the IStream interface.
341  *
342  * It will move the current stream pointer according to the parameters
343  * given.
344  *
345  * See the documentation of IStream for more info.
346  */
347 static HRESULT WINAPI HGLOBALStreamImpl_Seek(
348                   IStream*      iface,
349                   LARGE_INTEGER   dlibMove,         /* [in] */
350                   DWORD           dwOrigin,         /* [in] */
351                   ULARGE_INTEGER* plibNewPosition) /* [out] */
352 {
353   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
354
355   ULARGE_INTEGER newPosition;
356
357   TRACE("(%p, %x%08x, %d, %p)\n", iface, dlibMove.u.HighPart,
358         dlibMove.u.LowPart, dwOrigin, plibNewPosition);
359
360   /*
361    * The file pointer is moved depending on the given "function"
362    * parameter.
363    */
364   switch (dwOrigin)
365   {
366     case STREAM_SEEK_SET:
367       newPosition.u.HighPart = 0;
368       newPosition.u.LowPart = 0;
369       break;
370     case STREAM_SEEK_CUR:
371       newPosition = This->currentPosition;
372       break;
373     case STREAM_SEEK_END:
374       newPosition = This->streamSize;
375       break;
376     default:
377       return STG_E_INVALIDFUNCTION;
378   }
379
380   /*
381    * Move the actual file pointer
382    * If the file pointer ends-up after the end of the stream, the next Write operation will
383    * make the file larger. This is how it is documented.
384    */
385   if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart) return STG_E_INVALIDFUNCTION;
386
387   newPosition.QuadPart = RtlLargeIntegerAdd(newPosition.QuadPart, dlibMove.QuadPart);
388
389   if (plibNewPosition) *plibNewPosition = newPosition;
390   This->currentPosition = newPosition;
391
392   return S_OK;
393 }
394
395 /***
396  * This method is part of the IStream interface.
397  *
398  * It will change the size of a stream.
399  *
400  * TODO: Switch from small blocks to big blocks and vice versa.
401  *
402  * See the documentation of IStream for more info.
403  */
404 static HRESULT WINAPI HGLOBALStreamImpl_SetSize(
405                                      IStream*      iface,
406                                      ULARGE_INTEGER  libNewSize)   /* [in] */
407 {
408   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
409   HGLOBAL supportHandle;
410
411   TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
412
413   /*
414    * HighPart is ignored as shown in tests
415    */
416
417   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
418     return S_OK;
419
420   /*
421    * Re allocate the HGlobal to fit the new size of the stream.
422    */
423   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
424
425   if (supportHandle == 0)
426     return E_OUTOFMEMORY;
427
428   This->supportHandle = supportHandle;
429   This->streamSize.u.LowPart = libNewSize.u.LowPart;
430
431   return S_OK;
432 }
433
434 /***
435  * This method is part of the IStream interface.
436  *
437  * It will copy the 'cb' Bytes to 'pstm' IStream.
438  *
439  * See the documentation of IStream for more info.
440  */
441 static HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
442                                     IStream*      iface,
443                                     IStream*      pstm,         /* [unique][in] */
444                                     ULARGE_INTEGER  cb,           /* [in] */
445                                     ULARGE_INTEGER* pcbRead,      /* [out] */
446                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
447 {
448   HRESULT        hr = S_OK;
449   BYTE           tmpBuffer[128];
450   ULONG          bytesRead, bytesWritten, copySize;
451   ULARGE_INTEGER totalBytesRead;
452   ULARGE_INTEGER totalBytesWritten;
453
454   TRACE("(%p, %p, %d, %p, %p)\n", iface, pstm,
455         cb.u.LowPart, pcbRead, pcbWritten);
456
457   /*
458    * Sanity check
459    */
460   if ( pstm == 0 )
461     return STG_E_INVALIDPOINTER;
462
463   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
464   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
465
466   /*
467    * use stack to store data temporarly
468    * there is surely more performant way of doing it, for now this basic
469    * implementation will do the job
470    */
471   while ( cb.u.LowPart > 0 )
472   {
473     if ( cb.u.LowPart >= 128 )
474       copySize = 128;
475     else
476       copySize = cb.u.LowPart;
477
478     hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
479     if (FAILED(hr))
480         break;
481
482     totalBytesRead.u.LowPart += bytesRead;
483
484     if (bytesRead)
485     {
486         hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
487         if (FAILED(hr))
488             break;
489
490         totalBytesWritten.u.LowPart += bytesWritten;
491     }
492
493     if (bytesRead!=copySize)
494       cb.u.LowPart = 0;
495     else
496       cb.u.LowPart -= bytesRead;
497   }
498
499   /*
500    * Update number of bytes read and written
501    */
502   if (pcbRead)
503   {
504     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
505     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
506   }
507
508   if (pcbWritten)
509   {
510     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
511     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
512   }
513   return hr;
514 }
515
516 /***
517  * This method is part of the IStream interface.
518  *
519  * For streams supported by HGLOBALS, this function does nothing.
520  * This is what the documentation tells us.
521  *
522  * See the documentation of IStream for more info.
523  */
524 static HRESULT WINAPI HGLOBALStreamImpl_Commit(
525                   IStream*      iface,
526                   DWORD         grfCommitFlags)  /* [in] */
527 {
528   return S_OK;
529 }
530
531 /***
532  * This method is part of the IStream interface.
533  *
534  * For streams supported by HGLOBALS, this function does nothing.
535  * This is what the documentation tells us.
536  *
537  * See the documentation of IStream for more info.
538  */
539 static HRESULT WINAPI HGLOBALStreamImpl_Revert(
540                   IStream* iface)
541 {
542   return S_OK;
543 }
544
545 /***
546  * This method is part of the IStream interface.
547  *
548  * For streams supported by HGLOBALS, this function does nothing.
549  * This is what the documentation tells us.
550  *
551  * See the documentation of IStream for more info.
552  */
553 static HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
554                   IStream*       iface,
555                   ULARGE_INTEGER libOffset,   /* [in] */
556                   ULARGE_INTEGER cb,          /* [in] */
557                   DWORD          dwLockType)  /* [in] */
558 {
559   return STG_E_INVALIDFUNCTION;
560 }
561
562 /*
563  * This method is part of the IStream interface.
564  *
565  * For streams supported by HGLOBALS, this function does nothing.
566  * This is what the documentation tells us.
567  *
568  * See the documentation of IStream for more info.
569  */
570 static HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
571                   IStream*       iface,
572                   ULARGE_INTEGER libOffset,   /* [in] */
573                   ULARGE_INTEGER cb,          /* [in] */
574                   DWORD          dwLockType)  /* [in] */
575 {
576   return S_OK;
577 }
578
579 /***
580  * This method is part of the IStream interface.
581  *
582  * This method returns information about the current
583  * stream.
584  *
585  * See the documentation of IStream for more info.
586  */
587 static HRESULT WINAPI HGLOBALStreamImpl_Stat(
588                   IStream*     iface,
589                   STATSTG*     pstatstg,     /* [out] */
590                   DWORD        grfStatFlag)  /* [in] */
591 {
592   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
593
594   memset(pstatstg, 0, sizeof(STATSTG));
595
596   pstatstg->pwcsName = NULL;
597   pstatstg->type     = STGTY_STREAM;
598   pstatstg->cbSize   = This->streamSize;
599
600   return S_OK;
601 }
602
603 static HRESULT WINAPI HGLOBALStreamImpl_Clone(
604                   IStream*     iface,
605                   IStream**    ppstm) /* [out] */
606 {
607   ULARGE_INTEGER dummy;
608   LARGE_INTEGER offset;
609   HRESULT hr;
610   HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
611   TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface,This->deleteOnRelease,(long)This->currentPosition.QuadPart);
612   hr=CreateStreamOnHGlobal(This->supportHandle, FALSE, ppstm);
613   if(FAILED(hr))
614     return hr;
615   offset.QuadPart=(LONGLONG)This->currentPosition.QuadPart;
616   HGLOBALStreamImpl_Seek(*ppstm,offset,STREAM_SEEK_SET,&dummy);
617   return S_OK;
618 }
619
620 /*
621  * Virtual function table for the HGLOBALStreamImpl class.
622  */
623 static const IStreamVtbl HGLOBALStreamImpl_Vtbl =
624 {
625     HGLOBALStreamImpl_QueryInterface,
626     HGLOBALStreamImpl_AddRef,
627     HGLOBALStreamImpl_Release,
628     HGLOBALStreamImpl_Read,
629     HGLOBALStreamImpl_Write,
630     HGLOBALStreamImpl_Seek,
631     HGLOBALStreamImpl_SetSize,
632     HGLOBALStreamImpl_CopyTo,
633     HGLOBALStreamImpl_Commit,
634     HGLOBALStreamImpl_Revert,
635     HGLOBALStreamImpl_LockRegion,
636     HGLOBALStreamImpl_UnlockRegion,
637     HGLOBALStreamImpl_Stat,
638     HGLOBALStreamImpl_Clone
639 };
640
641 /******************************************************************************
642 ** HGLOBALStreamImpl implementation
643 */
644
645 /***
646  * This is the constructor for the HGLOBALStreamImpl class.
647  *
648  * Params:
649  *    hGlobal          - Handle that will support the stream. can be NULL.
650  *    fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released
651  *                       when the IStream object is destroyed.
652  */
653 static HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
654                 HGLOBAL  hGlobal,
655                 BOOL     fDeleteOnRelease)
656 {
657   HGLOBALStreamImpl* newStream;
658
659   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
660
661   if (newStream!=0)
662   {
663     /*
664      * Set-up the virtual function table and reference count.
665      */
666     newStream->lpVtbl = &HGLOBALStreamImpl_Vtbl;
667     newStream->ref    = 0;
668
669     /*
670      * Initialize the support.
671      */
672     newStream->supportHandle = hGlobal;
673     newStream->deleteOnRelease = fDeleteOnRelease;
674
675     /*
676      * This method will allocate a handle if one is not supplied.
677      */
678     if (!newStream->supportHandle)
679     {
680       newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
681                                              GMEM_SHARE, 0);
682     }
683
684     /*
685      * Start the stream at the beginning.
686      */
687     newStream->currentPosition.u.HighPart = 0;
688     newStream->currentPosition.u.LowPart = 0;
689
690     /*
691      * Initialize the size of the stream to the size of the handle.
692      */
693     newStream->streamSize.u.HighPart = 0;
694     newStream->streamSize.u.LowPart  = GlobalSize(newStream->supportHandle);
695   }
696
697   return newStream;
698 }
699
700
701 /***********************************************************************
702  *           CreateStreamOnHGlobal     [OLE32.@]
703  */
704 HRESULT WINAPI CreateStreamOnHGlobal(
705                 HGLOBAL   hGlobal,
706                 BOOL      fDeleteOnRelease,
707                 LPSTREAM* ppstm)
708 {
709   HGLOBALStreamImpl* newStream;
710
711   newStream = HGLOBALStreamImpl_Construct(hGlobal,
712                                           fDeleteOnRelease);
713
714   if (newStream!=NULL)
715   {
716     return IUnknown_QueryInterface((IUnknown*)newStream,
717                                    &IID_IStream,
718                                    (void**)ppstm);
719   }
720
721   return E_OUTOFMEMORY;
722 }
723
724 /***********************************************************************
725  *           GetHGlobalFromStream     [OLE32.@]
726  */
727 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
728 {
729   HGLOBALStreamImpl* pStream;
730
731   if (pstm == NULL)
732     return E_INVALIDARG;
733
734   pStream = (HGLOBALStreamImpl*) pstm;
735
736   /*
737    * Verify that the stream object was created with CreateStreamOnHGlobal.
738    */
739   if (pStream->lpVtbl == &HGLOBALStreamImpl_Vtbl)
740     *phglobal = pStream->supportHandle;
741   else
742   {
743     *phglobal = 0;
744     return E_INVALIDARG;
745   }
746
747   return S_OK;
748 }