/*++ Microsoft Windows Copyright (c) 1994 Microsoft Corporation. All rights reserved. Module Name: stream.cxx Abstract: Implements the IStream interface on a memory buffer. Author: ShannonC 09-Mar-1994 Environment: Windows NT and Windows 95. We do not support DOS and Win16. Revision History: 12-Oct-94 ShannonC Reformat for code review. --*/ #include #include class CNdrStream : public IStream { public: virtual HRESULT STDMETHODCALLTYPE QueryInterface( IN REFIID riid, OUT void **ppvObj); virtual ULONG STDMETHODCALLTYPE AddRef(); virtual ULONG STDMETHODCALLTYPE Release(); virtual HRESULT STDMETHODCALLTYPE Read( IN void * pv, IN ULONG cb, OUT ULONG * pcbRead); virtual HRESULT STDMETHODCALLTYPE Write( IN void const *pv, IN ULONG cb, OUT ULONG * pcbWritten); virtual HRESULT STDMETHODCALLTYPE Seek( IN LARGE_INTEGER dlibMove, IN DWORD dwOrigin, OUT ULARGE_INTEGER *plibNewPosition); virtual HRESULT STDMETHODCALLTYPE SetSize( IN ULARGE_INTEGER libNewSize); virtual HRESULT STDMETHODCALLTYPE CopyTo( IN IStream * pstm, IN ULARGE_INTEGER cb, OUT ULARGE_INTEGER *pcbRead, OUT ULARGE_INTEGER *pcbWritten); virtual HRESULT STDMETHODCALLTYPE Commit( IN DWORD grfCommitFlags); virtual HRESULT STDMETHODCALLTYPE Revert(); virtual HRESULT STDMETHODCALLTYPE LockRegion( IN ULARGE_INTEGER libOffset, IN ULARGE_INTEGER cb, IN DWORD dwLockType); virtual HRESULT STDMETHODCALLTYPE UnlockRegion( IN ULARGE_INTEGER libOffset, IN ULARGE_INTEGER cb, IN DWORD dwLockType); virtual HRESULT STDMETHODCALLTYPE Stat( OUT STATSTG * pstatstg, IN DWORD grfStatFlag); virtual HRESULT STDMETHODCALLTYPE Clone( OUT IStream **ppstm); CNdrStream( IN unsigned char * pData, IN unsigned long cbMax); private: long RefCount; unsigned char * pBuffer; unsigned long cbBufferLength; unsigned long position; }; EXTERN_C IStream *STDAPICALLTYPE NdrpCreateStreamOnMemory( IN unsigned char * pData, IN unsigned long cbSize) /*++ Routine Description: This function creates a stream on the specified memory buffer. Arguments: pData - Supplies pointer to memory buffer. cbSize - Supplies size of memory buffer. Return Value: This function returns a pointer to the newly created stream. --*/ { CNdrStream *pStream = new CNdrStream(pData, cbSize); return (IStream *)pStream; } CNdrStream::CNdrStream( IN unsigned char * pData, IN unsigned long cbMax) : pBuffer(pData), cbBufferLength(cbMax) /*++ Routine Description: This function creates a stream on the specified memory buffer. Arguments: pData - Supplies pointer to memory buffer. cbMax - Supplies size of memory buffer. Return Value: None. --*/ { RefCount = 1; position = 0; } ULONG STDMETHODCALLTYPE CNdrStream::AddRef() /*++ Routine Description: Increment the reference count. Arguments: Return Value: Reference count. --*/ { InterlockedIncrement(&RefCount); return (ULONG) RefCount; } HRESULT STDMETHODCALLTYPE CNdrStream::Clone( OUT IStream **ppstm) /*++ Routine Description: Create a new IStream object. The new IStream gets an independent seek pointer but it shares the underlying data buffer with the original IStream object. Arguments: ppstm - Pointer to the new stream. Return Value: S_OK - The stream was successfully copied. E_OUTOFMEMORY - The stream could not be copied due to lack of memory. --*/ { HRESULT hr; CNdrStream *pStream = new CNdrStream(pBuffer, cbBufferLength); if(pStream != 0) { pStream->position = position; hr = S_OK; } else { hr = E_OUTOFMEMORY; } *ppstm = (IStream *) pStream; return hr; } HRESULT STDMETHODCALLTYPE CNdrStream::Commit( IN DWORD grfCommitFlags) /*++ Routine Description: This stream does not support transacted mode. This function does nothing. Arguments: grfCommitFlags Return Value: S_OK --*/ { return S_OK; } HRESULT STDMETHODCALLTYPE CNdrStream::CopyTo( IN IStream * pstm, IN ULARGE_INTEGER cb, OUT ULARGE_INTEGER *pcbRead, OUT ULARGE_INTEGER *pcbWritten) /*++ Routine Description: Copies data from one stream to another stream. Arguments: pstm - Specifies the destination stream. cb - Specifies the number of bytes to be copied to the destination stream. pcbRead - Returns the number of bytes read from the source stream. pcbWritten - Returns the number of bytes written to the destination stream. Return Value: S_OK - The data was successfully copied. Other errors from IStream::Write. --*/ { HRESULT hr; unsigned char * pSource; unsigned long cbRead; unsigned long cbWritten; unsigned long cbRemaining; //Check if we are going off the end of the buffer. if(position < cbBufferLength) cbRemaining = cbBufferLength - position; else cbRemaining = 0; if((cb.HighPart == 0) && (cb.LowPart <= cbRemaining)) cbRead = cb.LowPart; else cbRead = cbRemaining; pSource = pBuffer + position; //copy the data hr = pstm->Write(pSource, cbRead, &cbWritten); //advance the current position position += cbRead; if (pcbRead != 0) { pcbRead->LowPart = cbRead; pcbRead->HighPart = 0; } if (pcbWritten != 0) { pcbWritten->LowPart = cbWritten; pcbWritten->HighPart = 0; } return hr; } HRESULT STDMETHODCALLTYPE CNdrStream::LockRegion( IN ULARGE_INTEGER libOffset, IN ULARGE_INTEGER cb, IN DWORD dwLockType) /*++ Routine Description: Range locking is not supported by this stream. Return Value: STG_E_INVALIDFUNCTION. --*/ { return STG_E_INVALIDFUNCTION; } HRESULT STDMETHODCALLTYPE CNdrStream::QueryInterface( REFIID riid, void **ppvObj) /*++ Routine Description: Query for an interface on the stream. The stream supports the IUnknown and IStream interfaces. Arguments: riid - Supplies the IID of the interface being requested. ppvObject - Returns a pointer to the requested interface. Return Value: S_OK E_NOINTERFACE --*/ { HRESULT hr; if ((memcmp(&riid, &IID_IUnknown, sizeof(IID)) == 0) || (memcmp(&riid, &IID_IStream, sizeof(IID)) == 0)) { this->AddRef(); *ppvObj = (IStream *) this; hr = S_OK; } else { *ppvObj = 0; hr = E_NOINTERFACE; } return hr; } HRESULT STDMETHODCALLTYPE CNdrStream::Read( OUT void * pv, IN ULONG cb, OUT ULONG *pcbRead) /*++ Routine Description: Reads data from the stream starting at the current seek pointer. Arguments: pv - Returns the data read from the stream. cb - Supplies the number of bytes to read from the stream. pcbRead - Returns the number of bytes actually read from the stream. Return Value: S_OK - The data was successfully read from the stream. S_FALSE - The number of bytes read was smaller than the number requested. --*/ { HRESULT hr; unsigned long cbRead; unsigned long cbRemaining; //Check if we are reading past the end of the buffer. if(position < cbBufferLength) cbRemaining = cbBufferLength - position; else cbRemaining = 0; if(cb <= cbRemaining) { cbRead = cb; hr = S_OK; } else { cbRead = cbRemaining; hr = S_FALSE; } //copy the data RpcpMemoryCopy(pv, pBuffer + position, cbRead); //advance the current position position += cbRead; if(pcbRead != 0) *pcbRead = cbRead; return hr; } ULONG STDMETHODCALLTYPE CNdrStream::Release() /*++ Routine Description: Decrement the reference count. When the reference count reaches zero, the stream is deleted. Arguments: Return Value: Reference count. --*/ { unsigned long count; count = RefCount - 1; if(InterlockedDecrement(&RefCount) == 0) { count = 0; delete this; } return count; } HRESULT STDMETHODCALLTYPE CNdrStream::Revert() /*++ Routine Description: This stream does not support transacted mode. This function does nothing. Arguments: None. Return Value: S_OK. --*/ { return S_OK; } HRESULT STDMETHODCALLTYPE CNdrStream::Seek( IN LARGE_INTEGER dlibMove, IN DWORD dwOrigin, OUT ULARGE_INTEGER *plibNewPosition) /*++ Routine Description: Sets the position of the seek pointer. It is an error to seek before the beginning of the stream or past the end of the stream. Arguments: dlibMove - Supplies the offset from the position specified in dwOrigin. dwOrigin - Supplies the seek mode. plibNewPosition - Returns the new position of the seek pointer. Return Value: S_OK - The seek pointer was successfully adjusted. STG_E_INVALIDFUNCTION - dwOrigin contains invalid value. STG_E_SEEKERROR - The seek pointer cannot be positioned before the beginning of the stream or past the end of the stream. --*/ { HRESULT hr; long high; long low; unsigned long offset; unsigned long cbRemaining; switch (dwOrigin) { case STREAM_SEEK_SET: //Set the seek position relative to the beginning of the stream. if((dlibMove.HighPart == 0) && (dlibMove.LowPart <= cbBufferLength)) { position = dlibMove.LowPart; hr = S_OK; } else { //It is an error to seek past the end of the stream. hr = STG_E_SEEKERROR; } break; case STREAM_SEEK_CUR: //Set the seek position relative to the current position of the stream. high = (long) dlibMove.HighPart; if(high < 0) { //Negative offset low = (long) dlibMove.LowPart; offset = -low; if((high == -1) && (offset <= position)) { position -= offset; hr = S_OK; } else { //It is an error to seek before the beginning of the stream. hr = STG_E_SEEKERROR; } } else { //Positive offset if(position < cbBufferLength) cbRemaining = cbBufferLength - position; else cbRemaining = 0; if((dlibMove.HighPart == 0) && (dlibMove.LowPart <= cbRemaining)) { position += dlibMove.LowPart; hr = S_OK; } else { //It is an error to seek past the end of the stream. hr = STG_E_SEEKERROR; } } break; case STREAM_SEEK_END: //Set the seek position relative to the end of the stream. high = (long) dlibMove.HighPart; if(high < 0) { //Negative offset low = (long) dlibMove.LowPart; offset = -low; if((high == -1) && (offset <= cbBufferLength)) { position = cbBufferLength - offset; hr = S_OK; } else { //It is an error to seek before the beginning of the stream. hr = STG_E_SEEKERROR; } } else if(dlibMove.QuadPart == 0) { position = cbBufferLength; hr = S_OK; } else { //Positive offset //It is an error to seek past the end of the stream. hr = STG_E_SEEKERROR; } break; default: //dwOrigin contains an invalid value. hr = STG_E_INVALIDFUNCTION; } if (plibNewPosition != 0) { plibNewPosition->LowPart = position; plibNewPosition->HighPart = 0; } return hr; } HRESULT STDMETHODCALLTYPE CNdrStream::SetSize( IN ULARGE_INTEGER libNewSize) /*++ Routine Description: Changes the size of the stream. Arguments: libNewSize - Supplies the new size of the stream. Return Value: S_OK - The stream size was successfully changed. STG_E_MEDIUMFULL - The stream size could not be changed. --*/ { HRESULT hr; if((libNewSize.HighPart == 0) && (libNewSize.LowPart <= cbBufferLength)) { cbBufferLength = libNewSize.LowPart; hr = S_OK; } else { hr = STG_E_MEDIUMFULL; } return hr; } HRESULT STDMETHODCALLTYPE CNdrStream::Stat( OUT STATSTG * pstatstg, IN DWORD grfStatFlag) /*++ Routine Description: This function gets information about this stream. Arguments: pstatstg - Returns information about this stream. grfStatFlg - Specifies the information to be returned in pstatstg. Return Value: S_OK. --*/ { memset(pstatstg, 0, sizeof(STATSTG)); pstatstg->type = STGTY_STREAM; pstatstg->cbSize.LowPart = cbBufferLength; pstatstg->cbSize.HighPart = 0; return S_OK; } HRESULT STDMETHODCALLTYPE CNdrStream::UnlockRegion( IN ULARGE_INTEGER libOffset, IN ULARGE_INTEGER cb, IN DWORD dwLockType) /*++ Routine Description: Range locking is not supported by this stream. Return Value: STG_E_INVALIDFUNCTION. --*/ { return STG_E_INVALIDFUNCTION; } HRESULT STDMETHODCALLTYPE CNdrStream::Write( IN void const *pv, IN ULONG cb, OUT ULONG * pcbWritten) /*++ Routine Description: Write data to the stream starting at the current seek pointer. Arguments: pv - Supplies the data to be written to the stream. cb - Specifies the number of bytes to be written to the stream. pcbWritten - Returns the number of bytes actually written to the stream. Return Value: S_OK - The data was successfully written to the stream. STG_E_MEDIUMFULL - Data cannot be written past the end of the stream. --*/ { HRESULT hr; unsigned long cbRemaining; unsigned long cbWritten; //Check if we are writing past the end of the buffer. if(position < cbBufferLength) cbRemaining = cbBufferLength - position; else cbRemaining = 0; if(cb <= cbRemaining) { cbWritten = cb; hr = S_OK; } else { cbWritten = cbRemaining; hr = STG_E_MEDIUMFULL; } // Write the data. RpcpMemoryCopy(pBuffer + position, pv, cbWritten); //Advance the current position position += cbWritten; //update pcbWritten if (pcbWritten != 0) *pcbWritten = cbWritten; return hr; }