[NTOSKRNL] Drop the useless Timestamp field
[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 "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 }