6a82b716f19a0ae34a2080083ff04ec322f2c445
[reactos.git] / dll / win32 / 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 * 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 }