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