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