xref: /reactos/dll/win32/ole32/hglobalstream.c (revision 139a3d66)
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  * Copyright 2016 Dmitry Timoshkov
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 
31 #define COBJMACROS
32 #define NONAMELESSUNION
33 
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winuser.h"
37 #include "objbase.h"
38 #include "ole2.h"
39 #include "winerror.h"
40 #include "winternl.h"
41 
42 #include "wine/debug.h"
43 
44 WINE_DEFAULT_DEBUG_CHANNEL(hglobalstream);
45 
46 struct handle_wrapper
47 {
48     LONG ref;
49     HGLOBAL hglobal;
50     ULONG size;
51     BOOL delete_on_release;
52     CRITICAL_SECTION lock;
53 };
54 
55 static void handle_addref(struct handle_wrapper *handle)
56 {
57     InterlockedIncrement(&handle->ref);
58 }
59 
60 static void handle_release(struct handle_wrapper *handle)
61 {
62     ULONG ref = InterlockedDecrement(&handle->ref);
63 
64     if (!ref)
65     {
66         if (handle->delete_on_release)
67         {
68             GlobalFree(handle->hglobal);
69             handle->hglobal = NULL;
70         }
71 
72         handle->lock.DebugInfo->Spare[0] = 0;
73         DeleteCriticalSection(&handle->lock);
74         HeapFree(GetProcessHeap(), 0, handle);
75     }
76 }
77 
78 static ULONG handle_read(struct handle_wrapper *handle, ULONG *pos, void *dest, ULONG len)
79 {
80     void *source;
81 
82     EnterCriticalSection(&handle->lock);
83 
84     if (*pos < handle->size)
85         len = min(handle->size - *pos, len);
86     else
87         len = 0;
88 
89     source = GlobalLock(handle->hglobal);
90     if (source)
91     {
92         memcpy(dest, (char *)source + *pos, len);
93         *pos += len;
94         GlobalUnlock(handle->hglobal);
95     }
96     else
97     {
98         WARN("read from invalid hglobal %p\n", handle->hglobal);
99         len = 0;
100     }
101 
102     LeaveCriticalSection(&handle->lock);
103     return len;
104 }
105 
106 static ULONG handle_write(struct handle_wrapper *handle, ULONG *pos, const void *source, ULONG len)
107 {
108     void *dest;
109 
110     if (!len)
111         return 0;
112 
113     EnterCriticalSection(&handle->lock);
114 
115     if (*pos + len > handle->size)
116     {
117         HGLOBAL hglobal = GlobalReAlloc(handle->hglobal, *pos + len, GMEM_MOVEABLE);
118         if (hglobal)
119         {
120             handle->hglobal = hglobal;
121             handle->size = *pos + len;
122         }
123         else
124         {
125             len = 0;
126             goto done;
127         }
128     }
129 
130     dest = GlobalLock(handle->hglobal);
131     if (dest)
132     {
133         memcpy((char *)dest + *pos, source, len);
134         *pos += len;
135         GlobalUnlock(handle->hglobal);
136     }
137     else
138     {
139         WARN("write to invalid hglobal %p\n", handle->hglobal);
140         /* len = 0; */
141     }
142 
143 done:
144     LeaveCriticalSection(&handle->lock);
145     return len;
146 }
147 
148 static HGLOBAL handle_gethglobal(struct handle_wrapper *handle)
149 {
150     return handle->hglobal;
151 }
152 
153 static HRESULT handle_setsize(struct handle_wrapper *handle, ULONG size)
154 {
155     HRESULT hr = S_OK;
156 
157     EnterCriticalSection(&handle->lock);
158 
159     if (handle->size != size)
160     {
161         HGLOBAL hglobal = GlobalReAlloc(handle->hglobal, size, GMEM_MOVEABLE);
162         if (hglobal)
163         {
164             handle->hglobal = hglobal;
165             handle->size = size;
166         }
167         else
168             hr = E_OUTOFMEMORY;
169     }
170 
171     LeaveCriticalSection(&handle->lock);
172     return hr;
173 }
174 
175 static ULONG handle_getsize(struct handle_wrapper *handle)
176 {
177     return handle->size;
178 }
179 
180 static struct handle_wrapper *handle_create(HGLOBAL hglobal, BOOL delete_on_release)
181 {
182     struct handle_wrapper *handle;
183 
184     handle = HeapAlloc(GetProcessHeap(), 0, sizeof(*handle));
185     if (handle)
186     {
187         handle->ref = 1;
188         handle->hglobal = hglobal;
189         handle->size = GlobalSize(hglobal);
190         handle->delete_on_release = delete_on_release;
191         InitializeCriticalSection(&handle->lock);
192         handle->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": handle_wrapper.lock");
193     }
194     return handle;
195 }
196 
197 /****************************************************************************
198  * HGLOBALStreamImpl definition.
199  *
200  * This class implements the IStream interface and represents a stream
201  * supported by an HGLOBAL pointer.
202  */
203 typedef struct
204 {
205   IStream IStream_iface;
206   LONG ref;
207 
208   struct handle_wrapper *handle;
209 
210   /* current position of the cursor */
211   ULARGE_INTEGER currentPosition;
212 } HGLOBALStreamImpl;
213 
214 static inline HGLOBALStreamImpl *impl_from_IStream(IStream *iface)
215 {
216   return CONTAINING_RECORD(iface, HGLOBALStreamImpl, IStream_iface);
217 }
218 
219 static HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
220 		  IStream*     iface,
221 		  REFIID         riid,	      /* [in] */
222 		  void**         ppvObject)   /* [iid_is][out] */
223 {
224   HGLOBALStreamImpl* This = impl_from_IStream(iface);
225 
226   if (ppvObject==0)
227     return E_INVALIDARG;
228 
229   *ppvObject = 0;
230 
231   if (IsEqualIID(&IID_IUnknown, riid) ||
232       IsEqualIID(&IID_ISequentialStream, riid) ||
233       IsEqualIID(&IID_IStream, riid))
234   {
235     *ppvObject = &This->IStream_iface;
236   }
237 
238   if ((*ppvObject)==0)
239     return E_NOINTERFACE;
240 
241   IStream_AddRef(iface);
242 
243   return S_OK;
244 }
245 
246 static ULONG WINAPI HGLOBALStreamImpl_AddRef(IStream* iface)
247 {
248   HGLOBALStreamImpl* This = impl_from_IStream(iface);
249   return InterlockedIncrement(&This->ref);
250 }
251 
252 static ULONG WINAPI HGLOBALStreamImpl_Release(
253 		IStream* iface)
254 {
255   HGLOBALStreamImpl* This= impl_from_IStream(iface);
256   ULONG ref = InterlockedDecrement(&This->ref);
257 
258   if (!ref)
259   {
260     handle_release(This->handle);
261     HeapFree(GetProcessHeap(), 0, This);
262   }
263 
264   return ref;
265 }
266 
267 /***
268  * This method is part of the ISequentialStream interface.
269  *
270  * If reads a block of information from the stream at the current
271  * position. It then moves the current position at the end of the
272  * read block
273  *
274  * See the documentation of ISequentialStream for more info.
275  */
276 static HRESULT WINAPI HGLOBALStreamImpl_Read(
277 		  IStream*     iface,
278 		  void*          pv,        /* [length_is][size_is][out] */
279 		  ULONG          cb,        /* [in] */
280 		  ULONG*         pcbRead)   /* [out] */
281 {
282   HGLOBALStreamImpl* This = impl_from_IStream(iface);
283   ULONG num_bytes;
284 
285   TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbRead);
286 
287   num_bytes = handle_read(This->handle, &This->currentPosition.u.LowPart, pv, cb);
288   if (pcbRead) *pcbRead = num_bytes;
289 
290   return S_OK;
291 }
292 
293 /***
294  * This method is part of the ISequentialStream interface.
295  *
296  * It writes a block of information to the stream at the current
297  * position. It then moves the current position at the end of the
298  * written block. If the stream is too small to fit the block,
299  * the stream is grown to fit.
300  *
301  * See the documentation of ISequentialStream for more info.
302  */
303 static HRESULT WINAPI HGLOBALStreamImpl_Write(
304 	          IStream*     iface,
305 		  const void*    pv,          /* [size_is][in] */
306 		  ULONG          cb,          /* [in] */
307 		  ULONG*         pcbWritten)  /* [out] */
308 {
309   HGLOBALStreamImpl* This = impl_from_IStream(iface);
310   ULONG num_bytes;
311 
312   TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbWritten);
313 
314   num_bytes = handle_write(This->handle, &This->currentPosition.u.LowPart, pv, cb);
315   if (pcbWritten) *pcbWritten = num_bytes;
316 
317   return (num_bytes < cb) ? E_OUTOFMEMORY : S_OK;
318 }
319 
320 /***
321  * This method is part of the IStream interface.
322  *
323  * It will move the current stream pointer according to the parameters
324  * given.
325  *
326  * See the documentation of IStream for more info.
327  */
328 static HRESULT WINAPI HGLOBALStreamImpl_Seek(
329 		  IStream*      iface,
330 		  LARGE_INTEGER   dlibMove,         /* [in] */
331 		  DWORD           dwOrigin,         /* [in] */
332 		  ULARGE_INTEGER* plibNewPosition) /* [out] */
333 {
334   HGLOBALStreamImpl* This = impl_from_IStream(iface);
335 
336   ULARGE_INTEGER newPosition = This->currentPosition;
337   HRESULT hr = S_OK;
338 
339   TRACE("(%p, %x%08x, %d, %p)\n", iface, dlibMove.u.HighPart,
340 	dlibMove.u.LowPart, dwOrigin, plibNewPosition);
341 
342   /*
343    * The file pointer is moved depending on the given "function"
344    * parameter.
345    */
346   switch (dwOrigin)
347   {
348     case STREAM_SEEK_SET:
349       newPosition.u.HighPart = 0;
350       newPosition.u.LowPart = 0;
351       break;
352     case STREAM_SEEK_CUR:
353       break;
354     case STREAM_SEEK_END:
355       newPosition.QuadPart = handle_getsize(This->handle);
356       break;
357     default:
358       hr = STG_E_SEEKERROR;
359       goto end;
360   }
361 
362   /*
363    * Move the actual file pointer
364    * If the file pointer ends-up after the end of the stream, the next Write operation will
365    * make the file larger. This is how it is documented.
366    */
367   newPosition.u.HighPart = 0;
368   newPosition.u.LowPart += dlibMove.QuadPart;
369 
370   if (dlibMove.u.LowPart >= 0x80000000 &&
371       newPosition.u.LowPart >= dlibMove.u.LowPart)
372   {
373     /* We tried to seek backwards and went past the start. */
374     hr = STG_E_SEEKERROR;
375     goto end;
376   }
377 
378   This->currentPosition = newPosition;
379 
380 end:
381   if (plibNewPosition) *plibNewPosition = This->currentPosition;
382 
383   return hr;
384 }
385 
386 /***
387  * This method is part of the IStream interface.
388  *
389  * It will change the size of a stream.
390  *
391  * TODO: Switch from small blocks to big blocks and vice versa.
392  *
393  * See the documentation of IStream for more info.
394  */
395 static HRESULT WINAPI HGLOBALStreamImpl_SetSize(
396 				     IStream*      iface,
397 				     ULARGE_INTEGER  libNewSize)   /* [in] */
398 {
399   HGLOBALStreamImpl* This = impl_from_IStream(iface);
400 
401   TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
402 
403   /*
404    * HighPart is ignored as shown in tests
405    */
406   return handle_setsize(This->handle, libNewSize.u.LowPart);
407 }
408 
409 /***
410  * This method is part of the IStream interface.
411  *
412  * It will copy the 'cb' Bytes to 'pstm' IStream.
413  *
414  * See the documentation of IStream for more info.
415  */
416 static HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
417 				    IStream*      iface,
418 				    IStream*      pstm,         /* [unique][in] */
419 				    ULARGE_INTEGER  cb,           /* [in] */
420 				    ULARGE_INTEGER* pcbRead,      /* [out] */
421 				    ULARGE_INTEGER* pcbWritten)   /* [out] */
422 {
423   HRESULT        hr = S_OK;
424   BYTE           tmpBuffer[128];
425   ULONG          bytesRead, bytesWritten, copySize;
426   ULARGE_INTEGER totalBytesRead;
427   ULARGE_INTEGER totalBytesWritten;
428 
429   TRACE("(%p, %p, %d, %p, %p)\n", iface, pstm,
430 	cb.u.LowPart, pcbRead, pcbWritten);
431 
432   if ( pstm == 0 )
433     return STG_E_INVALIDPOINTER;
434 
435   totalBytesRead.QuadPart = 0;
436   totalBytesWritten.QuadPart = 0;
437 
438   while ( cb.QuadPart > 0 )
439   {
440     if ( cb.QuadPart >= sizeof(tmpBuffer) )
441       copySize = sizeof(tmpBuffer);
442     else
443       copySize = cb.u.LowPart;
444 
445     hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
446     if (FAILED(hr))
447         break;
448 
449     totalBytesRead.QuadPart += bytesRead;
450 
451     if (bytesRead)
452     {
453         hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
454         if (FAILED(hr))
455             break;
456 
457         totalBytesWritten.QuadPart += bytesWritten;
458     }
459 
460     if (bytesRead!=copySize)
461       cb.QuadPart = 0;
462     else
463       cb.QuadPart -= bytesRead;
464   }
465 
466   if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
467   if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
468 
469   return hr;
470 }
471 
472 /***
473  * This method is part of the IStream interface.
474  *
475  * For streams supported by HGLOBALS, this function does nothing.
476  * This is what the documentation tells us.
477  *
478  * See the documentation of IStream for more info.
479  */
480 static HRESULT WINAPI HGLOBALStreamImpl_Commit(
481 		  IStream*      iface,
482 		  DWORD         grfCommitFlags)  /* [in] */
483 {
484   return S_OK;
485 }
486 
487 /***
488  * This method is part of the IStream interface.
489  *
490  * For streams supported by HGLOBALS, this function does nothing.
491  * This is what the documentation tells us.
492  *
493  * See the documentation of IStream for more info.
494  */
495 static HRESULT WINAPI HGLOBALStreamImpl_Revert(
496 		  IStream* iface)
497 {
498   return S_OK;
499 }
500 
501 /***
502  * This method is part of the IStream interface.
503  *
504  * For streams supported by HGLOBALS, this function does nothing.
505  * This is what the documentation tells us.
506  *
507  * See the documentation of IStream for more info.
508  */
509 static HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
510 		  IStream*       iface,
511 		  ULARGE_INTEGER libOffset,   /* [in] */
512 		  ULARGE_INTEGER cb,          /* [in] */
513 		  DWORD          dwLockType)  /* [in] */
514 {
515   return STG_E_INVALIDFUNCTION;
516 }
517 
518 /*
519  * This method is part of the IStream interface.
520  *
521  * For streams supported by HGLOBALS, this function does nothing.
522  * This is what the documentation tells us.
523  *
524  * See the documentation of IStream for more info.
525  */
526 static HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
527 		  IStream*       iface,
528 		  ULARGE_INTEGER libOffset,   /* [in] */
529 		  ULARGE_INTEGER cb,          /* [in] */
530 		  DWORD          dwLockType)  /* [in] */
531 {
532   return S_OK;
533 }
534 
535 /***
536  * This method is part of the IStream interface.
537  *
538  * This method returns information about the current
539  * stream.
540  *
541  * See the documentation of IStream for more info.
542  */
543 static HRESULT WINAPI HGLOBALStreamImpl_Stat(
544 		  IStream*     iface,
545 		  STATSTG*     pstatstg,     /* [out] */
546 		  DWORD        grfStatFlag)  /* [in] */
547 {
548   HGLOBALStreamImpl* This = impl_from_IStream(iface);
549 
550   memset(pstatstg, 0, sizeof(STATSTG));
551 
552   pstatstg->pwcsName = NULL;
553   pstatstg->type     = STGTY_STREAM;
554   pstatstg->cbSize.QuadPart = handle_getsize(This->handle);
555 
556   return S_OK;
557 }
558 
559 static const IStreamVtbl HGLOBALStreamImplVtbl;
560 
561 static HGLOBALStreamImpl *HGLOBALStreamImpl_Create(void)
562 {
563     HGLOBALStreamImpl *This;
564 
565     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
566     if (This)
567     {
568         This->IStream_iface.lpVtbl = &HGLOBALStreamImplVtbl;
569         This->ref = 1;
570     }
571     return This;
572 }
573 
574 static HRESULT WINAPI HGLOBALStreamImpl_Clone(
575 		  IStream*     iface,
576 		  IStream**    ppstm) /* [out] */
577 {
578   HGLOBALStreamImpl* This = impl_from_IStream(iface);
579   HGLOBALStreamImpl* clone;
580   ULARGE_INTEGER dummy;
581   LARGE_INTEGER offset;
582 
583   if (!ppstm) return E_INVALIDARG;
584 
585   *ppstm = NULL;
586 
587   TRACE(" Cloning %p (seek position=%d)\n", iface, This->currentPosition.u.LowPart);
588 
589   clone = HGLOBALStreamImpl_Create();
590   if (!clone) return E_OUTOFMEMORY;
591 
592   *ppstm = &clone->IStream_iface;
593 
594   handle_addref(This->handle);
595   clone->handle = This->handle;
596 
597   offset.QuadPart = (LONGLONG)This->currentPosition.QuadPart;
598   IStream_Seek(*ppstm, offset, STREAM_SEEK_SET, &dummy);
599   return S_OK;
600 }
601 
602 static const IStreamVtbl HGLOBALStreamImplVtbl =
603 {
604     HGLOBALStreamImpl_QueryInterface,
605     HGLOBALStreamImpl_AddRef,
606     HGLOBALStreamImpl_Release,
607     HGLOBALStreamImpl_Read,
608     HGLOBALStreamImpl_Write,
609     HGLOBALStreamImpl_Seek,
610     HGLOBALStreamImpl_SetSize,
611     HGLOBALStreamImpl_CopyTo,
612     HGLOBALStreamImpl_Commit,
613     HGLOBALStreamImpl_Revert,
614     HGLOBALStreamImpl_LockRegion,
615     HGLOBALStreamImpl_UnlockRegion,
616     HGLOBALStreamImpl_Stat,
617     HGLOBALStreamImpl_Clone
618 };
619 
620 /***********************************************************************
621  *           CreateStreamOnHGlobal     [OLE32.@]
622  */
623 HRESULT WINAPI CreateStreamOnHGlobal(
624 		HGLOBAL   hGlobal,
625 		BOOL      fDeleteOnRelease,
626 		LPSTREAM* ppstm)
627 {
628   HGLOBALStreamImpl* This;
629 
630   if (!ppstm)
631     return E_INVALIDARG;
632 
633   This = HGLOBALStreamImpl_Create();
634   if (!This) return E_OUTOFMEMORY;
635 
636   /* allocate a handle if one is not supplied */
637   if (!hGlobal)
638     hGlobal = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_SHARE, 0);
639 
640   This->handle = handle_create(hGlobal, fDeleteOnRelease);
641 
642   /* start at the beginning */
643   This->currentPosition.u.HighPart = 0;
644   This->currentPosition.u.LowPart = 0;
645 
646   *ppstm = &This->IStream_iface;
647 
648   return S_OK;
649 }
650 
651 /***********************************************************************
652  *           GetHGlobalFromStream     [OLE32.@]
653  */
654 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
655 {
656   HGLOBALStreamImpl* pStream;
657 
658   if (!pstm || !phglobal)
659     return E_INVALIDARG;
660 
661   pStream = impl_from_IStream(pstm);
662 
663   /*
664    * Verify that the stream object was created with CreateStreamOnHGlobal.
665    */
666   if (pStream->IStream_iface.lpVtbl == &HGLOBALStreamImplVtbl)
667     *phglobal = handle_gethglobal(pStream->handle);
668   else
669   {
670     *phglobal = 0;
671     return E_INVALIDARG;
672   }
673 
674   return S_OK;
675 }
676