//+============================================================================ // // File: SSMapStm.cxx // // Purpose: This file defines the CSSMappedStream class. // This class provdes a IMappedStream implementation // which maps an IStream from a Compound File. // // History: // // 5/6/98 MikeHill // - Use CoTaskMem rather than new/delete. // //+============================================================================ // -------- // Includes // -------- #include #include "SSMapStm.hxx" #include // IID_IMappedStream #ifdef _MAC_NODOC ASSERTDATA // File-specific data for FnAssert #endif //+---------------------------------------------------------------------------- // // Method: CSSMappedStream::Initialize // // Synopsis: Zero-out all of the member data. // // Arguments: None // // Returns: None // // //+---------------------------------------------------------------------------- VOID CSSMappedStream::Initialize() { propDbg(( DEB_ITRACE, "CSSMappedStream::Initialize\n" )); _pstm = NULL; _pbMappedStream = NULL; _cbMappedStream = 0; _cbActualStreamSize = 0; _powner = NULL; _fLowMem = FALSE; _fDirty = FALSE; #if DBGPROP _fChangePending = FALSE; #endif } // CSSMappedStream::Initialize() //+---------------------------------------------------------------------------- // // Member: Constructor/Destructor // // Synopsis: Initialize/cleanup this object. // //+---------------------------------------------------------------------------- CSSMappedStream::CSSMappedStream( IStream *pstm ) { DfpAssert( NULL != pstm ); // Initialize the member data. Initialize(); // Keep a copy of the Stream that we're mapping. _pstm = pstm; _pstm->AddRef(); _cRefs = 1; } CSSMappedStream::~CSSMappedStream( ) { // Just to be safe, free the mapping buffer (it should have // already been freed). DfpAssert( NULL == _pbMappedStream ); CoTaskMemFree( _pbMappedStream ); // If we've got the global reserved buffer locked, // free it now. if (_fLowMem) { g_ReservedMemory.UnlockMemory(); } // Free the stream which we were mapping. if( NULL != _pstm ) _pstm->Release(); } //+------------------------------------------------------------------- // // Member: CSSMappedStream::QueryInterface, AddRef, Release // // Synopsis: IUnknown members // //-------------------------------------------------------------------- HRESULT CSSMappedStream::QueryInterface( REFIID riid, void **ppvObject) { HRESULT hr = S_OK; // Validate the inputs VDATEREADPTRIN( &riid, IID ); VDATEPTROUT( ppvObject, void* ); // ----------------- // Perform the Query // ----------------- *ppvObject = NULL; if (IsEqualIID(riid,IID_IMappedStream) || IsEqualIID(riid,IID_IUnknown)) { *ppvObject = (IMappedStream *)this; CSSMappedStream::AddRef(); } else { hr = E_NOINTERFACE; } return(hr); } ULONG CSSMappedStream::AddRef(void) { InterlockedIncrement(&_cRefs); return(_cRefs); } ULONG CSSMappedStream::Release(void) { LONG lRet; lRet = InterlockedDecrement(&_cRefs); if (lRet == 0) { delete this; } else if (lRet <0) { lRet = 0; } return(lRet); } //+---------------------------------------------------------------------------- // // Method: CSSMappedStream::Open // // Synopsis: Open up the Stream which we're mapping, and // read it's data into a buffer. // // Arguments: [VOID*] powner // The owner of this Stream. We use this for the // PrOnMappedStreamEvent call. // [HRESULT*] phr // The return code. // // Returns: Nothing. // //+---------------------------------------------------------------------------- VOID CSSMappedStream::Open( IN VOID *powner, OUT HRESULT *phr ) { HRESULT &hr = *phr; VOID *pv = NULL; DfpAssert(!_fLowMem); hr = S_OK; propITrace( "CSSMappedStream::Open" ); // If given a pointer to the owner of this mapped stream, // save it. This could be NULL (i.e., when called from // ReOpen). if( NULL != powner ) _powner = powner; // If we haven't already read the stream, read it now. if( NULL == _pbMappedStream ) { STATSTG statstg; LARGE_INTEGER liSeek; DfpAssert( NULL != _pstm ); DfpAssert( 0 == _cbMappedStream ); DfpAssert( 0 == _cbActualStreamSize ); // Get and validate the size of the Stream. *phr = _pstm->Stat( &statstg, STATFLAG_NONAME ); if( FAILED(*phr) ) goto Exit; if( statstg.cbSize.HighPart != 0 || statstg.cbSize.LowPart > CBMAXPROPSETSTREAM ) { *phr = STG_E_INVALIDHEADER; goto Exit; } _cbMappedStream = _cbActualStreamSize = statstg.cbSize.LowPart; // Allocate a buffer to hold the Stream. If there isn't sufficient // memory in the system, lock and get the reserved buffer. In the // end, 'pv' points to the appropriate buffer. pv = CoTaskMemAlloc( _cbActualStreamSize ); if (pv == NULL) { pv = g_ReservedMemory.LockMemory(); // could wait until previous // property call completes _fLowMem = TRUE; } _pbMappedStream = (BYTE*) pv; // Seek to the start of the Stream. liSeek.HighPart = 0; liSeek.LowPart = 0; *phr = _pstm->Seek( liSeek, STREAM_SEEK_SET, NULL ); if( FAILED(*phr) ) goto Exit; // Read in the Stream. But only if it is non-zero; some // stream implementations (namely the Mac StreamOnHGlobal imp) // don't allow 0-length reads. if( 0 != _cbActualStreamSize ) { *phr = _pstm->Read( _pbMappedStream, _cbActualStreamSize, &_cbMappedStream); if( FAILED(*phr) ) goto Exit; // Ensure that we got all the bytes we requested. if( _cbMappedStream != _cbActualStreamSize ) { propDbg((DEBTRACE_ERROR, "CSSMappedStream(%08X)::Open bytes-read (%lu) doesn't match bytes-requested (%lu)\n", this, _cbMappedStream, _cbActualStreamSize )); *phr = STG_E_INVALIDHEADER; goto Exit; } } #if BIGENDIAN==1 // Notify our owner that we've read in new data. if( _powner != NULL && 0 != _cbMappedStream ) { *phr = PrOnMappedStreamEvent( _powner, _pbMappedStream, _cbMappedStream ); if( FAILED(*phr) ) goto Exit; } #endif } // if( NULL == _pbMappedStream ) // ---- // Exit // ---- Exit: // If there was an error, free any memory we have. if( FAILED(*phr) ) { propDbg((DEB_ERROR, "CSSMappedStream(%08X):Open exception returns %08X\n", this, *phr)); if (_fLowMem) g_ReservedMemory.UnlockMemory(); else CoTaskMemFree( pv ); _pbMappedStream = NULL; _cbMappedStream = 0; _cbActualStreamSize = 0; _fLowMem = FALSE; } return; } // CSSMappedStream::Open //+------------------------------------------------------------------- // // Member: CSSMappedStream::Flush // // Synopsis: Write out the mapping buffer to the Stream, // and Commit it. // // Arguments: [LONG*] phr // An HRESULT return code. // // Returns: None. // //-------------------------------------------------------------------- VOID CSSMappedStream::Flush(OUT LONG *phr) { HRESULT &hr = *phr; propITrace( "CSSMappedStream::Flush" ); // Write out any data we have cached to the Stream. hr = Write(); // Commit the Stream. if( SUCCEEDED(hr) ) { hr = _pstm->Commit(STGC_DEFAULT); } return; } //+------------------------------------------------------------------- // // Member: CSSMappedStream::Close // // Synopsis: Close the mapped stream by writing out // the mapping buffer and then freeing it. // // Arguments: [LONG*] phr // An HRESULT error code. // // Returns: None. // //-------------------------------------------------------------------- VOID CSSMappedStream::Close(OUT LONG *phr) { // Write the changes. We don't need to Commit them, // they will be implicitely committed when the // Stream is Released. HRESULT &hr = *phr; propITrace( "CSSMappedStream::Close" ); hr = Write(); // Even if we fail the write, we must free the memory. // (PrClosePropertySet deletes everything whether or not // there was an error here, so we must free the memory. // There's no danger of this happenning due to out-of- // disk-space conditions, because the propset code // pre-allocates). CoTaskMemFree( _pbMappedStream ); _pstm->Release(); // Re-zero the member data. Initialize(); return; } //+------------------------------------------------------------------- // // Member: CSSMappedStream::ReOpen // // Synopsis: Gets the caller a pointer to the already-opened // mapping buffer. If it isn't already opened, then // it is opened here. // // Arguments: [VOID**] ppv // Used to return the mapping buffer. // [LONG*] phr // Used to return an HRESULT. // // Returns: None. // //-------------------------------------------------------------------- VOID CSSMappedStream::ReOpen(IN OUT VOID **ppv, OUT LONG *phr) { *ppv = NULL; Open(NULL, // Unspecified owner. phr); if( SUCCEEDED(*phr) ) *ppv = _pbMappedStream; return; } //+------------------------------------------------------------------- // // Member: CSSMappedStream::Quiesce // // Synopsis: Unnecessary for this IMappedStream implementation. // //-------------------------------------------------------------------- VOID CSSMappedStream::Quiesce(VOID) { DfpAssert(_pbMappedStream != NULL); } //+------------------------------------------------------------------- // // Member: CSSMappedStream::Map // // Synopsis: Used to get a pointer to the current mapping. // // Arguments: [BOOLEAN] fCreate // Not used by this IMappedStream implementation. // [VOID**] ppv // Used to return the mapping buffer. // // Returns: None. // //-------------------------------------------------------------------- VOID CSSMappedStream::Map(BOOLEAN fCreate, VOID **ppv) { DfpAssert(_pbMappedStream != NULL); *ppv = _pbMappedStream; } //+------------------------------------------------------------------- // // Member: CSSMappedStream::Unmap // // Synopsis: Unnecessary for this IMappedStream implementation. // //-------------------------------------------------------------------- VOID CSSMappedStream::Unmap(BOOLEAN fFlush, VOID **ppv) { *ppv = NULL; } //+------------------------------------------------------------------- // // Member: CSSMappedStream::Write // // Synopsis: Writes the mapping buffer out to the original // Stream. // // Arguments: None. // // Returns: [HRESULT] // S_FALSE => Nothing needed to be written // //-------------------------------------------------------------------- #define STACK_BYTES 16 HRESULT CSSMappedStream::Write () { HRESULT hr; ULONG cbWritten; LARGE_INTEGER liSeek; BOOL fOwnerSignaled = FALSE; propITrace( "CSSMappedStream::Write" ); // We can return right away if there's nothing to write. // (_pbMappedStream may be NULL in the error path of our // caller). if (!_fDirty || NULL == _pbMappedStream ) { propDbg((DEB_PROP_INFO, "CPubStream(%08X):Flush returns with not-dirty\n", this)); return S_FALSE; } DfpAssert( _pstm != NULL ); #if BIGENDIAN==1 // Notify our owner that we're about to perform a Write. // Note that there are no goto Exit calls prior to this point, because // we making a corresponding call to PrOnMappedStreamEvent (for byte-swapping) // in the Exit. hr = PrOnMappedStreamEvent( _powner, _pbMappedStream, _cbMappedStream ); if( S_OK != hr ) goto Exit; fOwnerSignaled = TRUE; #endif // Seek to the start of the Stream. liSeek.HighPart = 0; liSeek.LowPart = 0; hr = _pstm->Seek( liSeek, STREAM_SEEK_SET, NULL ); if( FAILED(hr) ) goto Exit; // Write out the mapping buffer. hr = _pstm->Write(_pbMappedStream, _cbMappedStream, &cbWritten); if( S_OK != hr ) goto Exit; if( cbWritten != _cbMappedStream ) { propDbg((DEB_ERROR, "CSSMappedStream(%08X)::Write bytes-written (%lu) doesn't match bytes-requested (%lu)\n", this, cbWritten, _cbMappedStream )); hr = STG_E_INVALIDHEADER; goto Exit; } // If the buffer is shrinking, this is a good time to shrink the Stream. if (_cbMappedStream < _cbActualStreamSize) { ULARGE_INTEGER uli; uli.HighPart = 0; uli.LowPart = _cbMappedStream; hr = _pstm->SetSize(uli); if( S_OK == hr ) { _cbActualStreamSize = _cbMappedStream; } } // // If we changed the buffer size and it is less than the // actual underlying stream, then we need to zero out the memory // above the currrent size. // if (_cbMappedStream < _cbActualStreamSize) { PBYTE pTemp; LARGE_INTEGER li; DWORD cbWrite = _cbActualStreamSize - _cbMappedStream; li.HighPart = 0; li.LowPart = _cbMappedStream; hr = _pstm->Seek(li,STREAM_SEEK_SET,NULL); if (SUCCEEDED(hr)) { pTemp = reinterpret_cast( CoTaskMemAlloc( cbWrite )); if (pTemp != NULL) { memset(pTemp,0,cbWrite); // // Successfully allocated memory for the write. Write the // zeros out all at once. // hr = _pstm->Write(pTemp, cbWrite, NULL); if (FAILED(hr)) { propDbg((DEB_ERROR, "CSSMappedStream::Write " "write failure\n",hr)); goto Exit; } CoTaskMemFree( pTemp ); } else { // // We couldn't allocate memory. So we will use a small // stack buffer instead. // BYTE stackBuf[STACK_BYTES]; memset(stackBuf, 0, STACK_BYTES); while (cbWrite >= STACK_BYTES) { hr = _pstm->Write(stackBuf, STACK_BYTES, NULL); if (FAILED(hr)) { propDbg((DEB_ERROR, "CSSMappedStream::Write write failure\n",hr)); goto Exit; } cbWrite -= STACK_BYTES; } if (cbWrite < STACK_BYTES) { hr = _pstm->Write(stackBuf, cbWrite, NULL); if (FAILED(hr)) { propDbg((DEB_ERROR, "CSSMappedStream::Write write failure\n",hr)); goto Exit; } } } } else { propDbg((DEB_ERROR, "CSSMappedStream::Write seek failure\n",hr)); goto Exit; } } // ---- // Exit // ---- Exit: // Notify our owner that we're done with the Write. We do this // whether or not there was an error, because _pbMappedStream is // not modified, and therefore intact even in the error path. // This call allows the owner to correct the byte-order of the header. #if BIGENDIAN==1 if( fOwnerSignaled ) DfpVerify( PrOnMappedStreamEvent( _powner, _pbMappedStream, _cbMappedStream )); #endif if (hr == S_OK || hr == STG_E_REVERTED) { _fDirty = FALSE; } return hr; } //+------------------------------------------------------------------- // // Member: CSSMappedStream::GetSize // // Synopsis: Returns the current size of the mapped stream. // // Arguments: [LONG*] phr // Used to return an HRESULT. // // Returns: [ULONG] // The current size. // //-------------------------------------------------------------------- ULONG CSSMappedStream::GetSize(OUT LONG *phr) { HRESULT &hr = *phr; hr = S_OK; propITrace( "CSSMappedStream::GetSize" ); // If necessary, open the Stream. if( NULL == _pbMappedStream ) { Open(NULL, // Unspecified owner phr); } if( SUCCEEDED(*phr) ) { DfpAssert( NULL != _pbMappedStream ); } // Return the size of the mapped stream. If there was an // Open error, it will be zero, and *phr will be set. propDbg(( DEB_ITRACE, "CSSMappedStream::GetSize, size is %d\n", _cbMappedStream )); return _cbMappedStream; } //+------------------------------------------------------------------- // // Member: CSSMappedStream::SetSize // // Synopsis: Set the size of the mapped stream. // // Arguments: [ULONG] cb // The new size. // [BOOLEAN] fPersistent // If not set, then this change will not be stored - // thus the mapping buffer must be set, but the // Stream itself must not. This was added so that // CPropertySetStream could grow the buffer for internal // processing, when the Stream itself is read-only. // [VOID**] ppv // Used to return the new mapping buffer location. // // Returns: None. // // Pre-Conditions: // cb is below the maximum property set size. // //-------------------------------------------------------------------- VOID CSSMappedStream::SetSize(ULONG cb, IN BOOLEAN fPersistent, VOID **ppv, OUT LONG *phr) { BYTE *pv; HRESULT &hr = *phr; hr = S_OK; DfpAssert(cb != 0); DfpAssert(cb <= CBMAXPROPSETSTREAM); propITrace( "CSSMappedStream::SetSize" ); propTraceParameters(( "cb=%lu, fPersistent=%s, ppv=%p", cb, fPersistent?"True":"False" )); // // if we are growing the data, we should grow the stream // if (fPersistent && cb > _cbActualStreamSize) { ULARGE_INTEGER uli; uli.HighPart = 0; uli.LowPart = cb; //---------------- // Need to Grow! //---------------- propDbg(( DEB_ITRACE, "Growing from %d to %d\n", _cbActualStreamSize, cb )); *phr = _pstm->SetSize( uli ); if (FAILED(*phr) ) goto Exit; else _cbActualStreamSize = cb; } // // We only get here if we either (1) didn't want to grow the // underlying stream, or (2) we successfully grew the underlying stream. // // // Re-size the buffer to the size specified in cb. // if ( _fLowMem ) { // If we want to grow the buffer In low-memory conditions, // no realloc is necessary, because // _pbMappedStream is already large enough for the largest // property set. *ppv = _pbMappedStream; } else if ( cb != _cbMappedStream ) { // We must re-alloc the buffer. pv = reinterpret_cast( CoTaskMemAlloc( cb )); if ((pv == NULL) ) { // allocation failed: we need to try using a backup mechanism for // more memory. // copy the data to the global reserved chunk... we will wait until // someone else has released it. it will be released on the way out // of the property code. _fLowMem = TRUE; pv = g_ReservedMemory.LockMemory(); if ( NULL == pv) { *phr = E_OUTOFMEMORY; goto Exit; } else if( NULL != _pbMappedStream) { memcpy( pv, _pbMappedStream, min(cb,_cbMappedStream) ); } CoTaskMemFree( _pbMappedStream ); } else { memcpy( pv, _pbMappedStream, min(cb,_cbMappedStream) ); CoTaskMemFree( _pbMappedStream ); } _pbMappedStream = pv; *ppv = pv; } _cbMappedStream = cb; // ---- // Exit // ---- Exit: propDbg((DbgFlag(*phr,DEB_TRACE), "CSSMappedStream(%08X):SetSize %s returns hr=%08X\n", this, *phr != S_OK ? "exception" : "", *phr)); } //+------------------------------------------------------------------- // // Member: CSSMappedStream::Lock // // Synopsis: Locking is not supported by this class. // //-------------------------------------------------------------------- NTSTATUS CSSMappedStream::Lock(BOOLEAN fExclusive) { return(STATUS_SUCCESS); } //+------------------------------------------------------------------- // // Member: CSSMappedStream::Unlock // // Synopsis: Locking is not supported by this class. // However, this method still must check to // see if the reserved memory pool should be // freed for use by another property set. // //-------------------------------------------------------------------- NTSTATUS CSSMappedStream::Unlock(VOID) { // if at the end of the properties set/get call we have the low // memory region locked, we flush to disk. HRESULT hr = S_OK; if (_fLowMem) { Flush(&hr); g_ReservedMemory.UnlockMemory(); _pbMappedStream = NULL; _cbMappedStream = 0; _fLowMem = FALSE; propDbg((DEB_ERROR, "CPubStream(%08X):Unlock low-mem returns NTSTATUS=%08X\n", this, hr)); } return(hr); } //+------------------------------------------------------------------- // // Member: CSSMappedStream::QueryTimeStamps // // Synopsis: Not used by this IMappedStream derivation. // //-------------------------------------------------------------------- VOID CSSMappedStream::QueryTimeStamps(STATPROPSETSTG *pspss, BOOLEAN fNonSimple) const { } //+------------------------------------------------------------------- // // Member: CSSMappedStream::QueryModifyTime // // Synopsis: Not used by this IMappedStream derivation. // // Notes: // //-------------------------------------------------------------------- BOOLEAN CSSMappedStream::QueryModifyTime(OUT LONGLONG *pll) const { return(FALSE); } //+------------------------------------------------------------------- // // Member: Unused methods by this IMappedStream implementation: // QuerySecurity, IsWritable, GetHandle // //-------------------------------------------------------------------- BOOLEAN CSSMappedStream::QuerySecurity(OUT ULONG *pul) const { return(FALSE); } BOOLEAN CSSMappedStream::IsWriteable() const { return TRUE; } HANDLE CSSMappedStream::GetHandle(VOID) const { return(INVALID_HANDLE_VALUE); } //+------------------------------------------------------------------- // // Member: CSSMappedStream::SetModified/IsModified // //-------------------------------------------------------------------- VOID CSSMappedStream::SetModified(OUT LONG *phr) { _fDirty = TRUE; *phr = S_OK; } BOOLEAN CSSMappedStream::IsModified(VOID) const { propDbg(( DEB_ITRACE, "CSSMappedStream::IsModified (%s)\n", _fDirty?"TRUE":"FALSE" )); return (BOOLEAN) _fDirty; } //+------------------------------------------------------------------- // // Member: CSSMappedStream::IsNtMappedStream/SetChangePending // // Synopsis: Debug routines. // //-------------------------------------------------------------------- #if DBGPROP BOOLEAN CSSMappedStream::IsNtMappedStream(VOID) const { return(FALSE); } #endif #if DBGPROP BOOLEAN CSSMappedStream::SetChangePending(BOOLEAN f) { BOOL fOld = _fChangePending; _fChangePending = f; return((BOOLEAN)_fChangePending); } #endif