gphoto2.ds: Set supported groups.
[wine.git] / dlls / ole32 / hglobalstream.c
blob655e380ea959da6ff630f975b896612f634fa5ed
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 "config.h"
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
32 #define COBJMACROS
33 #define NONAMELESSUNION
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winuser.h"
38 #include "objbase.h"
39 #include "ole2.h"
40 #include "winerror.h"
41 #include "winternl.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(storage);
47 /****************************************************************************
48 * HGLOBALStreamImpl definition.
50 * This class implements the IStream interface and represents a stream
51 * supported by an HGLOBAL pointer.
53 typedef struct
55 IStream IStream_iface;
56 LONG ref;
58 /* support for the stream */
59 HGLOBAL supportHandle;
61 /* if TRUE the HGLOBAL is destroyed when the stream is finally released */
62 BOOL deleteOnRelease;
64 /* size of the stream */
65 ULARGE_INTEGER streamSize;
67 /* current position of the cursor */
68 ULARGE_INTEGER currentPosition;
69 } HGLOBALStreamImpl;
71 static inline HGLOBALStreamImpl *impl_from_IStream(IStream *iface)
73 return CONTAINING_RECORD(iface, HGLOBALStreamImpl, IStream_iface);
76 static HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
77 IStream* iface,
78 REFIID riid, /* [in] */
79 void** ppvObject) /* [iid_is][out] */
81 HGLOBALStreamImpl* This = impl_from_IStream(iface);
83 if (ppvObject==0)
84 return E_INVALIDARG;
86 *ppvObject = 0;
88 if (IsEqualIID(&IID_IUnknown, riid) ||
89 IsEqualIID(&IID_ISequentialStream, riid) ||
90 IsEqualIID(&IID_IStream, riid))
92 *ppvObject = &This->IStream_iface;
95 if ((*ppvObject)==0)
96 return E_NOINTERFACE;
98 IStream_AddRef(iface);
100 return S_OK;
103 static ULONG WINAPI HGLOBALStreamImpl_AddRef(IStream* iface)
105 HGLOBALStreamImpl* This = impl_from_IStream(iface);
106 return InterlockedIncrement(&This->ref);
109 static ULONG WINAPI HGLOBALStreamImpl_Release(
110 IStream* iface)
112 HGLOBALStreamImpl* This= impl_from_IStream(iface);
113 ULONG ref = InterlockedDecrement(&This->ref);
115 if (!ref)
117 if (This->deleteOnRelease)
119 GlobalFree(This->supportHandle);
120 This->supportHandle = NULL;
123 HeapFree(GetProcessHeap(), 0, This);
126 return ref;
129 /***
130 * This method is part of the ISequentialStream interface.
132 * If reads a block of information from the stream at the current
133 * position. It then moves the current position at the end of the
134 * read block
136 * See the documentation of ISequentialStream for more info.
138 static HRESULT WINAPI HGLOBALStreamImpl_Read(
139 IStream* iface,
140 void* pv, /* [length_is][size_is][out] */
141 ULONG cb, /* [in] */
142 ULONG* pcbRead) /* [out] */
144 HGLOBALStreamImpl* This = impl_from_IStream(iface);
146 void* supportBuffer;
147 ULONG bytesReadBuffer;
148 ULONG bytesToReadFromBuffer;
150 TRACE("(%p, %p, %d, %p)\n", iface,
151 pv, cb, pcbRead);
154 * If the caller is not interested in the number of bytes read,
155 * we use another buffer to avoid "if" statements in the code.
157 if (pcbRead==0)
158 pcbRead = &bytesReadBuffer;
161 * Using the known size of the stream, calculate the number of bytes
162 * to read from the block chain
164 bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
167 * Lock the buffer in position and copy the data.
169 supportBuffer = GlobalLock(This->supportHandle);
170 if (!supportBuffer)
172 WARN("read from invalid hglobal %p\n", This->supportHandle);
173 *pcbRead = 0;
174 return S_OK;
177 memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
180 * Move the current position to the new position
182 This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
185 * Return the number of bytes read.
187 *pcbRead = bytesToReadFromBuffer;
190 * Cleanup
192 GlobalUnlock(This->supportHandle);
195 * Always returns S_OK even if the end of the stream is reached before the
196 * buffer is filled
199 return S_OK;
202 /***
203 * This method is part of the ISequentialStream interface.
205 * It writes a block of information to the stream at the current
206 * position. It then moves the current position at the end of the
207 * written block. If the stream is too small to fit the block,
208 * the stream is grown to fit.
210 * See the documentation of ISequentialStream for more info.
212 static HRESULT WINAPI HGLOBALStreamImpl_Write(
213 IStream* iface,
214 const void* pv, /* [size_is][in] */
215 ULONG cb, /* [in] */
216 ULONG* pcbWritten) /* [out] */
218 HGLOBALStreamImpl* This = impl_from_IStream(iface);
220 void* supportBuffer;
221 ULARGE_INTEGER newSize;
222 ULONG bytesWritten = 0;
224 TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbWritten);
227 * If the caller is not interested in the number of bytes written,
228 * we use another buffer to avoid "if" statements in the code.
230 if (pcbWritten == 0)
231 pcbWritten = &bytesWritten;
233 if (cb == 0)
234 goto out;
236 *pcbWritten = 0;
238 newSize.u.HighPart = 0;
239 newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
242 * Verify if we need to grow the stream
244 if (newSize.u.LowPart > This->streamSize.u.LowPart)
246 /* grow stream */
247 HRESULT hr = IStream_SetSize(iface, newSize);
248 if (FAILED(hr))
250 ERR("IStream_SetSize failed with error 0x%08x\n", hr);
251 return hr;
256 * Lock the buffer in position and copy the data.
258 supportBuffer = GlobalLock(This->supportHandle);
259 if (!supportBuffer)
261 WARN("write to invalid hglobal %p\n", This->supportHandle);
262 return S_OK;
265 memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
268 * Move the current position to the new position
270 This->currentPosition.u.LowPart+=cb;
273 * Cleanup
275 GlobalUnlock(This->supportHandle);
277 out:
279 * Return the number of bytes read.
281 *pcbWritten = cb;
283 return S_OK;
286 /***
287 * This method is part of the IStream interface.
289 * It will move the current stream pointer according to the parameters
290 * given.
292 * See the documentation of IStream for more info.
294 static HRESULT WINAPI HGLOBALStreamImpl_Seek(
295 IStream* iface,
296 LARGE_INTEGER dlibMove, /* [in] */
297 DWORD dwOrigin, /* [in] */
298 ULARGE_INTEGER* plibNewPosition) /* [out] */
300 HGLOBALStreamImpl* This = impl_from_IStream(iface);
302 ULARGE_INTEGER newPosition = This->currentPosition;
303 HRESULT hr = S_OK;
305 TRACE("(%p, %x%08x, %d, %p)\n", iface, dlibMove.u.HighPart,
306 dlibMove.u.LowPart, dwOrigin, plibNewPosition);
309 * The file pointer is moved depending on the given "function"
310 * parameter.
312 switch (dwOrigin)
314 case STREAM_SEEK_SET:
315 newPosition.u.HighPart = 0;
316 newPosition.u.LowPart = 0;
317 break;
318 case STREAM_SEEK_CUR:
319 break;
320 case STREAM_SEEK_END:
321 newPosition = This->streamSize;
322 break;
323 default:
324 hr = STG_E_SEEKERROR;
325 goto end;
329 * Move the actual file pointer
330 * If the file pointer ends-up after the end of the stream, the next Write operation will
331 * make the file larger. This is how it is documented.
333 newPosition.u.HighPart = 0;
334 newPosition.u.LowPart += dlibMove.QuadPart;
336 if (dlibMove.u.LowPart >= 0x80000000 &&
337 newPosition.u.LowPart >= dlibMove.u.LowPart)
339 /* We tried to seek backwards and went past the start. */
340 hr = STG_E_SEEKERROR;
341 goto end;
344 This->currentPosition = newPosition;
346 end:
347 if (plibNewPosition) *plibNewPosition = This->currentPosition;
349 return hr;
352 /***
353 * This method is part of the IStream interface.
355 * It will change the size of a stream.
357 * TODO: Switch from small blocks to big blocks and vice versa.
359 * See the documentation of IStream for more info.
361 static HRESULT WINAPI HGLOBALStreamImpl_SetSize(
362 IStream* iface,
363 ULARGE_INTEGER libNewSize) /* [in] */
365 HGLOBALStreamImpl* This = impl_from_IStream(iface);
366 HGLOBAL supportHandle;
368 TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
371 * HighPart is ignored as shown in tests
374 if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
375 return S_OK;
378 * Re allocate the HGlobal to fit the new size of the stream.
380 supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
382 if (supportHandle == 0)
383 return E_OUTOFMEMORY;
385 This->supportHandle = supportHandle;
386 This->streamSize.u.LowPart = libNewSize.u.LowPart;
388 return S_OK;
391 /***
392 * This method is part of the IStream interface.
394 * It will copy the 'cb' Bytes to 'pstm' IStream.
396 * See the documentation of IStream for more info.
398 static HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
399 IStream* iface,
400 IStream* pstm, /* [unique][in] */
401 ULARGE_INTEGER cb, /* [in] */
402 ULARGE_INTEGER* pcbRead, /* [out] */
403 ULARGE_INTEGER* pcbWritten) /* [out] */
405 HRESULT hr = S_OK;
406 BYTE tmpBuffer[128];
407 ULONG bytesRead, bytesWritten, copySize;
408 ULARGE_INTEGER totalBytesRead;
409 ULARGE_INTEGER totalBytesWritten;
411 TRACE("(%p, %p, %d, %p, %p)\n", iface, pstm,
412 cb.u.LowPart, pcbRead, pcbWritten);
414 if ( pstm == 0 )
415 return STG_E_INVALIDPOINTER;
417 totalBytesRead.QuadPart = 0;
418 totalBytesWritten.QuadPart = 0;
420 while ( cb.QuadPart > 0 )
422 if ( cb.QuadPart >= sizeof(tmpBuffer) )
423 copySize = sizeof(tmpBuffer);
424 else
425 copySize = cb.u.LowPart;
427 hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
428 if (FAILED(hr))
429 break;
431 totalBytesRead.QuadPart += bytesRead;
433 if (bytesRead)
435 hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
436 if (FAILED(hr))
437 break;
439 totalBytesWritten.QuadPart += bytesWritten;
442 if (bytesRead!=copySize)
443 cb.QuadPart = 0;
444 else
445 cb.QuadPart -= bytesRead;
448 if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
449 if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
451 return hr;
454 /***
455 * This method is part of the IStream interface.
457 * For streams supported by HGLOBALS, this function does nothing.
458 * This is what the documentation tells us.
460 * See the documentation of IStream for more info.
462 static HRESULT WINAPI HGLOBALStreamImpl_Commit(
463 IStream* iface,
464 DWORD grfCommitFlags) /* [in] */
466 return S_OK;
469 /***
470 * This method is part of the IStream interface.
472 * For streams supported by HGLOBALS, this function does nothing.
473 * This is what the documentation tells us.
475 * See the documentation of IStream for more info.
477 static HRESULT WINAPI HGLOBALStreamImpl_Revert(
478 IStream* iface)
480 return S_OK;
483 /***
484 * This method is part of the IStream interface.
486 * For streams supported by HGLOBALS, this function does nothing.
487 * This is what the documentation tells us.
489 * See the documentation of IStream for more info.
491 static HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
492 IStream* iface,
493 ULARGE_INTEGER libOffset, /* [in] */
494 ULARGE_INTEGER cb, /* [in] */
495 DWORD dwLockType) /* [in] */
497 return STG_E_INVALIDFUNCTION;
501 * This method is part of the IStream interface.
503 * For streams supported by HGLOBALS, this function does nothing.
504 * This is what the documentation tells us.
506 * See the documentation of IStream for more info.
508 static HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
509 IStream* iface,
510 ULARGE_INTEGER libOffset, /* [in] */
511 ULARGE_INTEGER cb, /* [in] */
512 DWORD dwLockType) /* [in] */
514 return S_OK;
517 /***
518 * This method is part of the IStream interface.
520 * This method returns information about the current
521 * stream.
523 * See the documentation of IStream for more info.
525 static HRESULT WINAPI HGLOBALStreamImpl_Stat(
526 IStream* iface,
527 STATSTG* pstatstg, /* [out] */
528 DWORD grfStatFlag) /* [in] */
530 HGLOBALStreamImpl* This = impl_from_IStream(iface);
532 memset(pstatstg, 0, sizeof(STATSTG));
534 pstatstg->pwcsName = NULL;
535 pstatstg->type = STGTY_STREAM;
536 pstatstg->cbSize = This->streamSize;
538 return S_OK;
541 static HRESULT WINAPI HGLOBALStreamImpl_Clone(
542 IStream* iface,
543 IStream** ppstm) /* [out] */
545 HGLOBALStreamImpl* This = impl_from_IStream(iface);
546 ULARGE_INTEGER dummy;
547 LARGE_INTEGER offset;
548 HRESULT hr;
550 TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface,This->deleteOnRelease,(long)This->currentPosition.QuadPart);
551 hr = CreateStreamOnHGlobal(This->supportHandle, FALSE, ppstm);
552 if(FAILED(hr))
553 return hr;
554 offset.QuadPart = (LONGLONG)This->currentPosition.QuadPart;
555 IStream_Seek(*ppstm, offset, STREAM_SEEK_SET, &dummy);
556 return S_OK;
559 static const IStreamVtbl HGLOBALStreamImplVtbl =
561 HGLOBALStreamImpl_QueryInterface,
562 HGLOBALStreamImpl_AddRef,
563 HGLOBALStreamImpl_Release,
564 HGLOBALStreamImpl_Read,
565 HGLOBALStreamImpl_Write,
566 HGLOBALStreamImpl_Seek,
567 HGLOBALStreamImpl_SetSize,
568 HGLOBALStreamImpl_CopyTo,
569 HGLOBALStreamImpl_Commit,
570 HGLOBALStreamImpl_Revert,
571 HGLOBALStreamImpl_LockRegion,
572 HGLOBALStreamImpl_UnlockRegion,
573 HGLOBALStreamImpl_Stat,
574 HGLOBALStreamImpl_Clone
577 /***********************************************************************
578 * CreateStreamOnHGlobal [OLE32.@]
580 HRESULT WINAPI CreateStreamOnHGlobal(
581 HGLOBAL hGlobal,
582 BOOL fDeleteOnRelease,
583 LPSTREAM* ppstm)
585 HGLOBALStreamImpl* This;
587 if (!ppstm)
588 return E_INVALIDARG;
590 This = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
591 if (!This) return E_OUTOFMEMORY;
593 This->IStream_iface.lpVtbl = &HGLOBALStreamImplVtbl;
594 This->ref = 1;
596 /* initialize the support */
597 This->supportHandle = hGlobal;
598 This->deleteOnRelease = fDeleteOnRelease;
600 /* allocate a handle if one is not supplied */
601 if (!This->supportHandle)
602 This->supportHandle = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_SHARE, 0);
604 /* start at the beginning */
605 This->currentPosition.u.HighPart = 0;
606 This->currentPosition.u.LowPart = 0;
608 /* initialize the size of the stream to the size of the handle */
609 This->streamSize.u.HighPart = 0;
610 This->streamSize.u.LowPart = GlobalSize(This->supportHandle);
612 *ppstm = &This->IStream_iface;
614 return S_OK;
617 /***********************************************************************
618 * GetHGlobalFromStream [OLE32.@]
620 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
622 HGLOBALStreamImpl* pStream;
624 if (pstm == NULL)
625 return E_INVALIDARG;
627 pStream = (HGLOBALStreamImpl*) pstm;
630 * Verify that the stream object was created with CreateStreamOnHGlobal.
632 if (pStream->IStream_iface.lpVtbl == &HGLOBALStreamImplVtbl)
633 *phglobal = pStream->supportHandle;
634 else
636 *phglobal = 0;
637 return E_INVALIDARG;
640 return S_OK;