winegcc: Add entry symbol underscore when building linker command.
[wine.git] / dlls / ole32 / hglobalstream.c
blob6133690d9d1f00539de54512cc81750a910a049f
1 /*
2 * HGLOBAL Stream implementation
4 * This file contains the implementation of the stream interface
5 * for streams contained supported by an HGLOBAL pointer.
7 * Copyright 1999 Francis Beaudet
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.
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.
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
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <string.h>
30 #define COBJMACROS
31 #define NONAMELESSUNION
33 #include "windef.h"
34 #include "winbase.h"
35 #include "winuser.h"
36 #include "objbase.h"
37 #include "ole2.h"
38 #include "winerror.h"
39 #include "winternl.h"
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(storage);
45 /****************************************************************************
46 * HGLOBALStreamImpl definition.
48 * This class implements the IStream interface and represents a stream
49 * supported by an HGLOBAL pointer.
51 typedef struct
53 IStream IStream_iface;
54 LONG ref;
56 /* support for the stream */
57 HGLOBAL supportHandle;
59 /* if TRUE the HGLOBAL is destroyed when the stream is finally released */
60 BOOL deleteOnRelease;
62 /* size of the stream */
63 ULARGE_INTEGER streamSize;
65 /* current position of the cursor */
66 ULARGE_INTEGER currentPosition;
67 } HGLOBALStreamImpl;
69 static inline HGLOBALStreamImpl *impl_from_IStream(IStream *iface)
71 return CONTAINING_RECORD(iface, HGLOBALStreamImpl, IStream_iface);
74 static HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
75 IStream* iface,
76 REFIID riid, /* [in] */
77 void** ppvObject) /* [iid_is][out] */
79 HGLOBALStreamImpl* This = impl_from_IStream(iface);
81 if (ppvObject==0)
82 return E_INVALIDARG;
84 *ppvObject = 0;
86 if (IsEqualIID(&IID_IUnknown, riid) ||
87 IsEqualIID(&IID_ISequentialStream, riid) ||
88 IsEqualIID(&IID_IStream, riid))
90 *ppvObject = &This->IStream_iface;
93 if ((*ppvObject)==0)
94 return E_NOINTERFACE;
96 IStream_AddRef(iface);
98 return S_OK;
101 static ULONG WINAPI HGLOBALStreamImpl_AddRef(IStream* iface)
103 HGLOBALStreamImpl* This = impl_from_IStream(iface);
104 return InterlockedIncrement(&This->ref);
107 static ULONG WINAPI HGLOBALStreamImpl_Release(
108 IStream* iface)
110 HGLOBALStreamImpl* This= impl_from_IStream(iface);
111 ULONG ref = InterlockedDecrement(&This->ref);
113 if (!ref)
115 if (This->deleteOnRelease)
117 GlobalFree(This->supportHandle);
118 This->supportHandle = NULL;
121 HeapFree(GetProcessHeap(), 0, This);
124 return ref;
127 /***
128 * This method is part of the ISequentialStream interface.
130 * If reads a block of information from the stream at the current
131 * position. It then moves the current position at the end of the
132 * read block
134 * See the documentation of ISequentialStream for more info.
136 static HRESULT WINAPI HGLOBALStreamImpl_Read(
137 IStream* iface,
138 void* pv, /* [length_is][size_is][out] */
139 ULONG cb, /* [in] */
140 ULONG* pcbRead) /* [out] */
142 HGLOBALStreamImpl* This = impl_from_IStream(iface);
144 void* supportBuffer;
145 ULONG bytesReadBuffer;
146 ULONG bytesToReadFromBuffer;
148 TRACE("(%p, %p, %d, %p)\n", iface,
149 pv, cb, pcbRead);
152 * If the caller is not interested in the number of bytes read,
153 * we use another buffer to avoid "if" statements in the code.
155 if (pcbRead==0)
156 pcbRead = &bytesReadBuffer;
159 * Using the known size of the stream, calculate the number of bytes
160 * to read from the block chain
162 bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
165 * Lock the buffer in position and copy the data.
167 supportBuffer = GlobalLock(This->supportHandle);
168 if (!supportBuffer)
170 WARN("read from invalid hglobal %p\n", This->supportHandle);
171 *pcbRead = 0;
172 return S_OK;
175 memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
178 * Move the current position to the new position
180 This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
183 * Return the number of bytes read.
185 *pcbRead = bytesToReadFromBuffer;
188 * Cleanup
190 GlobalUnlock(This->supportHandle);
193 * Always returns S_OK even if the end of the stream is reached before the
194 * buffer is filled
197 return S_OK;
200 /***
201 * This method is part of the ISequentialStream interface.
203 * It writes a block of information to the stream at the current
204 * position. It then moves the current position at the end of the
205 * written block. If the stream is too small to fit the block,
206 * the stream is grown to fit.
208 * See the documentation of ISequentialStream for more info.
210 static HRESULT WINAPI HGLOBALStreamImpl_Write(
211 IStream* iface,
212 const void* pv, /* [size_is][in] */
213 ULONG cb, /* [in] */
214 ULONG* pcbWritten) /* [out] */
216 HGLOBALStreamImpl* This = impl_from_IStream(iface);
218 void* supportBuffer;
219 ULARGE_INTEGER newSize;
220 ULONG bytesWritten = 0;
222 TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbWritten);
225 * If the caller is not interested in the number of bytes written,
226 * we use another buffer to avoid "if" statements in the code.
228 if (pcbWritten == 0)
229 pcbWritten = &bytesWritten;
231 if (cb == 0)
232 goto out;
234 *pcbWritten = 0;
236 newSize.u.HighPart = 0;
237 newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
240 * Verify if we need to grow the stream
242 if (newSize.u.LowPart > This->streamSize.u.LowPart)
244 /* grow stream */
245 HRESULT hr = IStream_SetSize(iface, newSize);
246 if (FAILED(hr))
248 ERR("IStream_SetSize failed with error 0x%08x\n", hr);
249 return hr;
254 * Lock the buffer in position and copy the data.
256 supportBuffer = GlobalLock(This->supportHandle);
257 if (!supportBuffer)
259 WARN("write to invalid hglobal %p\n", This->supportHandle);
260 return S_OK;
263 memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
266 * Move the current position to the new position
268 This->currentPosition.u.LowPart+=cb;
271 * Cleanup
273 GlobalUnlock(This->supportHandle);
275 out:
277 * Return the number of bytes read.
279 *pcbWritten = cb;
281 return S_OK;
284 /***
285 * This method is part of the IStream interface.
287 * It will move the current stream pointer according to the parameters
288 * given.
290 * See the documentation of IStream for more info.
292 static HRESULT WINAPI HGLOBALStreamImpl_Seek(
293 IStream* iface,
294 LARGE_INTEGER dlibMove, /* [in] */
295 DWORD dwOrigin, /* [in] */
296 ULARGE_INTEGER* plibNewPosition) /* [out] */
298 HGLOBALStreamImpl* This = impl_from_IStream(iface);
300 ULARGE_INTEGER newPosition = This->currentPosition;
301 HRESULT hr = S_OK;
303 TRACE("(%p, %x%08x, %d, %p)\n", iface, dlibMove.u.HighPart,
304 dlibMove.u.LowPart, dwOrigin, plibNewPosition);
307 * The file pointer is moved depending on the given "function"
308 * parameter.
310 switch (dwOrigin)
312 case STREAM_SEEK_SET:
313 newPosition.u.HighPart = 0;
314 newPosition.u.LowPart = 0;
315 break;
316 case STREAM_SEEK_CUR:
317 break;
318 case STREAM_SEEK_END:
319 newPosition = This->streamSize;
320 break;
321 default:
322 hr = STG_E_SEEKERROR;
323 goto end;
327 * Move the actual file pointer
328 * If the file pointer ends-up after the end of the stream, the next Write operation will
329 * make the file larger. This is how it is documented.
331 newPosition.u.HighPart = 0;
332 newPosition.u.LowPart += dlibMove.QuadPart;
334 if (dlibMove.u.LowPart >= 0x80000000 &&
335 newPosition.u.LowPart >= dlibMove.u.LowPart)
337 /* We tried to seek backwards and went past the start. */
338 hr = STG_E_SEEKERROR;
339 goto end;
342 This->currentPosition = newPosition;
344 end:
345 if (plibNewPosition) *plibNewPosition = This->currentPosition;
347 return hr;
350 /***
351 * This method is part of the IStream interface.
353 * It will change the size of a stream.
355 * TODO: Switch from small blocks to big blocks and vice versa.
357 * See the documentation of IStream for more info.
359 static HRESULT WINAPI HGLOBALStreamImpl_SetSize(
360 IStream* iface,
361 ULARGE_INTEGER libNewSize) /* [in] */
363 HGLOBALStreamImpl* This = impl_from_IStream(iface);
364 HGLOBAL supportHandle;
366 TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
369 * HighPart is ignored as shown in tests
372 if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
373 return S_OK;
376 * Re allocate the HGlobal to fit the new size of the stream.
378 supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
380 if (supportHandle == 0)
381 return E_OUTOFMEMORY;
383 This->supportHandle = supportHandle;
384 This->streamSize.u.LowPart = libNewSize.u.LowPart;
386 return S_OK;
389 /***
390 * This method is part of the IStream interface.
392 * It will copy the 'cb' Bytes to 'pstm' IStream.
394 * See the documentation of IStream for more info.
396 static HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
397 IStream* iface,
398 IStream* pstm, /* [unique][in] */
399 ULARGE_INTEGER cb, /* [in] */
400 ULARGE_INTEGER* pcbRead, /* [out] */
401 ULARGE_INTEGER* pcbWritten) /* [out] */
403 HRESULT hr = S_OK;
404 BYTE tmpBuffer[128];
405 ULONG bytesRead, bytesWritten, copySize;
406 ULARGE_INTEGER totalBytesRead;
407 ULARGE_INTEGER totalBytesWritten;
409 TRACE("(%p, %p, %d, %p, %p)\n", iface, pstm,
410 cb.u.LowPart, pcbRead, pcbWritten);
412 if ( pstm == 0 )
413 return STG_E_INVALIDPOINTER;
415 totalBytesRead.QuadPart = 0;
416 totalBytesWritten.QuadPart = 0;
418 while ( cb.QuadPart > 0 )
420 if ( cb.QuadPart >= sizeof(tmpBuffer) )
421 copySize = sizeof(tmpBuffer);
422 else
423 copySize = cb.u.LowPart;
425 hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
426 if (FAILED(hr))
427 break;
429 totalBytesRead.QuadPart += bytesRead;
431 if (bytesRead)
433 hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
434 if (FAILED(hr))
435 break;
437 totalBytesWritten.QuadPart += bytesWritten;
440 if (bytesRead!=copySize)
441 cb.QuadPart = 0;
442 else
443 cb.QuadPart -= bytesRead;
446 if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
447 if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
449 return hr;
452 /***
453 * This method is part of the IStream interface.
455 * For streams supported by HGLOBALS, this function does nothing.
456 * This is what the documentation tells us.
458 * See the documentation of IStream for more info.
460 static HRESULT WINAPI HGLOBALStreamImpl_Commit(
461 IStream* iface,
462 DWORD grfCommitFlags) /* [in] */
464 return S_OK;
467 /***
468 * This method is part of the IStream interface.
470 * For streams supported by HGLOBALS, this function does nothing.
471 * This is what the documentation tells us.
473 * See the documentation of IStream for more info.
475 static HRESULT WINAPI HGLOBALStreamImpl_Revert(
476 IStream* iface)
478 return S_OK;
481 /***
482 * This method is part of the IStream interface.
484 * For streams supported by HGLOBALS, this function does nothing.
485 * This is what the documentation tells us.
487 * See the documentation of IStream for more info.
489 static HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
490 IStream* iface,
491 ULARGE_INTEGER libOffset, /* [in] */
492 ULARGE_INTEGER cb, /* [in] */
493 DWORD dwLockType) /* [in] */
495 return STG_E_INVALIDFUNCTION;
499 * This method is part of the IStream interface.
501 * For streams supported by HGLOBALS, this function does nothing.
502 * This is what the documentation tells us.
504 * See the documentation of IStream for more info.
506 static HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
507 IStream* iface,
508 ULARGE_INTEGER libOffset, /* [in] */
509 ULARGE_INTEGER cb, /* [in] */
510 DWORD dwLockType) /* [in] */
512 return S_OK;
515 /***
516 * This method is part of the IStream interface.
518 * This method returns information about the current
519 * stream.
521 * See the documentation of IStream for more info.
523 static HRESULT WINAPI HGLOBALStreamImpl_Stat(
524 IStream* iface,
525 STATSTG* pstatstg, /* [out] */
526 DWORD grfStatFlag) /* [in] */
528 HGLOBALStreamImpl* This = impl_from_IStream(iface);
530 memset(pstatstg, 0, sizeof(STATSTG));
532 pstatstg->pwcsName = NULL;
533 pstatstg->type = STGTY_STREAM;
534 pstatstg->cbSize = This->streamSize;
536 return S_OK;
539 static HRESULT WINAPI HGLOBALStreamImpl_Clone(
540 IStream* iface,
541 IStream** ppstm) /* [out] */
543 HGLOBALStreamImpl* This = impl_from_IStream(iface);
544 ULARGE_INTEGER dummy;
545 LARGE_INTEGER offset;
546 HRESULT hr;
548 TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface,This->deleteOnRelease,(long)This->currentPosition.QuadPart);
549 hr = CreateStreamOnHGlobal(This->supportHandle, FALSE, ppstm);
550 if(FAILED(hr))
551 return hr;
552 offset.QuadPart = (LONGLONG)This->currentPosition.QuadPart;
553 IStream_Seek(*ppstm, offset, STREAM_SEEK_SET, &dummy);
554 return S_OK;
557 static const IStreamVtbl HGLOBALStreamImplVtbl =
559 HGLOBALStreamImpl_QueryInterface,
560 HGLOBALStreamImpl_AddRef,
561 HGLOBALStreamImpl_Release,
562 HGLOBALStreamImpl_Read,
563 HGLOBALStreamImpl_Write,
564 HGLOBALStreamImpl_Seek,
565 HGLOBALStreamImpl_SetSize,
566 HGLOBALStreamImpl_CopyTo,
567 HGLOBALStreamImpl_Commit,
568 HGLOBALStreamImpl_Revert,
569 HGLOBALStreamImpl_LockRegion,
570 HGLOBALStreamImpl_UnlockRegion,
571 HGLOBALStreamImpl_Stat,
572 HGLOBALStreamImpl_Clone
575 /***********************************************************************
576 * CreateStreamOnHGlobal [OLE32.@]
578 HRESULT WINAPI CreateStreamOnHGlobal(
579 HGLOBAL hGlobal,
580 BOOL fDeleteOnRelease,
581 LPSTREAM* ppstm)
583 HGLOBALStreamImpl* This;
585 if (!ppstm)
586 return E_INVALIDARG;
588 This = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
589 if (!This) return E_OUTOFMEMORY;
591 This->IStream_iface.lpVtbl = &HGLOBALStreamImplVtbl;
592 This->ref = 1;
594 /* initialize the support */
595 This->supportHandle = hGlobal;
596 This->deleteOnRelease = fDeleteOnRelease;
598 /* allocate a handle if one is not supplied */
599 if (!This->supportHandle)
600 This->supportHandle = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_SHARE, 0);
602 /* start at the beginning */
603 This->currentPosition.u.HighPart = 0;
604 This->currentPosition.u.LowPart = 0;
606 /* initialize the size of the stream to the size of the handle */
607 This->streamSize.u.HighPart = 0;
608 This->streamSize.u.LowPart = GlobalSize(This->supportHandle);
610 *ppstm = &This->IStream_iface;
612 return S_OK;
615 /***********************************************************************
616 * GetHGlobalFromStream [OLE32.@]
618 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
620 HGLOBALStreamImpl* pStream;
622 if (pstm == NULL)
623 return E_INVALIDARG;
625 pStream = (HGLOBALStreamImpl*) pstm;
628 * Verify that the stream object was created with CreateStreamOnHGlobal.
630 if (pStream->IStream_iface.lpVtbl == &HGLOBALStreamImplVtbl)
631 *phglobal = pStream->supportHandle;
632 else
634 *phglobal = 0;
635 return E_INVALIDARG;
638 return S_OK;