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