-/*\r
- * HGLOBAL Stream implementation\r
- *\r
- * This file contains the implementation of the stream interface\r
- * for streams contained supported by an HGLOBAL pointer.\r
- *\r
- * Copyright 1999 Francis Beaudet\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- */\r
-\r
-#include "config.h"\r
-\r
-#include <assert.h>\r
-#include <stdlib.h>\r
-#include <stdarg.h>\r
-#include <stdio.h>\r
-#include <string.h>\r
-\r
-#define COBJMACROS\r
-#define NONAMELESSUNION\r
-#define NONAMELESSSTRUCT\r
-\r
-#include "windef.h"\r
-#include "winbase.h"\r
-#include "winuser.h"\r
-#include "objbase.h"\r
-#include "ole2.h"\r
-#include "winerror.h"\r
-#include "winreg.h"\r
-#include "winternl.h"\r
-\r
-#include "wine/debug.h"\r
-\r
-WINE_DEFAULT_DEBUG_CHANNEL(storage);\r
-\r
-/****************************************************************************\r
- * HGLOBALStreamImpl definition.\r
- *\r
- * This class imlements the IStream inteface and represents a stream\r
- * supported by an HGLOBAL pointer.\r
- */\r
-struct HGLOBALStreamImpl\r
-{\r
- IStreamVtbl *lpVtbl; /* Needs to be the first item in the struct\r
- * since we want to cast this in an IStream pointer */\r
-\r
- /*\r
- * Reference count\r
- */\r
- ULONG ref;\r
-\r
- /*\r
- * Support for the stream\r
- */\r
- HGLOBAL supportHandle;\r
-\r
- /*\r
- * This flag is TRUE if the HGLOBAL is destroyed when the stream\r
- * is finally released.\r
- */\r
- BOOL deleteOnRelease;\r
-\r
- /*\r
- * Helper variable that contains the size of the stream\r
- */\r
- ULARGE_INTEGER streamSize;\r
-\r
- /*\r
- * This is the current position of the cursor in the stream\r
- */\r
- ULARGE_INTEGER currentPosition;\r
-};\r
-\r
-typedef struct HGLOBALStreamImpl HGLOBALStreamImpl;\r
-\r
-/***\r
- * This is the destructor of the HGLOBALStreamImpl class.\r
- *\r
- * This method will clean-up all the resources used-up by the given HGLOBALStreamImpl\r
- * class. The pointer passed-in to this function will be freed and will not\r
- * be valid anymore.\r
- */\r
-static void HGLOBALStreamImpl_Destroy(HGLOBALStreamImpl* This)\r
-{\r
- TRACE("(%p)\n", This);\r
-\r
- /*\r
- * Release the HGlobal if the constructor asked for that.\r
- */\r
- if (This->deleteOnRelease)\r
- {\r
- GlobalFree(This->supportHandle);\r
- This->supportHandle=0;\r
- }\r
-\r
- /*\r
- * Finally, free the memory used-up by the class.\r
- */\r
- HeapFree(GetProcessHeap(), 0, This);\r
-}\r
-\r
-/***\r
- * This implements the IUnknown method AddRef for this\r
- * class\r
- */\r
-static ULONG WINAPI HGLOBALStreamImpl_AddRef(\r
- IStream* iface)\r
-{\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
- return InterlockedIncrement(&This->ref);\r
-}\r
-\r
-/***\r
- * This implements the IUnknown method QueryInterface for this\r
- * class\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(\r
- IStream* iface,\r
- REFIID riid, /* [in] */\r
- void** ppvObject) /* [iid_is][out] */\r
-{\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
-\r
- /*\r
- * Perform a sanity check on the parameters.\r
- */\r
- if (ppvObject==0)\r
- return E_INVALIDARG;\r
-\r
- /*\r
- * Initialize the return parameter.\r
- */\r
- *ppvObject = 0;\r
-\r
- /*\r
- * Compare the riid with the interface IDs implemented by this object.\r
- */\r
- if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)\r
- {\r
- *ppvObject = (IStream*)This;\r
- }\r
- else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)\r
- {\r
- *ppvObject = (IStream*)This;\r
- }\r
-\r
- /*\r
- * Check that we obtained an interface.\r
- */\r
- if ((*ppvObject)==0)\r
- return E_NOINTERFACE;\r
-\r
- /*\r
- * Query Interface always increases the reference count by one when it is\r
- * successful\r
- */\r
- HGLOBALStreamImpl_AddRef(iface);\r
-\r
- return S_OK;\r
-}\r
-\r
-/***\r
- * This implements the IUnknown method Release for this\r
- * class\r
- */\r
-static ULONG WINAPI HGLOBALStreamImpl_Release(\r
- IStream* iface)\r
-{\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
- ULONG newRef;\r
-\r
- newRef = InterlockedDecrement(&This->ref);\r
-\r
- /*\r
- * If the reference count goes down to 0, perform suicide.\r
- */\r
- if (newRef==0)\r
- {\r
- HGLOBALStreamImpl_Destroy(This);\r
- }\r
-\r
- return newRef;\r
-}\r
-\r
-/***\r
- * This method is part of the ISequentialStream interface.\r
- *\r
- * If reads a block of information from the stream at the current\r
- * position. It then moves the current position at the end of the\r
- * read block\r
- *\r
- * See the documentation of ISequentialStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_Read(\r
- IStream* iface,\r
- void* pv, /* [length_is][size_is][out] */\r
- ULONG cb, /* [in] */\r
- ULONG* pcbRead) /* [out] */\r
-{\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
-\r
- void* supportBuffer;\r
- ULONG bytesReadBuffer;\r
- ULONG bytesToReadFromBuffer;\r
-\r
- TRACE("(%p, %p, %ld, %p)\n", iface,\r
- pv, cb, pcbRead);\r
-\r
- /*\r
- * If the caller is not interested in the nubmer of bytes read,\r
- * we use another buffer to avoid "if" statements in the code.\r
- */\r
- if (pcbRead==0)\r
- pcbRead = &bytesReadBuffer;\r
-\r
- /*\r
- * Using the known size of the stream, calculate the number of bytes\r
- * to read from the block chain\r
- */\r
- bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);\r
-\r
- /*\r
- * Lock the buffer in position and copy the data.\r
- */\r
- supportBuffer = GlobalLock(This->supportHandle);\r
-\r
- memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);\r
-\r
- /*\r
- * Move the current position to the new position\r
- */\r
- This->currentPosition.u.LowPart+=bytesToReadFromBuffer;\r
-\r
- /*\r
- * Return the number of bytes read.\r
- */\r
- *pcbRead = bytesToReadFromBuffer;\r
-\r
- /*\r
- * Cleanup\r
- */\r
- GlobalUnlock(This->supportHandle);\r
-\r
- /*\r
- * The function returns S_OK if the buffer was filled completely\r
- * it returns S_FALSE if the end of the stream is reached before the\r
- * buffer is filled\r
- */\r
- if(*pcbRead == cb)\r
- return S_OK;\r
-\r
- return S_FALSE;\r
-}\r
-\r
-/***\r
- * This method is part of the ISequentialStream interface.\r
- *\r
- * It writes a block of information to the stream at the current\r
- * position. It then moves the current position at the end of the\r
- * written block. If the stream is too small to fit the block,\r
- * the stream is grown to fit.\r
- *\r
- * See the documentation of ISequentialStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_Write(\r
- IStream* iface,\r
- const void* pv, /* [size_is][in] */\r
- ULONG cb, /* [in] */\r
- ULONG* pcbWritten) /* [out] */\r
-{\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
-\r
- void* supportBuffer;\r
- ULARGE_INTEGER newSize;\r
- ULONG bytesWritten = 0;\r
-\r
- TRACE("(%p, %p, %ld, %p)\n", iface,\r
- pv, cb, pcbWritten);\r
-\r
- /*\r
- * If the caller is not interested in the number of bytes written,\r
- * we use another buffer to avoid "if" statements in the code.\r
- */\r
- if (pcbWritten == 0)\r
- pcbWritten = &bytesWritten;\r
-\r
- if (cb == 0)\r
- {\r
- return S_OK;\r
- }\r
- else\r
- {\r
- newSize.u.HighPart = 0;\r
- newSize.u.LowPart = This->currentPosition.u.LowPart + cb;\r
- }\r
-\r
- /*\r
- * Verify if we need to grow the stream\r
- */\r
- if (newSize.u.LowPart > This->streamSize.u.LowPart)\r
- {\r
- /* grow stream */\r
- IStream_SetSize(iface, newSize);\r
- }\r
-\r
- /*\r
- * Lock the buffer in position and copy the data.\r
- */\r
- supportBuffer = GlobalLock(This->supportHandle);\r
-\r
- memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);\r
-\r
- /*\r
- * Move the current position to the new position\r
- */\r
- This->currentPosition.u.LowPart+=cb;\r
-\r
- /*\r
- * Return the number of bytes read.\r
- */\r
- *pcbWritten = cb;\r
-\r
- /*\r
- * Cleanup\r
- */\r
- GlobalUnlock(This->supportHandle);\r
-\r
- return S_OK;\r
-}\r
-\r
-/***\r
- * This method is part of the IStream interface.\r
- *\r
- * It will move the current stream pointer according to the parameters\r
- * given.\r
- *\r
- * See the documentation of IStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_Seek(\r
- IStream* iface,\r
- LARGE_INTEGER dlibMove, /* [in] */\r
- DWORD dwOrigin, /* [in] */\r
- ULARGE_INTEGER* plibNewPosition) /* [out] */\r
-{\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
-\r
- ULARGE_INTEGER newPosition;\r
-\r
- TRACE("(%p, %lx%08lx, %ld, %p)\n", iface, dlibMove.u.HighPart,\r
- dlibMove.u.LowPart, dwOrigin, plibNewPosition);\r
-\r
- /*\r
- * The file pointer is moved depending on the given "function"\r
- * parameter.\r
- */\r
- switch (dwOrigin)\r
- {\r
- case STREAM_SEEK_SET:\r
- newPosition.u.HighPart = 0;\r
- newPosition.u.LowPart = 0;\r
- break;\r
- case STREAM_SEEK_CUR:\r
- newPosition = This->currentPosition;\r
- break;\r
- case STREAM_SEEK_END:\r
- newPosition = This->streamSize;\r
- break;\r
- default:\r
- return STG_E_INVALIDFUNCTION;\r
- }\r
-\r
- /*\r
- * Move the actual file pointer\r
- * If the file pointer ends-up after the end of the stream, the next Write operation will\r
- * make the file larger. This is how it is documented.\r
- */\r
- if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart) return STG_E_INVALIDFUNCTION;\r
-\r
- newPosition.QuadPart = RtlLargeIntegerAdd(newPosition.QuadPart, dlibMove.QuadPart);\r
-\r
- if (plibNewPosition) *plibNewPosition = newPosition;\r
- This->currentPosition = newPosition;\r
-\r
- return S_OK;\r
-}\r
-\r
-/***\r
- * This method is part of the IStream interface.\r
- *\r
- * It will change the size of a stream.\r
- *\r
- * TODO: Switch from small blocks to big blocks and vice versa.\r
- *\r
- * See the documentation of IStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_SetSize(\r
- IStream* iface,\r
- ULARGE_INTEGER libNewSize) /* [in] */\r
-{\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
- HGLOBAL supportHandle;\r
-\r
- TRACE("(%p, %ld)\n", iface, libNewSize.u.LowPart);\r
-\r
- /*\r
- * As documented.\r
- */\r
- if (libNewSize.u.HighPart != 0)\r
- return STG_E_INVALIDFUNCTION;\r
-\r
- if (This->streamSize.u.LowPart == libNewSize.u.LowPart)\r
- return S_OK;\r
-\r
- /*\r
- * Re allocate the HGlobal to fit the new size of the stream.\r
- */\r
- supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);\r
-\r
- if (supportHandle == 0)\r
- return STG_E_MEDIUMFULL;\r
-\r
- This->supportHandle = supportHandle;\r
- This->streamSize.u.LowPart = libNewSize.u.LowPart;\r
-\r
- return S_OK;\r
-}\r
-\r
-/***\r
- * This method is part of the IStream interface.\r
- *\r
- * It will copy the 'cb' Bytes to 'pstm' IStream.\r
- *\r
- * See the documentation of IStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_CopyTo(\r
- IStream* iface,\r
- IStream* pstm, /* [unique][in] */\r
- ULARGE_INTEGER cb, /* [in] */\r
- ULARGE_INTEGER* pcbRead, /* [out] */\r
- ULARGE_INTEGER* pcbWritten) /* [out] */\r
-{\r
- HRESULT hr = S_OK;\r
- BYTE tmpBuffer[128];\r
- ULONG bytesRead, bytesWritten, copySize;\r
- ULARGE_INTEGER totalBytesRead;\r
- ULARGE_INTEGER totalBytesWritten;\r
-\r
- TRACE("(%p, %p, %ld, %p, %p)\n", iface, pstm,\r
- cb.u.LowPart, pcbRead, pcbWritten);\r
-\r
- /*\r
- * Sanity check\r
- */\r
- if ( pstm == 0 )\r
- return STG_E_INVALIDPOINTER;\r
-\r
- totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;\r
- totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;\r
-\r
- /*\r
- * use stack to store data temporarly\r
- * there is surely more performant way of doing it, for now this basic\r
- * implementation will do the job\r
- */\r
- while ( cb.u.LowPart > 0 )\r
- {\r
- if ( cb.u.LowPart >= 128 )\r
- copySize = 128;\r
- else\r
- copySize = cb.u.LowPart;\r
-\r
- IStream_Read(iface, tmpBuffer, copySize, &bytesRead);\r
-\r
- totalBytesRead.u.LowPart += bytesRead;\r
-\r
- IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);\r
-\r
- totalBytesWritten.u.LowPart += bytesWritten;\r
-\r
- /*\r
- * Check that read & write operations were succesfull\r
- */\r
- if (bytesRead != bytesWritten)\r
- {\r
- hr = STG_E_MEDIUMFULL;\r
- break;\r
- }\r
-\r
- if (bytesRead!=copySize)\r
- cb.u.LowPart = 0;\r
- else\r
- cb.u.LowPart -= bytesRead;\r
- }\r
-\r
- /*\r
- * Update number of bytes read and written\r
- */\r
- if (pcbRead)\r
- {\r
- pcbRead->u.LowPart = totalBytesRead.u.LowPart;\r
- pcbRead->u.HighPart = totalBytesRead.u.HighPart;\r
- }\r
-\r
- if (pcbWritten)\r
- {\r
- pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;\r
- pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;\r
- }\r
- return hr;\r
-}\r
-\r
-/***\r
- * This method is part of the IStream interface.\r
- *\r
- * For streams supported by HGLOBALS, this function does nothing.\r
- * This is what the documentation tells us.\r
- *\r
- * See the documentation of IStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_Commit(\r
- IStream* iface,\r
- DWORD grfCommitFlags) /* [in] */\r
-{\r
- return S_OK;\r
-}\r
-\r
-/***\r
- * This method is part of the IStream interface.\r
- *\r
- * For streams supported by HGLOBALS, this function does nothing.\r
- * This is what the documentation tells us.\r
- *\r
- * See the documentation of IStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_Revert(\r
- IStream* iface)\r
-{\r
- return S_OK;\r
-}\r
-\r
-/***\r
- * This method is part of the IStream interface.\r
- *\r
- * For streams supported by HGLOBALS, this function does nothing.\r
- * This is what the documentation tells us.\r
- *\r
- * See the documentation of IStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_LockRegion(\r
- IStream* iface,\r
- ULARGE_INTEGER libOffset, /* [in] */\r
- ULARGE_INTEGER cb, /* [in] */\r
- DWORD dwLockType) /* [in] */\r
-{\r
- return S_OK;\r
-}\r
-\r
-/*\r
- * This method is part of the IStream interface.\r
- *\r
- * For streams supported by HGLOBALS, this function does nothing.\r
- * This is what the documentation tells us.\r
- *\r
- * See the documentation of IStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(\r
- IStream* iface,\r
- ULARGE_INTEGER libOffset, /* [in] */\r
- ULARGE_INTEGER cb, /* [in] */\r
- DWORD dwLockType) /* [in] */\r
-{\r
- return S_OK;\r
-}\r
-\r
-/***\r
- * This method is part of the IStream interface.\r
- *\r
- * This method returns information about the current\r
- * stream.\r
- *\r
- * See the documentation of IStream for more info.\r
- */\r
-static HRESULT WINAPI HGLOBALStreamImpl_Stat(\r
- IStream* iface,\r
- STATSTG* pstatstg, /* [out] */\r
- DWORD grfStatFlag) /* [in] */\r
-{\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
-\r
- memset(pstatstg, 0, sizeof(STATSTG));\r
-\r
- pstatstg->pwcsName = NULL;\r
- pstatstg->type = STGTY_STREAM;\r
- pstatstg->cbSize = This->streamSize;\r
-\r
- return S_OK;\r
-}\r
-\r
-static HRESULT WINAPI HGLOBALStreamImpl_Clone(\r
- IStream* iface,\r
- IStream** ppstm) /* [out] */\r
-{\r
- ULARGE_INTEGER dummy;\r
- LARGE_INTEGER offset;\r
- HRESULT hr;\r
- HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;\r
- TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface,This->deleteOnRelease,(long)This->currentPosition.QuadPart);\r
- hr=CreateStreamOnHGlobal(This->supportHandle, FALSE, ppstm);\r
- if(FAILED(hr))\r
- return hr;\r
- offset.QuadPart=(LONGLONG)This->currentPosition.QuadPart;\r
- HGLOBALStreamImpl_Seek(*ppstm,offset,STREAM_SEEK_SET,&dummy);\r
- return S_OK;\r
-}\r
-\r
-/*\r
- * Virtual function table for the HGLOBALStreamImpl class.\r
- */\r
-static IStreamVtbl HGLOBALStreamImpl_Vtbl =\r
-{\r
- HGLOBALStreamImpl_QueryInterface,\r
- HGLOBALStreamImpl_AddRef,\r
- HGLOBALStreamImpl_Release,\r
- HGLOBALStreamImpl_Read,\r
- HGLOBALStreamImpl_Write,\r
- HGLOBALStreamImpl_Seek,\r
- HGLOBALStreamImpl_SetSize,\r
- HGLOBALStreamImpl_CopyTo,\r
- HGLOBALStreamImpl_Commit,\r
- HGLOBALStreamImpl_Revert,\r
- HGLOBALStreamImpl_LockRegion,\r
- HGLOBALStreamImpl_UnlockRegion,\r
- HGLOBALStreamImpl_Stat,\r
- HGLOBALStreamImpl_Clone\r
-};\r
-\r
-/******************************************************************************\r
-** HGLOBALStreamImpl implementation\r
-*/\r
-\r
-/***\r
- * This is the constructor for the HGLOBALStreamImpl class.\r
- *\r
- * Params:\r
- * hGlobal - Handle that will support the stream. can be NULL.\r
- * fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released\r
- * when the IStream object is destroyed.\r
- */\r
-HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(\r
- HGLOBAL hGlobal,\r
- BOOL fDeleteOnRelease)\r
-{\r
- HGLOBALStreamImpl* newStream;\r
-\r
- newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));\r
-\r
- if (newStream!=0)\r
- {\r
- /*\r
- * Set-up the virtual function table and reference count.\r
- */\r
- newStream->lpVtbl = &HGLOBALStreamImpl_Vtbl;\r
- newStream->ref = 0;\r
-\r
- /*\r
- * Initialize the support.\r
- */\r
- newStream->supportHandle = hGlobal;\r
- newStream->deleteOnRelease = fDeleteOnRelease;\r
-\r
- /*\r
- * This method will allocate a handle if one is not supplied.\r
- */\r
- if (!newStream->supportHandle)\r
- {\r
- newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |\r
- GMEM_SHARE, 0);\r
- }\r
-\r
- /*\r
- * Start the stream at the beginning.\r
- */\r
- newStream->currentPosition.u.HighPart = 0;\r
- newStream->currentPosition.u.LowPart = 0;\r
-\r
- /*\r
- * Initialize the size of the stream to the size of the handle.\r
- */\r
- newStream->streamSize.u.HighPart = 0;\r
- newStream->streamSize.u.LowPart = GlobalSize(newStream->supportHandle);\r
- }\r
-\r
- return newStream;\r
-}\r
-\r
-\r
-/***********************************************************************\r
- * CreateStreamOnHGlobal [OLE32.@]\r
- */\r
-HRESULT WINAPI CreateStreamOnHGlobal(\r
- HGLOBAL hGlobal,\r
- BOOL fDeleteOnRelease,\r
- LPSTREAM* ppstm)\r
-{\r
- HGLOBALStreamImpl* newStream;\r
-\r
- newStream = HGLOBALStreamImpl_Construct(hGlobal,\r
- fDeleteOnRelease);\r
-\r
- if (newStream!=NULL)\r
- {\r
- return IUnknown_QueryInterface((IUnknown*)newStream,\r
- &IID_IStream,\r
- (void**)ppstm);\r
- }\r
-\r
- return E_OUTOFMEMORY;\r
-}\r
-\r
-/***********************************************************************\r
- * GetHGlobalFromStream [OLE32.@]\r
- */\r
-HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)\r
-{\r
- HGLOBALStreamImpl* pStream;\r
-\r
- if (pstm == NULL)\r
- return E_INVALIDARG;\r
-\r
- pStream = (HGLOBALStreamImpl*) pstm;\r
-\r
- /*\r
- * Verify that the stream object was created with CreateStreamOnHGlobal.\r
- */\r
- if (pStream->lpVtbl == &HGLOBALStreamImpl_Vtbl)\r
- *phglobal = pStream->supportHandle;\r
- else\r
- {\r
- *phglobal = 0;\r
- return E_INVALIDARG;\r
- }\r
-\r
- return S_OK;\r
-}\r
+/*
+ * HGLOBAL Stream implementation
+ *
+ * This file contains the implementation of the stream interface
+ * for streams contained supported by an HGLOBAL pointer.
+ *
+ * Copyright 1999 Francis Beaudet
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "objbase.h"
+#include "ole2.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "winternl.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(storage);
+
+/****************************************************************************
+ * HGLOBALStreamImpl definition.
+ *
+ * This class imlements the IStream inteface and represents a stream
+ * supported by an HGLOBAL pointer.
+ */
+struct HGLOBALStreamImpl
+{
+ IStreamVtbl *lpVtbl; /* Needs to be the first item in the struct
+ * since we want to cast this in an IStream pointer */
+
+ /*
+ * Reference count
+ */
+ ULONG ref;
+
+ /*
+ * Support for the stream
+ */
+ HGLOBAL supportHandle;
+
+ /*
+ * This flag is TRUE if the HGLOBAL is destroyed when the stream
+ * is finally released.
+ */
+ BOOL deleteOnRelease;
+
+ /*
+ * Helper variable that contains the size of the stream
+ */
+ ULARGE_INTEGER streamSize;
+
+ /*
+ * This is the current position of the cursor in the stream
+ */
+ ULARGE_INTEGER currentPosition;
+};
+
+typedef struct HGLOBALStreamImpl HGLOBALStreamImpl;
+
+/***
+ * This is the destructor of the HGLOBALStreamImpl class.
+ *
+ * This method will clean-up all the resources used-up by the given HGLOBALStreamImpl
+ * class. The pointer passed-in to this function will be freed and will not
+ * be valid anymore.
+ */
+static void HGLOBALStreamImpl_Destroy(HGLOBALStreamImpl* This)
+{
+ TRACE("(%p)\n", This);
+
+ /*
+ * Release the HGlobal if the constructor asked for that.
+ */
+ if (This->deleteOnRelease)
+ {
+ GlobalFree(This->supportHandle);
+ This->supportHandle=0;
+ }
+
+ /*
+ * Finally, free the memory used-up by the class.
+ */
+ HeapFree(GetProcessHeap(), 0, This);
+}
+
+/***
+ * This implements the IUnknown method AddRef for this
+ * class
+ */
+static ULONG WINAPI HGLOBALStreamImpl_AddRef(
+ IStream* iface)
+{
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+ return InterlockedIncrement(&This->ref);
+}
+
+/***
+ * This implements the IUnknown method QueryInterface for this
+ * class
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
+ IStream* iface,
+ REFIID riid, /* [in] */
+ void** ppvObject) /* [iid_is][out] */
+{
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+
+ /*
+ * Perform a sanity check on the parameters.
+ */
+ if (ppvObject==0)
+ return E_INVALIDARG;
+
+ /*
+ * Initialize the return parameter.
+ */
+ *ppvObject = 0;
+
+ /*
+ * Compare the riid with the interface IDs implemented by this object.
+ */
+ if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
+ {
+ *ppvObject = (IStream*)This;
+ }
+ else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
+ {
+ *ppvObject = (IStream*)This;
+ }
+
+ /*
+ * Check that we obtained an interface.
+ */
+ if ((*ppvObject)==0)
+ return E_NOINTERFACE;
+
+ /*
+ * Query Interface always increases the reference count by one when it is
+ * successful
+ */
+ HGLOBALStreamImpl_AddRef(iface);
+
+ return S_OK;
+}
+
+/***
+ * This implements the IUnknown method Release for this
+ * class
+ */
+static ULONG WINAPI HGLOBALStreamImpl_Release(
+ IStream* iface)
+{
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+ ULONG newRef;
+
+ newRef = InterlockedDecrement(&This->ref);
+
+ /*
+ * If the reference count goes down to 0, perform suicide.
+ */
+ if (newRef==0)
+ {
+ HGLOBALStreamImpl_Destroy(This);
+ }
+
+ return newRef;
+}
+
+/***
+ * This method is part of the ISequentialStream interface.
+ *
+ * If reads a block of information from the stream at the current
+ * position. It then moves the current position at the end of the
+ * read block
+ *
+ * See the documentation of ISequentialStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_Read(
+ IStream* iface,
+ void* pv, /* [length_is][size_is][out] */
+ ULONG cb, /* [in] */
+ ULONG* pcbRead) /* [out] */
+{
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+
+ void* supportBuffer;
+ ULONG bytesReadBuffer;
+ ULONG bytesToReadFromBuffer;
+
+ TRACE("(%p, %p, %ld, %p)\n", iface,
+ pv, cb, pcbRead);
+
+ /*
+ * If the caller is not interested in the nubmer of bytes read,
+ * we use another buffer to avoid "if" statements in the code.
+ */
+ if (pcbRead==0)
+ pcbRead = &bytesReadBuffer;
+
+ /*
+ * Using the known size of the stream, calculate the number of bytes
+ * to read from the block chain
+ */
+ bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
+
+ /*
+ * Lock the buffer in position and copy the data.
+ */
+ supportBuffer = GlobalLock(This->supportHandle);
+
+ memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
+
+ /*
+ * Move the current position to the new position
+ */
+ This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
+
+ /*
+ * Return the number of bytes read.
+ */
+ *pcbRead = bytesToReadFromBuffer;
+
+ /*
+ * Cleanup
+ */
+ GlobalUnlock(This->supportHandle);
+
+ /*
+ * The function returns S_OK if the buffer was filled completely
+ * it returns S_FALSE if the end of the stream is reached before the
+ * buffer is filled
+ */
+ if(*pcbRead == cb)
+ return S_OK;
+
+ return S_FALSE;
+}
+
+/***
+ * This method is part of the ISequentialStream interface.
+ *
+ * It writes a block of information to the stream at the current
+ * position. It then moves the current position at the end of the
+ * written block. If the stream is too small to fit the block,
+ * the stream is grown to fit.
+ *
+ * See the documentation of ISequentialStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_Write(
+ IStream* iface,
+ const void* pv, /* [size_is][in] */
+ ULONG cb, /* [in] */
+ ULONG* pcbWritten) /* [out] */
+{
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+
+ void* supportBuffer;
+ ULARGE_INTEGER newSize;
+ ULONG bytesWritten = 0;
+
+ TRACE("(%p, %p, %ld, %p)\n", iface,
+ pv, cb, pcbWritten);
+
+ /*
+ * If the caller is not interested in the number of bytes written,
+ * we use another buffer to avoid "if" statements in the code.
+ */
+ if (pcbWritten == 0)
+ pcbWritten = &bytesWritten;
+
+ if (cb == 0)
+ {
+ return S_OK;
+ }
+ else
+ {
+ newSize.u.HighPart = 0;
+ newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
+ }
+
+ /*
+ * Verify if we need to grow the stream
+ */
+ if (newSize.u.LowPart > This->streamSize.u.LowPart)
+ {
+ /* grow stream */
+ IStream_SetSize(iface, newSize);
+ }
+
+ /*
+ * Lock the buffer in position and copy the data.
+ */
+ supportBuffer = GlobalLock(This->supportHandle);
+
+ memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
+
+ /*
+ * Move the current position to the new position
+ */
+ This->currentPosition.u.LowPart+=cb;
+
+ /*
+ * Return the number of bytes read.
+ */
+ *pcbWritten = cb;
+
+ /*
+ * Cleanup
+ */
+ GlobalUnlock(This->supportHandle);
+
+ return S_OK;
+}
+
+/***
+ * This method is part of the IStream interface.
+ *
+ * It will move the current stream pointer according to the parameters
+ * given.
+ *
+ * See the documentation of IStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_Seek(
+ IStream* iface,
+ LARGE_INTEGER dlibMove, /* [in] */
+ DWORD dwOrigin, /* [in] */
+ ULARGE_INTEGER* plibNewPosition) /* [out] */
+{
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+
+ ULARGE_INTEGER newPosition;
+
+ TRACE("(%p, %lx%08lx, %ld, %p)\n", iface, dlibMove.u.HighPart,
+ dlibMove.u.LowPart, dwOrigin, plibNewPosition);
+
+ /*
+ * The file pointer is moved depending on the given "function"
+ * parameter.
+ */
+ switch (dwOrigin)
+ {
+ case STREAM_SEEK_SET:
+ newPosition.u.HighPart = 0;
+ newPosition.u.LowPart = 0;
+ break;
+ case STREAM_SEEK_CUR:
+ newPosition = This->currentPosition;
+ break;
+ case STREAM_SEEK_END:
+ newPosition = This->streamSize;
+ break;
+ default:
+ return STG_E_INVALIDFUNCTION;
+ }
+
+ /*
+ * Move the actual file pointer
+ * If the file pointer ends-up after the end of the stream, the next Write operation will
+ * make the file larger. This is how it is documented.
+ */
+ if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart) return STG_E_INVALIDFUNCTION;
+
+ newPosition.QuadPart = RtlLargeIntegerAdd(newPosition.QuadPart, dlibMove.QuadPart);
+
+ if (plibNewPosition) *plibNewPosition = newPosition;
+ This->currentPosition = newPosition;
+
+ return S_OK;
+}
+
+/***
+ * This method is part of the IStream interface.
+ *
+ * It will change the size of a stream.
+ *
+ * TODO: Switch from small blocks to big blocks and vice versa.
+ *
+ * See the documentation of IStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_SetSize(
+ IStream* iface,
+ ULARGE_INTEGER libNewSize) /* [in] */
+{
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+ HGLOBAL supportHandle;
+
+ TRACE("(%p, %ld)\n", iface, libNewSize.u.LowPart);
+
+ /*
+ * As documented.
+ */
+ if (libNewSize.u.HighPart != 0)
+ return STG_E_INVALIDFUNCTION;
+
+ if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
+ return S_OK;
+
+ /*
+ * Re allocate the HGlobal to fit the new size of the stream.
+ */
+ supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
+
+ if (supportHandle == 0)
+ return STG_E_MEDIUMFULL;
+
+ This->supportHandle = supportHandle;
+ This->streamSize.u.LowPart = libNewSize.u.LowPart;
+
+ return S_OK;
+}
+
+/***
+ * This method is part of the IStream interface.
+ *
+ * It will copy the 'cb' Bytes to 'pstm' IStream.
+ *
+ * See the documentation of IStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
+ IStream* iface,
+ IStream* pstm, /* [unique][in] */
+ ULARGE_INTEGER cb, /* [in] */
+ ULARGE_INTEGER* pcbRead, /* [out] */
+ ULARGE_INTEGER* pcbWritten) /* [out] */
+{
+ HRESULT hr = S_OK;
+ BYTE tmpBuffer[128];
+ ULONG bytesRead, bytesWritten, copySize;
+ ULARGE_INTEGER totalBytesRead;
+ ULARGE_INTEGER totalBytesWritten;
+
+ TRACE("(%p, %p, %ld, %p, %p)\n", iface, pstm,
+ cb.u.LowPart, pcbRead, pcbWritten);
+
+ /*
+ * Sanity check
+ */
+ if ( pstm == 0 )
+ return STG_E_INVALIDPOINTER;
+
+ totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
+ totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
+
+ /*
+ * use stack to store data temporarly
+ * there is surely more performant way of doing it, for now this basic
+ * implementation will do the job
+ */
+ while ( cb.u.LowPart > 0 )
+ {
+ if ( cb.u.LowPart >= 128 )
+ copySize = 128;
+ else
+ copySize = cb.u.LowPart;
+
+ IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
+
+ totalBytesRead.u.LowPart += bytesRead;
+
+ IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
+
+ totalBytesWritten.u.LowPart += bytesWritten;
+
+ /*
+ * Check that read & write operations were succesfull
+ */
+ if (bytesRead != bytesWritten)
+ {
+ hr = STG_E_MEDIUMFULL;
+ break;
+ }
+
+ if (bytesRead!=copySize)
+ cb.u.LowPart = 0;
+ else
+ cb.u.LowPart -= bytesRead;
+ }
+
+ /*
+ * Update number of bytes read and written
+ */
+ if (pcbRead)
+ {
+ pcbRead->u.LowPart = totalBytesRead.u.LowPart;
+ pcbRead->u.HighPart = totalBytesRead.u.HighPart;
+ }
+
+ if (pcbWritten)
+ {
+ pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
+ pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
+ }
+ return hr;
+}
+
+/***
+ * This method is part of the IStream interface.
+ *
+ * For streams supported by HGLOBALS, this function does nothing.
+ * This is what the documentation tells us.
+ *
+ * See the documentation of IStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_Commit(
+ IStream* iface,
+ DWORD grfCommitFlags) /* [in] */
+{
+ return S_OK;
+}
+
+/***
+ * This method is part of the IStream interface.
+ *
+ * For streams supported by HGLOBALS, this function does nothing.
+ * This is what the documentation tells us.
+ *
+ * See the documentation of IStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_Revert(
+ IStream* iface)
+{
+ return S_OK;
+}
+
+/***
+ * This method is part of the IStream interface.
+ *
+ * For streams supported by HGLOBALS, this function does nothing.
+ * This is what the documentation tells us.
+ *
+ * See the documentation of IStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
+ IStream* iface,
+ ULARGE_INTEGER libOffset, /* [in] */
+ ULARGE_INTEGER cb, /* [in] */
+ DWORD dwLockType) /* [in] */
+{
+ return S_OK;
+}
+
+/*
+ * This method is part of the IStream interface.
+ *
+ * For streams supported by HGLOBALS, this function does nothing.
+ * This is what the documentation tells us.
+ *
+ * See the documentation of IStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
+ IStream* iface,
+ ULARGE_INTEGER libOffset, /* [in] */
+ ULARGE_INTEGER cb, /* [in] */
+ DWORD dwLockType) /* [in] */
+{
+ return S_OK;
+}
+
+/***
+ * This method is part of the IStream interface.
+ *
+ * This method returns information about the current
+ * stream.
+ *
+ * See the documentation of IStream for more info.
+ */
+static HRESULT WINAPI HGLOBALStreamImpl_Stat(
+ IStream* iface,
+ STATSTG* pstatstg, /* [out] */
+ DWORD grfStatFlag) /* [in] */
+{
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+
+ memset(pstatstg, 0, sizeof(STATSTG));
+
+ pstatstg->pwcsName = NULL;
+ pstatstg->type = STGTY_STREAM;
+ pstatstg->cbSize = This->streamSize;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI HGLOBALStreamImpl_Clone(
+ IStream* iface,
+ IStream** ppstm) /* [out] */
+{
+ ULARGE_INTEGER dummy;
+ LARGE_INTEGER offset;
+ HRESULT hr;
+ HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface;
+ TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface,This->deleteOnRelease,(long)This->currentPosition.QuadPart);
+ hr=CreateStreamOnHGlobal(This->supportHandle, FALSE, ppstm);
+ if(FAILED(hr))
+ return hr;
+ offset.QuadPart=(LONGLONG)This->currentPosition.QuadPart;
+ HGLOBALStreamImpl_Seek(*ppstm,offset,STREAM_SEEK_SET,&dummy);
+ return S_OK;
+}
+
+/*
+ * Virtual function table for the HGLOBALStreamImpl class.
+ */
+static IStreamVtbl HGLOBALStreamImpl_Vtbl =
+{
+ HGLOBALStreamImpl_QueryInterface,
+ HGLOBALStreamImpl_AddRef,
+ HGLOBALStreamImpl_Release,
+ HGLOBALStreamImpl_Read,
+ HGLOBALStreamImpl_Write,
+ HGLOBALStreamImpl_Seek,
+ HGLOBALStreamImpl_SetSize,
+ HGLOBALStreamImpl_CopyTo,
+ HGLOBALStreamImpl_Commit,
+ HGLOBALStreamImpl_Revert,
+ HGLOBALStreamImpl_LockRegion,
+ HGLOBALStreamImpl_UnlockRegion,
+ HGLOBALStreamImpl_Stat,
+ HGLOBALStreamImpl_Clone
+};
+
+/******************************************************************************
+** HGLOBALStreamImpl implementation
+*/
+
+/***
+ * This is the constructor for the HGLOBALStreamImpl class.
+ *
+ * Params:
+ * hGlobal - Handle that will support the stream. can be NULL.
+ * fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released
+ * when the IStream object is destroyed.
+ */
+HGLOBALStreamImpl* HGLOBALStreamImpl_Construct(
+ HGLOBAL hGlobal,
+ BOOL fDeleteOnRelease)
+{
+ HGLOBALStreamImpl* newStream;
+
+ newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
+
+ if (newStream!=0)
+ {
+ /*
+ * Set-up the virtual function table and reference count.
+ */
+ newStream->lpVtbl = &HGLOBALStreamImpl_Vtbl;
+ newStream->ref = 0;
+
+ /*
+ * Initialize the support.
+ */
+ newStream->supportHandle = hGlobal;
+ newStream->deleteOnRelease = fDeleteOnRelease;
+
+ /*
+ * This method will allocate a handle if one is not supplied.
+ */
+ if (!newStream->supportHandle)
+ {
+ newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
+ GMEM_SHARE, 0);
+ }
+
+ /*
+ * Start the stream at the beginning.
+ */
+ newStream->currentPosition.u.HighPart = 0;
+ newStream->currentPosition.u.LowPart = 0;
+
+ /*
+ * Initialize the size of the stream to the size of the handle.
+ */
+ newStream->streamSize.u.HighPart = 0;
+ newStream->streamSize.u.LowPart = GlobalSize(newStream->supportHandle);
+ }
+
+ return newStream;
+}
+
+
+/***********************************************************************
+ * CreateStreamOnHGlobal [OLE32.@]
+ */
+HRESULT WINAPI CreateStreamOnHGlobal(
+ HGLOBAL hGlobal,
+ BOOL fDeleteOnRelease,
+ LPSTREAM* ppstm)
+{
+ HGLOBALStreamImpl* newStream;
+
+ newStream = HGLOBALStreamImpl_Construct(hGlobal,
+ fDeleteOnRelease);
+
+ if (newStream!=NULL)
+ {
+ return IUnknown_QueryInterface((IUnknown*)newStream,
+ &IID_IStream,
+ (void**)ppstm);
+ }
+
+ return E_OUTOFMEMORY;
+}
+
+/***********************************************************************
+ * GetHGlobalFromStream [OLE32.@]
+ */
+HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
+{
+ HGLOBALStreamImpl* pStream;
+
+ if (pstm == NULL)
+ return E_INVALIDARG;
+
+ pStream = (HGLOBALStreamImpl*) pstm;
+
+ /*
+ * Verify that the stream object was created with CreateStreamOnHGlobal.
+ */
+ if (pStream->lpVtbl == &HGLOBALStreamImpl_Vtbl)
+ *phglobal = pStream->supportHandle;
+ else
+ {
+ *phglobal = 0;
+ return E_INVALIDARG;
+ }
+
+ return S_OK;
+}