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