From 4f8b5a85c4eb939357afde2e56036a24a045d36e Mon Sep 17 00:00:00 2001 From: Francis Beaudet Date: Sat, 24 Apr 1999 12:00:31 +0000 Subject: [PATCH] Implemented CreateStreamOnHGLOBAL. --- ole/Makefile.in | 1 + ole/hglobalstream.c | 782 ++++++++++++++++++++++++++++++++++++++++++++ ole/ole2stubs.c | 9 - ole/stg_stream.c | 3 +- 4 files changed, 784 insertions(+), 11 deletions(-) create mode 100644 ole/hglobalstream.c diff --git a/ole/Makefile.in b/ole/Makefile.in index ad7ed751d5f..188d588f479 100644 --- a/ole/Makefile.in +++ b/ole/Makefile.in @@ -12,6 +12,7 @@ C_SRCS = \ compositemoniker.c \ filemoniker.c \ guid.c \ + hglobalstream.c \ ifs.c \ itemmoniker.c \ moniker.c \ diff --git a/ole/hglobalstream.c b/ole/hglobalstream.c new file mode 100644 index 00000000000..03fcb3dbd27 --- /dev/null +++ b/ole/hglobalstream.c @@ -0,0 +1,782 @@ +/* + * HGLOBAL Stream implementation + * + * This file contains the implementation of the stream interface + * for streams contained suported by an HGLOBAL pointer. + * + * Copyright 1999 Francis Beaudet + */ +#include +#include +#include +#include + +#include "winbase.h" +#include "winerror.h" +#include "debug.h" + +#include "objbase.h" + +DEFAULT_DEBUG_CHANNEL(ole) + +/**************************************************************************** + * HGLOBALStreamImpl definition. + * + * This class imlements the IStream inteface and represents a stream + * supported by an HGLOBAL pointer. + */ +struct HGLOBALStreamImpl +{ + ICOM_VTABLE(IStream) *lpvtbl; /* Needs to be the first item in the stuct + * since we want to cast this in a 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; + +/* + * Method definition for the StgStreamImpl class. + */ +HGLOBALStreamImpl* HGLOBALStreamImpl_Construct( + HGLOBAL hGlobal, + BOOL fDeleteOnRelease); + +void HGLOBALStreamImpl_Destroy( + HGLOBALStreamImpl* This); + +void HGLOBALStreamImpl_OpenBlockChain( + HGLOBALStreamImpl* This); + +HRESULT WINAPI HGLOBALStreamImpl_QueryInterface( + IStream* iface, + REFIID riid, /* [in] */ + void** ppvObject); /* [iid_is][out] */ + +ULONG WINAPI HGLOBALStreamImpl_AddRef( + IStream* iface); + +ULONG WINAPI HGLOBALStreamImpl_Release( + IStream* iface); + +HRESULT WINAPI HGLOBALStreamImpl_Read( + IStream* iface, + void* pv, /* [length_is][size_is][out] */ + ULONG cb, /* [in] */ + ULONG* pcbRead); /* [out] */ + +HRESULT WINAPI HGLOBALStreamImpl_Write( + IStream* iface, + const void* pv, /* [size_is][in] */ + ULONG cb, /* [in] */ + ULONG* pcbWritten); /* [out] */ + +HRESULT WINAPI HGLOBALStreamImpl_Seek( + IStream* iface, + LARGE_INTEGER dlibMove, /* [in] */ + DWORD dwOrigin, /* [in] */ + ULARGE_INTEGER* plibNewPosition); /* [out] */ + +HRESULT WINAPI HGLOBALStreamImpl_SetSize( + IStream* iface, + ULARGE_INTEGER libNewSize); /* [in] */ + +HRESULT WINAPI HGLOBALStreamImpl_CopyTo( + IStream* iface, + IStream* pstm, /* [unique][in] */ + ULARGE_INTEGER cb, /* [in] */ + ULARGE_INTEGER* pcbRead, /* [out] */ + ULARGE_INTEGER* pcbWritten); /* [out] */ + +HRESULT WINAPI HGLOBALStreamImpl_Commit( + IStream* iface, + DWORD grfCommitFlags); /* [in] */ + +HRESULT WINAPI HGLOBALStreamImpl_Revert( + IStream* iface); + +HRESULT WINAPI HGLOBALStreamImpl_LockRegion( + IStream* iface, + ULARGE_INTEGER libOffset, /* [in] */ + ULARGE_INTEGER cb, /* [in] */ + DWORD dwLockType); /* [in] */ + +HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion( + IStream* iface, + ULARGE_INTEGER libOffset, /* [in] */ + ULARGE_INTEGER cb, /* [in] */ + DWORD dwLockType); /* [in] */ + +HRESULT WINAPI HGLOBALStreamImpl_Stat( + IStream* iface, + STATSTG* pstatstg, /* [out] */ + DWORD grfStatFlag); /* [in] */ + +HRESULT WINAPI HGLOBALStreamImpl_Clone( + IStream* iface, + IStream** ppstm); /* [out] */ + + +/* + * Virtual function table for the HGLOBALStreamImpl class. + */ +static ICOM_VTABLE(IStream) 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 +}; + +/*********************************************************************** + * CreateStreamOnHGlobal [OLE32.61] + */ +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; +} + +/****************************************************************************** +** 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 == NULL) + { + newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, 0); + } + + /* + * Start the stream at the begining. + */ + newStream->currentPosition.HighPart = 0; + newStream->currentPosition.LowPart = 0; + + /* + * Initialize the size of the stream to the size of the handle. + */ + newStream->streamSize.HighPart = 0; + newStream->streamSize.LowPart = GlobalSize(newStream->supportHandle); + } + + return newStream; +} + +/*** + * 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. + */ +void HGLOBALStreamImpl_Destroy(HGLOBALStreamImpl* 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 QueryInterface for this + * class + */ +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 AddRef for this + * class + */ +ULONG WINAPI HGLOBALStreamImpl_AddRef( + IStream* iface) +{ + HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface; + + This->ref++; + + return This->ref; +} + +/*** + * This implements the IUnknown method Release for this + * class + */ +ULONG WINAPI HGLOBALStreamImpl_Release( + IStream* iface) +{ + HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface; + + ULONG newRef; + + This->ref--; + + newRef = 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. + */ +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; + + /* + * 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.LowPart - This->currentPosition.LowPart, cb); + + /* + * Lock the buffer in position and copy the data. + */ + supportBuffer = GlobalLock(This->supportHandle); + + memcpy(pv, supportBuffer+This->currentPosition.LowPart, bytesToReadFromBuffer); + + /* + * Move the current position to the new position + */ + This->currentPosition.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. + */ +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; + + /* + * 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.HighPart = 0; + newSize.LowPart = This->currentPosition.LowPart + cb; + } + + /* + * Verify if we need to grow the stream + */ + if (newSize.LowPart > This->streamSize.LowPart) + { + /* grow stream */ + HGLOBALStreamImpl_SetSize(iface, newSize); + } + + /* + * Lock the buffer in position and copy the data. + */ + supportBuffer = GlobalLock(This->supportHandle); + + memcpy(supportBuffer+This->currentPosition.LowPart, pv, cb); + + /* + * Move the current position to the new position + */ + This->currentPosition.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. + */ +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; + + /* + * The caller is allowed to pass in NULL as the new position return value. + * If it happens, we assign it to a dynamic variable to avoid special cases + * in the code below. + */ + if (plibNewPosition == 0) + { + plibNewPosition = &newPosition; + } + + /* + * The file pointer is moved depending on the given "function" + * parameter. + */ + switch (dwOrigin) + { + case STREAM_SEEK_SET: + plibNewPosition->HighPart = 0; + plibNewPosition->LowPart = 0; + break; + case STREAM_SEEK_CUR: + *plibNewPosition = This->currentPosition; + break; + case STREAM_SEEK_END: + *plibNewPosition = This->streamSize; + break; + default: + return STG_E_INVALIDFUNCTION; + } + + /* + * We don't support files with offsets of 64 bits. + */ + assert(dlibMove.HighPart == 0); + + /* + * Check if we end-up before the beginning of the file. That should trigger an + * error. + */ + if ( (dlibMove.LowPart<0) && (plibNewPosition->LowPart < (ULONG)(-dlibMove.LowPart)) ) + { + /* + * I don't know what error to send there. + */ + return E_FAIL; + } + + /* + * 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. + */ + plibNewPosition->LowPart += dlibMove.LowPart; + This->currentPosition = *plibNewPosition; + + 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. + */ +HRESULT WINAPI HGLOBALStreamImpl_SetSize( + IStream* iface, + ULARGE_INTEGER libNewSize) /* [in] */ +{ + HGLOBALStreamImpl* const This=(HGLOBALStreamImpl*)iface; + + /* + * As documented. + */ + if (libNewSize.HighPart != 0) + return STG_E_INVALIDFUNCTION; + + if (This->streamSize.LowPart == libNewSize.LowPart) + return S_OK; + + /* + * Re allocate the HGlobal to fit the new size of the stream. + */ + This->supportHandle = GlobalReAlloc(This->supportHandle, + libNewSize.LowPart, + 0); + + This->streamSize.LowPart = libNewSize.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. + */ +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; + + /* + * Sanity check + */ + if ( pstm == 0 ) + return STG_E_INVALIDPOINTER; + + totalBytesRead.LowPart = totalBytesRead.HighPart = 0; + totalBytesWritten.LowPart = totalBytesWritten.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.LowPart > 0 ) + { + if ( cb.LowPart >= 128 ) + copySize = 128; + else + copySize = cb.LowPart; + + HGLOBALStreamImpl_Read(iface, tmpBuffer, 128, &bytesRead); + + totalBytesRead.LowPart += bytesRead; + + HGLOBALStreamImpl_Write(pstm, tmpBuffer, bytesRead, &bytesWritten); + + totalBytesWritten.LowPart += bytesWritten; + + /* + * Check that read & write operations were succesfull + */ + if ( (bytesRead != copySize) && (bytesWritten != copySize) ) + { + hr = STG_E_MEDIUMFULL; + break; + } + + cb.LowPart = cb.LowPart - copySize; + } + + /* + * Update number of bytes read and written + */ + if (pcbRead) + { + pcbRead->LowPart = totalBytesRead.LowPart; + pcbRead->HighPart = totalBytesRead.HighPart; + } + + if (pcbWritten) + { + pcbWritten->LowPart = totalBytesWritten.LowPart; + pcbWritten->HighPart = totalBytesWritten.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. + */ +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. + */ +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. + */ +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. + */ +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. + */ +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; +} + +HRESULT WINAPI HGLOBALStreamImpl_Clone( + IStream* iface, + IStream** ppstm) /* [out] */ +{ + FIXME(ole, "not implemented!\n"); + return E_NOTIMPL; +} diff --git a/ole/ole2stubs.c b/ole/ole2stubs.c index b1ef486cf37..a2cc72a8709 100644 --- a/ole/ole2stubs.c +++ b/ole/ole2stubs.c @@ -273,15 +273,6 @@ HRESULT WINAPI OleRegEnumVerbs (REFCLSID clsid, LPENUMOLEVERB* ppenum) return S_OK; } -/*********************************************************************** - * CreateStreamOnHGlobal [OLE32.61] - */ -HRESULT WINAPI CreateStreamOnHGlobal (HGLOBAL hGlobal, BOOL fDeleteOnRelease, LPSTREAM* ppstm) -{ - FIXME(ole,"(%x,%x,%p), stub!\n", hGlobal, fDeleteOnRelease, ppstm); - return S_OK; -} - /*********************************************************************** * OleSave [OLE32.124] */ diff --git a/ole/stg_stream.c b/ole/stg_stream.c index 906c8bbdd76..7eba7907ac0 100644 --- a/ole/stg_stream.c +++ b/ole/stg_stream.c @@ -169,7 +169,7 @@ HRESULT WINAPI StgStreamImpl_QueryInterface( { *ppvObject = (IStream*)This; } - else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStream)) == 0) + else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0) { *ppvObject = (IStream*)This; } @@ -644,7 +644,6 @@ HRESULT WINAPI StgStreamImpl_CopyTo( ULARGE_INTEGER* pcbRead, /* [out] */ ULARGE_INTEGER* pcbWritten) /* [out] */ { - StgStreamImpl* const This=(StgStreamImpl*)iface; HRESULT hr = S_OK; BYTE tmpBuffer[128]; ULONG bytesRead, bytesWritten, copySize;