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
handle_addref(struct handle_wrapper * handle)55 static void handle_addref(struct handle_wrapper *handle)
56 {
57 InterlockedIncrement(&handle->ref);
58 }
59
handle_release(struct handle_wrapper * handle)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
handle_read(struct handle_wrapper * handle,ULONG * pos,void * dest,ULONG len)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
handle_write(struct handle_wrapper * handle,ULONG * pos,const void * source,ULONG len)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
handle_gethglobal(struct handle_wrapper * handle)148 static HGLOBAL handle_gethglobal(struct handle_wrapper *handle)
149 {
150 return handle->hglobal;
151 }
152
handle_setsize(struct handle_wrapper * handle,ULONG size)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
handle_getsize(struct handle_wrapper * handle)175 static ULONG handle_getsize(struct handle_wrapper *handle)
176 {
177 return handle->size;
178 }
179
handle_create(HGLOBAL hglobal,BOOL delete_on_release)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
impl_from_IStream(IStream * iface)214 static inline HGLOBALStreamImpl *impl_from_IStream(IStream *iface)
215 {
216 return CONTAINING_RECORD(iface, HGLOBALStreamImpl, IStream_iface);
217 }
218
HGLOBALStreamImpl_QueryInterface(IStream * iface,REFIID riid,void ** ppvObject)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
HGLOBALStreamImpl_AddRef(IStream * iface)246 static ULONG WINAPI HGLOBALStreamImpl_AddRef(IStream* iface)
247 {
248 HGLOBALStreamImpl* This = impl_from_IStream(iface);
249 return InterlockedIncrement(&This->ref);
250 }
251
HGLOBALStreamImpl_Release(IStream * iface)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 */
HGLOBALStreamImpl_Read(IStream * iface,void * pv,ULONG cb,ULONG * pcbRead)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 */
HGLOBALStreamImpl_Write(IStream * iface,const void * pv,ULONG cb,ULONG * pcbWritten)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 */
HGLOBALStreamImpl_Seek(IStream * iface,LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER * plibNewPosition)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 */
HGLOBALStreamImpl_SetSize(IStream * iface,ULARGE_INTEGER libNewSize)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 */
HGLOBALStreamImpl_CopyTo(IStream * iface,IStream * pstm,ULARGE_INTEGER cb,ULARGE_INTEGER * pcbRead,ULARGE_INTEGER * pcbWritten)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 */
HGLOBALStreamImpl_Commit(IStream * iface,DWORD grfCommitFlags)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 */
HGLOBALStreamImpl_Revert(IStream * iface)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 */
HGLOBALStreamImpl_LockRegion(IStream * iface,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType)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 */
HGLOBALStreamImpl_UnlockRegion(IStream * iface,ULARGE_INTEGER libOffset,ULARGE_INTEGER cb,DWORD dwLockType)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 */
HGLOBALStreamImpl_Stat(IStream * iface,STATSTG * pstatstg,DWORD grfStatFlag)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
HGLOBALStreamImpl_Create(void)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
HGLOBALStreamImpl_Clone(IStream * iface,IStream ** ppstm)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 */
CreateStreamOnHGlobal(HGLOBAL hGlobal,BOOL fDeleteOnRelease,LPSTREAM * ppstm)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 */
GetHGlobalFromStream(IStream * pstm,HGLOBAL * phglobal)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