/******************************************************************************1*2* Global memory implementation of ILockBytes.3*4* Copyright 1999 Thuy Nguyen5*6* This library is free software; you can redistribute it and/or7* modify it under the terms of the GNU Lesser General Public8* License as published by the Free Software Foundation; either9* version 2.1 of the License, or (at your option) any later version.10*11* This library is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU14* Lesser General Public License for more details.15*16* You should have received a copy of the GNU Lesser General Public17* License along with this library; if not, write to the Free Software18* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA19*/2021#include <assert.h>22#include <stdarg.h>23#include <string.h>2425#define COBJMACROS26#include "windef.h"27#include "winbase.h"28#include "winuser.h"29#include "objbase.h"30#include "ole2.h"31#include "winerror.h"3233#include "wine/debug.h"3435WINE_DEFAULT_DEBUG_CHANNEL(ole);3637/******************************************************************************38* HGLOBALLockBytesImpl definition.39*40* This class implements the ILockBytes interface and represents a byte array41* object supported by an HGLOBAL pointer.42*/43struct HGLOBALLockBytesImpl44{45ILockBytes ILockBytes_iface;46LONG ref;4748/*49* Support for the LockBytes object50*/51HGLOBAL supportHandle;5253/*54* This flag is TRUE if the HGLOBAL is destroyed when the object55* is finally released.56*/57BOOL deleteOnRelease;5859/*60* Helper variable that contains the size of the byte array61*/62ULARGE_INTEGER byteArraySize;63};6465typedef struct HGLOBALLockBytesImpl HGLOBALLockBytesImpl;6667static inline HGLOBALLockBytesImpl *impl_from_ILockBytes( ILockBytes *iface )68{69return CONTAINING_RECORD(iface, HGLOBALLockBytesImpl, ILockBytes_iface);70}7172static const ILockBytesVtbl HGLOBALLockBytesImpl_Vtbl;7374/******************************************************************************75* CreateILockBytesOnHGlobal [OLE32.@]76*77* Create a byte array object which is intended to be the compound file foundation.78* This object supports a COM implementation of the ILockBytes interface.79*80* PARAMS81* global [ I] Global memory handle82* delete_on_release [ I] Whether the handle should be freed when the object is released.83* ret [ O] Address of ILockBytes pointer that receives84* the interface pointer to the new byte array object.85*86* RETURNS87* Success: S_OK88*89* NOTES90* The supplied ILockBytes pointer can be used by the StgCreateDocfileOnILockBytes91* function to build a compound file on top of this byte array object.92* The ILockBytes interface instance calls the GlobalReAlloc function to grow93* the memory block as required.94*/95HRESULT WINAPI CreateILockBytesOnHGlobal(HGLOBAL global, BOOL delete_on_release, ILockBytes **ret)96{97HGLOBALLockBytesImpl* lockbytes;9899lockbytes = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALLockBytesImpl));100if (!lockbytes) return E_OUTOFMEMORY;101102lockbytes->ILockBytes_iface.lpVtbl = &HGLOBALLockBytesImpl_Vtbl;103lockbytes->ref = 1;104105/*106* Initialize the support.107*/108lockbytes->supportHandle = global;109lockbytes->deleteOnRelease = delete_on_release;110111/*112* This method will allocate a handle if one is not supplied.113*/114if (lockbytes->supportHandle == 0)115lockbytes->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, 0);116117/*118* Initialize the size of the array to the size of the handle.119*/120lockbytes->byteArraySize.HighPart = 0;121lockbytes->byteArraySize.LowPart = GlobalSize(lockbytes->supportHandle);122123*ret = &lockbytes->ILockBytes_iface;124125return S_OK;126}127128/******************************************************************************129* GetHGlobalFromILockBytes [OLE32.@]130*131* Retrieve a global memory handle to a byte array object created132* using the CreateILockBytesOnHGlobal function.133*134* PARAMS135* plkbyt [ I] Pointer to the ILockBytes interface on byte array object136* phglobal [ O] Address to store a global memory handle137* RETURNS138* S_OK if *phglobal has a correct value139* E_INVALIDARG if any parameters are invalid140*141*/142HRESULT WINAPI GetHGlobalFromILockBytes(ILockBytes* iface, HGLOBAL* phglobal)143{144HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);145STATSTG stbuf;146HRESULT hres;147ULARGE_INTEGER start;148ULONG xread;149150*phglobal = 0;151if (This->ILockBytes_iface.lpVtbl == &HGLOBALLockBytesImpl_Vtbl) {152*phglobal = This->supportHandle;153if (*phglobal == 0)154return E_INVALIDARG;155return S_OK;156}157/* It is not our lockbytes implementation, so use a more generic way */158hres = ILockBytes_Stat(iface,&stbuf,STATFLAG_NONAME);159if (hres != S_OK) {160ERR("Cannot ILockBytes_Stat, %lx\n",hres);161return hres;162}163TRACE("cbSize is %s\n", wine_dbgstr_longlong(stbuf.cbSize.QuadPart));164*phglobal = GlobalAlloc( GMEM_MOVEABLE|GMEM_SHARE, stbuf.cbSize.LowPart);165if (!*phglobal)166return E_INVALIDARG;167memset(&start,0,sizeof(start));168hres = ILockBytes_ReadAt(iface, start, GlobalLock(*phglobal), stbuf.cbSize.LowPart, &xread);169GlobalUnlock(*phglobal);170if (hres != S_OK) {171FIXME("%p->ReadAt failed with %lx\n",iface,hres);172return hres;173}174if (stbuf.cbSize.LowPart != xread) {175FIXME("Read size is not requested size %ld vs %ld?\n",stbuf.cbSize.LowPart, xread);176}177return S_OK;178}179180/******************************************************************************181*182* HGLOBALLockBytesImpl implementation183*184*/185186/******************************************************************************187* This implements the IUnknown method QueryInterface for this188* class189*/190static HRESULT WINAPI HGLOBALLockBytesImpl_QueryInterface(191ILockBytes* iface,192REFIID riid, /* [in] */193void** ppvObject) /* [iid_is][out] */194{195HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);196197if (ppvObject==0)198return E_INVALIDARG;199200*ppvObject = 0;201202if (IsEqualIID(riid, &IID_IUnknown) ||203IsEqualIID(riid, &IID_ILockBytes))204{205*ppvObject = &This->ILockBytes_iface;206}207else208return E_NOINTERFACE;209210ILockBytes_AddRef(iface);211212return S_OK;213}214215/******************************************************************************216* This implements the IUnknown method AddRef for this217* class218*/219static ULONG WINAPI HGLOBALLockBytesImpl_AddRef(ILockBytes* iface)220{221HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);222return InterlockedIncrement(&This->ref);223}224225/******************************************************************************226* This implements the IUnknown method Release for this227* class228*/229static ULONG WINAPI HGLOBALLockBytesImpl_Release(ILockBytes* iface)230{231HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);232ULONG ref;233234ref = InterlockedDecrement(&This->ref);235if (!ref)236{237if (This->deleteOnRelease)238{239GlobalFree(This->supportHandle);240This->supportHandle = 0;241}242HeapFree(GetProcessHeap(), 0, This);243}244245return ref;246}247248/******************************************************************************249* This method is part of the ILockBytes interface.250*251* It reads a block of information from the byte array at the specified252* offset.253*254* See the documentation of ILockBytes for more info.255*/256static HRESULT WINAPI HGLOBALLockBytesImpl_ReadAt(257ILockBytes* iface,258ULARGE_INTEGER ulOffset, /* [in] */259void* pv, /* [length_is][size_is][out] */260ULONG cb, /* [in] */261ULONG* pcbRead) /* [out] */262{263HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);264265void* supportBuffer;266ULONG bytesReadBuffer = 0;267ULONG bytesToReadFromBuffer;268269/*270* If the caller is not interested in the number of bytes read,271* we use another buffer to avoid "if" statements in the code.272*/273if (pcbRead == 0)274pcbRead = &bytesReadBuffer;275276/*277* Make sure the offset is valid.278*/279if (ulOffset.LowPart > This->byteArraySize.LowPart)280return E_FAIL;281282/*283* Using the known size of the array, calculate the number of bytes284* to read.285*/286bytesToReadFromBuffer = min(This->byteArraySize.LowPart - ulOffset.LowPart, cb);287288/*289* Lock the buffer in position and copy the data.290*/291supportBuffer = GlobalLock(This->supportHandle);292293memcpy(pv,294(char *) supportBuffer + ulOffset.LowPart,295bytesToReadFromBuffer);296297/*298* Return the number of bytes read.299*/300*pcbRead = bytesToReadFromBuffer;301302/*303* Cleanup304*/305GlobalUnlock(This->supportHandle);306307/*308* The function returns S_OK if the specified number of bytes were read309* or the end of the array was reached.310* It returns STG_E_READFAULT if the number of bytes to read does not equal311* the number of bytes actually read.312*/313if(*pcbRead == cb)314return S_OK;315316return STG_E_READFAULT;317}318319/******************************************************************************320* This method is part of the ILockBytes interface.321*322* It writes the specified bytes at the specified offset.323* position. If the array is too small, it will be resized.324*325* See the documentation of ILockBytes for more info.326*/327static HRESULT WINAPI HGLOBALLockBytesImpl_WriteAt(328ILockBytes* iface,329ULARGE_INTEGER ulOffset, /* [in] */330const void* pv, /* [size_is][in] */331ULONG cb, /* [in] */332ULONG* pcbWritten) /* [out] */333{334HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);335336void* supportBuffer;337ULARGE_INTEGER newSize;338ULONG bytesWritten = 0;339340/*341* If the caller is not interested in the number of bytes written,342* we use another buffer to avoid "if" statements in the code.343*/344if (pcbWritten == 0)345pcbWritten = &bytesWritten;346347if (cb == 0)348{349return S_OK;350}351else352{353newSize.HighPart = 0;354newSize.LowPart = ulOffset.LowPart + cb;355}356357/*358* Verify if we need to grow the stream359*/360if (newSize.LowPart > This->byteArraySize.LowPart)361{362/* grow stream */363if (ILockBytes_SetSize(iface, newSize) == STG_E_MEDIUMFULL)364return STG_E_MEDIUMFULL;365}366367/*368* Lock the buffer in position and copy the data.369*/370supportBuffer = GlobalLock(This->supportHandle);371372memcpy((char *) supportBuffer + ulOffset.LowPart, pv, cb);373374/*375* Return the number of bytes written.376*/377*pcbWritten = cb;378379/*380* Cleanup381*/382GlobalUnlock(This->supportHandle);383384return S_OK;385}386387/******************************************************************************388* This method is part of the ILockBytes interface.389*390* See the documentation of ILockBytes for more info.391*/392static HRESULT WINAPI HGLOBALLockBytesImpl_Flush(ILockBytes* iface)393{394return S_OK;395}396397/******************************************************************************398* This method is part of the ILockBytes interface.399*400* It will change the size of the byte array.401*402* See the documentation of ILockBytes for more info.403*/404static HRESULT WINAPI HGLOBALLockBytesImpl_SetSize(405ILockBytes* iface,406ULARGE_INTEGER libNewSize) /* [in] */407{408HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);409HGLOBAL supportHandle;410411/*412* As documented.413*/414if (libNewSize.HighPart != 0)415return STG_E_INVALIDFUNCTION;416417if (This->byteArraySize.LowPart == libNewSize.LowPart)418return S_OK;419420/*421* Re allocate the HGlobal to fit the new size of the stream.422*/423supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.LowPart, GMEM_MOVEABLE);424425if (supportHandle == 0)426return STG_E_MEDIUMFULL;427428This->supportHandle = supportHandle;429This->byteArraySize.LowPart = libNewSize.LowPart;430431return S_OK;432}433434/******************************************************************************435* This method is part of the ILockBytes interface.436*437* The global memory implementation of ILockBytes does not support locking.438*439* See the documentation of ILockBytes for more info.440*/441static HRESULT WINAPI HGLOBALLockBytesImpl_LockRegion(442ILockBytes* iface,443ULARGE_INTEGER libOffset, /* [in] */444ULARGE_INTEGER cb, /* [in] */445DWORD dwLockType) /* [in] */446{447return STG_E_INVALIDFUNCTION;448}449450/******************************************************************************451* This method is part of the ILockBytes interface.452*453* The global memory implementation of ILockBytes does not support locking.454*455* See the documentation of ILockBytes for more info.456*/457static HRESULT WINAPI HGLOBALLockBytesImpl_UnlockRegion(458ILockBytes* iface,459ULARGE_INTEGER libOffset, /* [in] */460ULARGE_INTEGER cb, /* [in] */461DWORD dwLockType) /* [in] */462{463return STG_E_INVALIDFUNCTION;464}465466/******************************************************************************467* This method is part of the ILockBytes interface.468*469* This method returns information about the current470* byte array object.471*472* See the documentation of ILockBytes for more info.473*/474static HRESULT WINAPI HGLOBALLockBytesImpl_Stat(475ILockBytes* iface,476STATSTG* pstatstg, /* [out] */477DWORD grfStatFlag) /* [in] */478{479HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);480481memset(pstatstg, 0, sizeof(STATSTG));482483pstatstg->pwcsName = NULL;484pstatstg->type = STGTY_LOCKBYTES;485pstatstg->cbSize = This->byteArraySize;486487return S_OK;488}489490/*491* Virtual function table for the HGLOBALLockBytesImpl class.492*/493static const ILockBytesVtbl HGLOBALLockBytesImpl_Vtbl =494{495HGLOBALLockBytesImpl_QueryInterface,496HGLOBALLockBytesImpl_AddRef,497HGLOBALLockBytesImpl_Release,498HGLOBALLockBytesImpl_ReadAt,499HGLOBALLockBytesImpl_WriteAt,500HGLOBALLockBytesImpl_Flush,501HGLOBALLockBytesImpl_SetSize,502HGLOBALLockBytesImpl_LockRegion,503HGLOBALLockBytesImpl_UnlockRegion,504HGLOBALLockBytesImpl_Stat,505};506507508