Implemented CreateStreamOnHGLOBAL.
diff --git a/ole/hglobalstream.c b/ole/hglobalstream.c
new file mode 100644
index 0000000..03fcb3d
--- /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 <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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;
+}