//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1993. // // File: memstm.cpp // // Contents: Implementations of IStream and ILockBytes on memory // (versus the file system) // // Classes: CMemStm // CMemBytes // CMarshalMemStm // CMarshalMemBytes // // Functions: CreateMemStm // CloneMemStm // ReleaseMemStm // CreateStreamOnHGlobal // GetHGlobalFromStream // CMemStmUnMarshal // CMemBytesUnMarshall // // History: dd-mmm-yy Author Comment // 31-Jan-95 t-ScottH added Dump methods to CMemStm and CMemBytes // (_DEBUG only) // added DumpCMemStm and CMemBytes APIs // 04-Nov-94 ricksa Made CMemStm class multithread safe. // 24-Jan-94 alexgo first pass at converting to Cairo-style // memory allocation // 11-Jan-94 alexgo added VDATEHEAP macros to every function & // method, fixed compile warnings, removed // custom marshalling code. Memory streams // and ILockBytes now use standard // marshalling. // 16-Dec-93 alexgo fixed memory reference bugs (bad pointer) // 02-Dec-93 alexgo 32bit port, implement CMemStm::CopyTo // 11/22/93 - ChrisWe - replace overloaded ==, != with // IsEqualIID and IsEqualCLSID // // Notes: // //-------------------------------------------------------------------------- #include #pragma SEG(memstm) #include #include "memstm.h" #include "sem.hxx" #include #ifdef _DEBUG #include "dbgdump.h" #endif // _DEBUG NAME_SEG(CMemStm) ASSERTDATA // CRefMutexSem implementation // // Instances of this class are shared among all CMemStm objects // cloned from a common CMemStm object, as well as their parent. // // This guarantees synchronization between all instances of CMemStm that share common data CRefMutexSem::CRefMutexSem() : m_lRefs(1) { // Note: we begin life with one reference } CRefMutexSem* CRefMutexSem::CreateInstance() { CRefMutexSem* prefMutexSem = NULL; prefMutexSem = new CRefMutexSem(); if (prefMutexSem != NULL) { if (prefMutexSem->FInit() == FALSE) { ASSERT(FALSE); delete prefMutexSem; prefMutexSem = NULL; } } return prefMutexSem; } BOOL CRefMutexSem::FInit() { return m_mxs.FInit(); } ULONG CRefMutexSem::AddRef() { return InterlockedIncrement (&m_lRefs); } ULONG CRefMutexSem::Release() { LONG lRefs = InterlockedDecrement (&m_lRefs); if (lRefs == 0) { delete this; } return lRefs; } void CRefMutexSem::RequestCS() { m_mxs.Request(); } void CRefMutexSem::ReleaseCS() { m_mxs.Release(); } const CMutexSem2* CRefMutexSem::GetMutexSem() { return &m_mxs; } inline CRefMutexAutoLock::CRefMutexAutoLock (CRefMutexSem* pmxs) { Win4Assert (pmxs != NULL); m_pmxs = pmxs; m_pmxs->RequestCS(); END_CONSTRUCTION (CRefMutexAutoLock); } inline CRefMutexAutoLock::~CRefMutexAutoLock() { m_pmxs->ReleaseCS(); } // Shared memory IStream implementation // //+------------------------------------------------------------------------- // // Member: CMemStm::CMemStm // // Synopsis: constructor for memory stream // // Arguments: none // // History: 20-Dec-94 Rickhi moved from h file // //-------------------------------------------------------------------------- CMemStm::CMemStm() { m_hMem = NULL; m_pData = NULL; m_pos = 0; m_refs = 0; m_pmxs = NULL; } CMemStm::~CMemStm() { if (m_pmxs != NULL) { m_pmxs->Release(); } } //+------------------------------------------------------------------------- // // Member: CMemStm::QueryInterface // // Synopsis: retrieves the requested interface // // Effects: // // Arguments: [iidInterface] -- the requested interface ID // [ppvObj] -- where to put the interface pointer // // Requires: // // Returns: NOERROR, E_OUTOFMEMORY, E_NOINTERFACE // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 04-Nov-94 ricksa Modified for multithreading // 11-Jan-94 alexgo removed QI for IMarshal so that // the standard marshaller is used. // This is fix marshalling across // process on 32bit platforms. // 02-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_QueryInterface) STDMETHODIMP CMemStm::QueryInterface(REFIID iidInterface, void FAR* FAR* ppvObj) { VDATEHEAP(); HRESULT error; VDATEPTROUT( ppvObj, LPVOID ); *ppvObj = NULL; VDATEIID( iidInterface ); // Two interfaces supported: IUnknown, IStream if (m_pData != NULL && (IsEqualIID(iidInterface, IID_IStream) || IsEqualIID(iidInterface, IID_ISequentialStream) || IsEqualIID(iidInterface, IID_IUnknown))) { AddRef(); // A pointer to this object is returned *ppvObj = this; error = NOERROR; } #ifndef WIN32 else if (IsEqualIID(iidInterface, IID_IMarshal)) { *ppvObj = (LPVOID) CMarshalMemStm::Create(this); if (*ppvObj != NULL) { error = NOERROR; } else { error = ResultFromScode(E_OUTOFMEMORY); } } #endif else { // Not accessible or unsupported interface *ppvObj = NULL; error = ResultFromScode(E_NOINTERFACE); } return error; } //+------------------------------------------------------------------------- // // Member: CMemStm::AddRef // // Synopsis: increments the reference count // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 02-Dec-93 alexgo 32bit port // 04-Nov-94 ricksa Modified for multithreading // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_AddRef) STDMETHODIMP_(ULONG) CMemStm::AddRef(void) { VDATEHEAP(); return InterlockedIncrement((LONG *) &m_refs); } //+------------------------------------------------------------------------- // // Member: CMemStm::Release // // Synopsis: decrements the reference count // // Effects: deletes the object when ref count == 0 // // Arguments: void // // Requires: // // Returns: ULONG -- the new ref count // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 04-Nov-94 ricksa Modified for multithreading // 16-Dec-93 alexgo added GlobalUnlock of the MEMSTM handle // 02-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Release) STDMETHODIMP_(ULONG) CMemStm::Release(void) { VDATEHEAP(); // The reason for this here is that there is a race when releasing // this object. If two threads are trying to release this object // at the same time, there is a case where the first one dec's // the ref count & then loses the processor to the second thread. // This second thread decrements the reference count to 0 and frees // the memory. The first thread can no longer safely examine the // internal state of the object. ULONG ulResult = InterlockedDecrement((LONG *) &m_refs); if (ulResult == 0) { // this MEMSTM handle was GlobalLock'ed in ::Create // we unlock it here, as we no longer need it. GlobalUnlock(m_hMem); ReleaseMemStm(&m_hMem); delete this; } return ulResult; } //+------------------------------------------------------------------------- // // Member: CMemStm::Read // // Synopsis: reads [cb] bytes from the stream // // Effects: // // Arguments: [pb] -- where to put the data read // [cb] -- the number of bytes to read // [pcbRead] -- where to put the actual number of bytes // read // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: uses xmemcpy // // History: dd-mmm-yy Author Comment // 04-Nov-94 ricksa Modified for multithreading // 02-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Read) STDMETHODIMP CMemStm::Read(void HUGEP* pb, ULONG cb, ULONG FAR* pcbRead) { VDATEHEAP(); HRESULT error = NOERROR; ULONG cbRead = cb; if(cb) { VDATEPTROUT( pb, char); } // Single thread CRefMutexAutoLock lck(m_pmxs); if (pcbRead) { VDATEPTROUT( pcbRead, ULONG ); *pcbRead = 0L; } // cbRead + m_pos could cause roll-over. if ( ( (cbRead + m_pos) > m_pData->cb) || ( (cbRead + m_pos) < m_pos) ) { // Caller is asking for more bytes than we have left if(m_pData->cb > m_pos) cbRead = m_pData->cb - m_pos; else cbRead = 0; } if (cbRead > 0) { Assert (m_pData->hGlobal); BYTE HUGEP* pGlobal = (BYTE HUGEP *)GlobalLock( m_pData->hGlobal); if (NULL==pGlobal) { LEERROR(1, "GlobalLock Failed!"); return ResultFromScode (STG_E_READFAULT); } // overlap is currently considered a bug (see the discussion // on the Write method _xmemcpy(pb, pGlobal + m_pos, cbRead); GlobalUnlock (m_pData->hGlobal); m_pos += cbRead; } if (pcbRead != NULL) { *pcbRead = cbRead; } return error; } //+------------------------------------------------------------------------- // // Member: CMemStm::Write // // Synopsis: Writes [cb] bytes into the stream // // Effects: // // Arguments: [pb] -- the bytes to write // [cb] -- the number of bytes to write // [pcbWritten] -- where to put the number of bytes written // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: resizes the internal buffer (if needed), then uses xmemcpy // // History: dd-mmm-yy Author Comment // 04-Nov-94 ricksa Modified for multithreading // 02-Dec-93 alexgo 32bit port, fixed bug dealing with // 0-byte sized memory // 06-Dec-93 alexgo handle overlap case. // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Write) STDMETHODIMP CMemStm::Write(void const HUGEP* pb, ULONG cb, ULONG FAR* pcbWritten) { VDATEHEAP(); HRESULT error = NOERROR; ULONG cbWritten = cb; ULARGE_INTEGER ularge_integer; BYTE HUGEP* pGlobal; if(cb) { VDATEPTRIN( pb , char ); } // Single thread CRefMutexAutoLock lck(m_pmxs); if (pcbWritten != NULL) { *pcbWritten = 0; } if (cbWritten + m_pos > m_pData->cb) { ULISet32( ularge_integer, m_pos+cbWritten ); error = SetSize(ularge_integer); if (error != NOERROR) { goto Exit; } } // we don't write anything if 0 bytes are asked for for two // reasons: 1. optimization, 2. m_pData->hGlobal could be a // handle to a zero-byte memory block, in which case GlobalLock // will fail. if( cbWritten > 0 ) { pGlobal = (BYTE HUGEP *)GlobalLock (m_pData->hGlobal); if (NULL==pGlobal) { LEERROR(1, "GlobalLock Failed!"); return ResultFromScode (STG_E_WRITEFAULT); } // we use memmove here instead of memcpy to handle the // overlap case. Recall that the app originally gave // use the memory for the memstm. He could (either through // a CopyTo or through really strange code), be giving us // this region to read from, so we have to handle the overlapp // case. The same argument also applies for Read, but for // now, we'll consider overlap on Read a bug. _xmemmove(pGlobal + m_pos, pb, cbWritten); GlobalUnlock (m_pData->hGlobal); m_pos += cbWritten; } if (pcbWritten != NULL) { *pcbWritten = cbWritten; } Exit: return error; } //+------------------------------------------------------------------------- // // Member: CMemStm::Seek // // Synopsis: Moves the internal seek pointer // // Effects: // // Arguments: [dlibMoveIN] -- the amount to move by // [dwOrigin] -- flags to control whether seeking is // relative to the current postion or // the begging/end. // [plibNewPosition] -- where to put the new position // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 04-Nov-94 ricksa Modified for multithreading // 02-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Seek) STDMETHODIMP CMemStm::Seek(LARGE_INTEGER dlibMoveIN, DWORD dwOrigin, ULARGE_INTEGER FAR* plibNewPosition) { VDATEHEAP(); HRESULT error = NOERROR; LONG dlibMove = dlibMoveIN.LowPart ; ULONG cbNewPos = dlibMove; // Single thread CRefMutexAutoLock lck(m_pmxs); if (plibNewPosition != NULL) { VDATEPTROUT( plibNewPosition, ULONG ); ULISet32(*plibNewPosition, m_pos); } switch(dwOrigin) { case STREAM_SEEK_SET: if (dlibMove >= 0) { m_pos = dlibMove; } else { error = ResultFromScode(STG_E_SEEKERROR); } break; case STREAM_SEEK_CUR: if (!(dlibMove < 0 && ((ULONG) -dlibMove) > m_pos)) { m_pos += dlibMove; } else { error = ResultFromScode(STG_E_SEEKERROR); } break; case STREAM_SEEK_END: if (!(dlibMove < 0 && ((ULONG) -dlibMove) > m_pData->cb)) { m_pos = m_pData->cb + dlibMove; } else { error = ResultFromScode(STG_E_SEEKERROR); } break; default: error = ResultFromScode(STG_E_SEEKERROR); } if (plibNewPosition != NULL) { ULISet32(*plibNewPosition, m_pos); } return error; } //+------------------------------------------------------------------------- // // Member: CMemStm::SetSize // // Synopsis: Sets the size of our memory // // Effects: // // Arguments: [cb] -- the new size // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: calls GlobalRealloc // // History: dd-mmm-yy Author Comment // 04-Nov-94 ricksa Modified for multithreading // 02-Dec-93 alexgo 32bit port, added assert // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_SetSize) STDMETHODIMP CMemStm::SetSize(ULARGE_INTEGER cb) { VDATEHEAP(); HANDLE hMemNew; // Single thread CRefMutexAutoLock lck(m_pmxs); // make sure we aren't in overflow conditions. AssertSz(cb.HighPart == 0, "MemStream::More than 2^32 bytes asked for"); if (m_pData->cb == cb.LowPart) { return NOERROR; } hMemNew = GlobalReAlloc(m_pData->hGlobal, max (cb.LowPart,1), GMEM_SHARE | GMEM_MOVEABLE); if (hMemNew == NULL) { return ResultFromScode (E_OUTOFMEMORY); } m_pData->hGlobal = hMemNew; m_pData->cb = cb.LowPart; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemStm::CopyTo // // Synopsis: Copies data from [this] stream to [pstm] // // Effects: // // Arguments: [pstm] -- the stream to copy to // [cb] -- the number of bytes to copy // [pcbRead] -- where to return the number of bytes read // [pcbWritten] -- where to return the number of bytes written // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: does an IStream->Write to the given stream // // History: dd-mmm-yy Author Comment // 04-Nov-94 ricksa Modified for multithreading // 03-Dec-93 alexgo original implementation // // Notes: This implementation assumes that the address space // is not greater than ULARGE_INTEGER.LowPart (which is // for for 32bit operating systems). 64bit NT may need // to revisit this code. // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_CopyTo) STDMETHODIMP CMemStm::CopyTo(IStream FAR *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER FAR * pcbRead, ULARGE_INTEGER FAR * pcbWritten) { VDATEHEAP(); ULONG cbRead = cb.LowPart; ULONG cbWritten = 0; HRESULT hresult = NOERROR; // pstm cannot be NULL VDATEPTRIN(pstm, LPSTREAM); // Single thread CRefMutexAutoLock lck(m_pmxs); // the spec says that if cb is it's maximum value (all bits set, // since it's unsigned), then we will simply read the copy of // this stream if ( ~(cb.LowPart) == 0 && ~(cb.HighPart) == 0 ) { cbRead = m_pData->cb - m_pos; } else if ( cb.HighPart > 0 ) { // we assume that our memory stream cannot // be large enough to accomodate very large (>32bit) // copy to requests. Since this is probably an error // on the caller's part, we assert. AssertSz(0, "WARNING: CopyTo request exceeds 32 bits"); // set the Read value to what's left, so that "Ignore"ing // the assert works properly. cbRead = m_pData->cb - m_pos; } else if ( cbRead + m_pos > m_pData->cb ) { // more bytes were requested to read than we had left. // cbRead is set to the amount remaining. cbRead = m_pData->cb - m_pos; } // now write the data to the stream if ( cbRead > 0 ) { BYTE HUGEP* pGlobal = (BYTE HUGEP *)GlobalLock( m_pData->hGlobal); if( pGlobal == NULL ) { LEERROR(1, "GlobalLock failed"); return ResultFromScode(STG_E_INSUFFICIENTMEMORY); } hresult = pstm->Write(pGlobal + m_pos, cbRead, &cbWritten); // in the error case, the spec says that the return values // may be meaningless, so we do not need to do any special // error handling here GlobalUnlock(m_pData->hGlobal); } // increment our seek pointer and set the out parameters m_pos += cbRead; if( pcbRead ) { ULISet32(*pcbRead, cbRead); } if( pcbWritten ) { ULISet32(*pcbWritten, cbWritten); } return hresult; } //+------------------------------------------------------------------------- // // Member: CMemStm::Commit // // Synopsis: Does nothing, no transactions available on memory streams // // Effects: // // Arguments: [grfCommitFlags] // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 03-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Commit) STDMETHODIMP CMemStm::Commit(DWORD grfCommitFlags) { VDATEHEAP(); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemStm::Revert // // Synopsis: does nothing, as no transactions are supported on memory // streams // // Effects: // // Arguments: void // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 03-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Revert) STDMETHODIMP CMemStm::Revert(void) { VDATEHEAP(); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemStm::LockRegion // // Synopsis: not supported in OLE2.01 // // Effects: // // Arguments: [libOffset] // [cb] // [dwLockType] // // Requires: // // Returns: STG_E_INVALIDFUNCTION // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 03-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_LockRegion) STDMETHODIMP CMemStm::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { VDATEHEAP(); return ResultFromScode(STG_E_INVALIDFUNCTION); } //+------------------------------------------------------------------------- // // Member: CMemStm::UnlockRegion // // Synopsis: not implemented for OLE2.01 // // Effects: // // Arguments: [libOffset] // [cb] // [dwLockType] // // Requires: // // Returns: STG_E_INVALIDFUNCTION // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 03-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_UnlockRegion) STDMETHODIMP CMemStm::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { VDATEHEAP(); return ResultFromScode(STG_E_INVALIDFUNCTION); } //+------------------------------------------------------------------------- // // Member: CMemStm::Stat // // Synopsis: Returns info about this stream // // Effects: // // Arguments: [pstatstg] -- the STATSTG to fill with info // [statflag] -- status flags, unused // // Requires: // // Returns: NOERROR, E_INVALIDARG // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 03-Dec-93 alexgo 32bit port // 01-Jun-94 AlexT Set type correctly // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Stat) STDMETHODIMP CMemStm::Stat(STATSTG FAR *pstatstg, DWORD statflag) { VDATEHEAP(); VDATEPTROUT( pstatstg, STATSTG ); memset ( pstatstg, 0, sizeof(STATSTG) ); pstatstg->type = STGTY_STREAM; pstatstg->cbSize.LowPart = m_pData->cb; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemStm::Clone // // Synopsis: creates a new instance of this stream pointing to the // same data at the same position (same seek pointer) // // Effects: // // Arguments: [ppstm] -- where to put the new CMemStm pointer // // Requires: // // Returns: NOERROR, E_OUTOFMEMORY // // Signals: // // Modifies: // // Derivation: IStream // // Algorithm: // // History: dd-mmm-yy Author Comment // 03-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Clone) STDMETHODIMP CMemStm::Clone(IStream FAR * FAR *ppstm) { VDATEHEAP(); CMemStm FAR* pCMemStm; VDATEPTROUT (ppstm, LPSTREAM); *ppstm = pCMemStm = CMemStm::Create(m_hMem, m_pmxs); if (pCMemStm == NULL) { return ResultFromScode(E_OUTOFMEMORY); } pCMemStm->m_pos = m_pos; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemStm::Create // // Synopsis: Creates a new CMemStm. [hMem] must be a handle to a MEMSTM // block. // // Effects: // // Arguments: [hMem] -- handle to a MEMSTM block // // Requires: // // Returns: CMemStm * // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 15-Dec-93 alexgo fixed memory access bug // 03-Dec-93 alexgo 32bit port // 20-Sep-2000 mfeingol Added Mutex inheritance // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStm_Create) STDSTATICIMP_(CMemStm FAR*) CMemStm::Create(HANDLE hMem, CRefMutexSem* pmxs) { VDATEHEAP(); CMemStm FAR* pCMemStm = NULL; struct MEMSTM FAR* pData; pData = (MEMSTM FAR*) GlobalLock(hMem); if (pData != NULL) { pCMemStm = new CMemStm; if (pCMemStm != NULL) { // Initialize CMemStm pCMemStm->m_hMem = hMem; InterlockedIncrement ((LPLONG) &(pCMemStm->m_pData = pData)->cRef); // AddRefMemStm pCMemStm->m_refs = 1; pCMemStm->m_dwSig = STREAM_SIG; if (pmxs != NULL) { // Addref the input pmxs->AddRef(); } else { // Create a new mutex (implicit addref) pmxs = CRefMutexSem::CreateInstance(); } if (pmxs != NULL) { // Give the CMemStm a mutex pCMemStm->m_pmxs = pmxs; } else { // uh-oh, low on memory delete pCMemStm; pCMemStm = NULL; GlobalUnlock(hMem); } } else { // uh-oh, low on memory GlobalUnlock(hMem); } } // we do *not* unlock the memory now, the memstm structure should // be locked for the lifetime of any CMemStm's that refer to it. // when the CMemStm is destroyed, we will release our lock on // hMem. return pCMemStm; } //+------------------------------------------------------------------------- // // Member: CMemStm::Dump, public (_DEBUG only) // // Synopsis: return a string containing the contents of the data members // // Effects: // // Arguments: [ppszDump] - an out pointer to a null terminated character array // [ulFlag] - flag determining prefix of all newlines of the // out character array (default is 0 - no prefix) // [nIndentLevel] - will add a indent prefix after the other prefix // for ALL newlines (including those with no prefix) // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: [ppsz] - argument // // Derivation: // // Algorithm: use dbgstream to create a string containing information on the // content of data structures // // History: dd-mmm-yy Author Comment // 20-Jan-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG HRESULT CMemStm::Dump(char **ppszDump, ULONG ulFlag, int nIndentLevel) { int i; char *pszPrefix; char *pszMEMSTM; char *pszCMutexSem; dbgstream dstrPrefix; dbgstream dstrDump(400); // determine prefix of newlines if ( ulFlag & DEB_VERBOSE ) { dstrPrefix << this << " _VB "; } // determine indentation prefix for all newlines for (i = 0; i < nIndentLevel; i++) { dstrPrefix << DUMPTAB; } pszPrefix = dstrPrefix.str(); // put data members in stream dstrDump << pszPrefix << "Impl. Signature = " << m_dwSig << endl; dstrDump << pszPrefix << "No. of References = " << m_refs << endl; dstrDump << pszPrefix << "Seek pointer = " << m_pos << endl; dstrDump << pszPrefix << "Memory handle = " << m_hMem << endl; if (m_pData != NULL) { pszMEMSTM = DumpMEMSTM(m_pData, ulFlag, nIndentLevel + 1); dstrDump << pszPrefix << "MEMSTM:" << endl; dstrDump << pszMEMSTM; CoTaskMemFree(pszMEMSTM); } else { dstrDump << pszPrefix << "MEMSTM = " << m_pData << endl; } pszCMutexSem = DumpCMutexSem ((CMutexSem2*) m_pmxs->GetMutexSem()); dstrDump << pszPrefix << "Mutex = " << pszCMutexSem << endl; CoTaskMemFree(pszCMutexSem); // cleanup and provide pointer to character array *ppszDump = dstrDump.str(); if (*ppszDump == NULL) { *ppszDump = UtDupStringA(szDumpErrorMessage); } CoTaskMemFree(pszPrefix); return NOERROR; } #endif // _DEBUG //+------------------------------------------------------------------------- // // Function: DumpCMemStm, public (_DEBUG only) // // Synopsis: calls the CMemStm::Dump method, takes care of errors and // returns the zero terminated string // // Effects: // // Arguments: [pMS] - pointer to CMemStm // [ulFlag] - flag determining prefix of all newlines of the // out character array (default is 0 - no prefix) // [nIndentLevel] - will add a indent prefix after the other prefix // for ALL newlines (including those with no prefix) // // Requires: // // Returns: character array of structure dump or error (null terminated) // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 20-Jan-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG char *DumpCMemStm(CMemStm *pMS, ULONG ulFlag, int nIndentLevel) { HRESULT hresult; char *pszDump; if (pMS == NULL) { return UtDupStringA(szDumpBadPtr); } hresult = pMS->Dump(&pszDump, ulFlag, nIndentLevel); if (hresult != NOERROR) { CoTaskMemFree(pszDump); return DumpHRESULT(hresult); } return pszDump; } #endif // _DEBUG //+------------------------------------------------------------------------- // // Function: CreateMemStm // // Synopsis: Allocates memory and creates a CMemStm for it. // // Effects: // // Arguments: [cb] -- the number of bytes to allocate // [phMem] -- where to put a handle to the MEMSTM structure // // Requires: // // Returns: LPSTREAM to the CMemStream // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 03-Dec-93 alexgo 32bit port // // Notes: phMem must be free'd with ReleaseMemStm (because of ref // counting and the nested handle) // //-------------------------------------------------------------------------- #pragma SEG(CreateMemStm) STDAPI_(LPSTREAM) CreateMemStm(DWORD cb, LPHANDLE phMem) { VDATEHEAP(); HANDLE h; LPSTREAM pstm = NULL; if (phMem) { *phMem = NULL; } h = GlobalAlloc (GMEM_SHARE | GMEM_MOVEABLE, cb); if (NULL==h) { return NULL; } if (CreateStreamOnHGlobal (h, TRUE, &pstm) != NOERROR) { GlobalFree(h); // COM+ 22886 return NULL; } if (phMem) { // retrieve handle from just-created CMemStm *phMem = ((CMemStm FAR*)pstm)->m_hMem; // use pointer to bump ref count Assert(((CMemStm FAR*)pstm)->m_pData != NULL); InterlockedIncrement ((LPLONG) &((CMemStm FAR*)pstm)->m_pData->cRef); // AddRefMemStm } return pstm; } //+------------------------------------------------------------------------- // // Function: CloneMemStm // // Synopsis: Clones a memory stream // // Effects: // // Arguments: [hMem] -- a handle to the MEMSTM block // // Requires: // // Returns: LPSTREAM // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CloneMemStm) STDAPI_(LPSTREAM) CloneMemStm(HANDLE hMem) { VDATEHEAP(); return CMemStm::Create(hMem, NULL); // Create the stream } //+------------------------------------------------------------------------- // // Function: ReleaseMemStm // // Synopsis: Releases the memory used by a MEMSTM structure (including // the nested handle) // // Effects: // // Arguments: [phMem] -- pointer the MEMSTM handle // [fInternalOnly] -- if TRUE, then only the actual memory // that MEMSTM refers to is freed // (not the MEMSTM structure itself) // // Requires: // // Returns: void // // Signals: // // Modifies: sets *phMem to NULL on success // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port and fixed bad memory access // bug // // Notes: REVIEW32: look at taking out the second argument // //-------------------------------------------------------------------------- #pragma SEG(ReleaseMemStm) STDAPI_(void) ReleaseMemStm (LPHANDLE phMem, BOOL fInternalOnly) { VDATEHEAP(); struct MEMSTM FAR* pData; pData = (MEMSTM FAR*) GlobalLock(*phMem); // check for NULL pointer in case handle got freed already // decrement ref count and free if no refs left if (pData != NULL && InterlockedDecrement ((LPLONG) &pData->cRef) == 0) { if (pData->fDeleteOnRelease) { Verify (0==GlobalFree (pData->hGlobal)); } if (!fInternalOnly) { GlobalUnlock(*phMem); Verify (0==GlobalFree(*phMem)); goto End; } } GlobalUnlock(*phMem); End: *phMem = NULL; } //+------------------------------------------------------------------------- // // Function: CreateStreamOnHGlobal // // Synopsis: Creates a CMemStm from the given hGlobal (if [hGlobal] is // NULL, we allocate a zero byte one) // // Effects: // // Arguments: [hGlobal] -- the memory // [fDeleteOnRelease] -- whether the memory should be // release on delete // [ppstm] -- where to put the stream // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 11-Jan-93 alexgo removed initialization of cbSize to -1 // to fix compile warning // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CreateStreamOnHGlobal) STDAPI CreateStreamOnHGlobal(HANDLE hGlobal, BOOL fDeleteOnRelease, LPSTREAM FAR* ppstm) { OLETRACEIN((API_CreateStreamOnHGlobal, PARAMFMT("hGlobal= %h, fDeleteOnRelease= %B, ppstm= %p"), hGlobal, fDeleteOnRelease, ppstm)); VDATEHEAP(); HANDLE hMem = NULL; struct MEMSTM FAR* pData = NULL; LPSTREAM pstm = NULL; DWORD cbSize; BOOL fAllocated = FALSE; HRESULT hresult; VDATEPTROUT_LABEL (ppstm, LPSTREAM, SafeExit, hresult); *ppstm = NULL; if (NULL==hGlobal) { hGlobal = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, 0); if (hGlobal == NULL) { goto FreeMem; } cbSize = 0; fAllocated = TRUE; } else { cbSize = (ULONG) GlobalSize (hGlobal); // Is there a way to verify a zero-sized handle? // we currently do no verification for them if (cbSize!=0) { // verify validity of passed-in handle if (NULL==GlobalLock(hGlobal)) { // bad handle hresult = ResultFromScode (E_INVALIDARG); goto SafeExit; } GlobalUnlock (hGlobal); } } hMem = GlobalAlloc (GMEM_SHARE | GMEM_MOVEABLE, sizeof (MEMSTM)); if (hMem == NULL) { goto FreeMem; } pData = (MEMSTM FAR*) GlobalLock(hMem); if (pData == NULL) { GlobalUnlock(hMem); goto FreeMem; } pData->cRef = 0; pData->cb = cbSize; pData->fDeleteOnRelease = fDeleteOnRelease; pData->hGlobal = hGlobal; GlobalUnlock(hMem); pstm = CMemStm::Create(hMem, NULL); if (pstm == NULL) { goto FreeMem; } *ppstm = pstm; CALLHOOKOBJECTCREATE(S_OK,CLSID_NULL,IID_IStream,(IUnknown **)ppstm); hresult = NOERROR; goto SafeExit; FreeMem: if (hGlobal && fAllocated) { Verify(0==GlobalFree(hGlobal)); } if (hMem) { Verify(0==GlobalFree(hMem)); } LEERROR(1, "Out of memory!"); hresult = ResultFromScode(E_OUTOFMEMORY); SafeExit: OLETRACEOUT((API_CreateStreamOnHGlobal, hresult)); return hresult; } //+------------------------------------------------------------------------- // // Function: GetHGlobalFromStream // // Synopsis: Retrieves the HGLOBAL to the memory from the given stream // pointer (must be a pointer to a CMemByte structure) // // Effects: // // Arguments: [pstm] -- pointer to the CMemByte // [phglobal] -- where to put the hglobal // // Requires: // // Returns: HRESULT (E_INVALIDARG, E_OUTOFMEMORY, NOERROR) // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(GetHGlobalFromStream) STDAPI GetHGlobalFromStream(LPSTREAM pstm, HGLOBAL FAR* phglobal) { OLETRACEIN((API_GetHGlobalFromStream, PARAMFMT("pstm= %p, phglobal= %p"), pstm, phglobal)); VDATEHEAP(); HRESULT hresult; CMemStm FAR* pCMemStm; MEMSTM FAR* pMem; VDATEIFACE_LABEL (pstm, errRtn, hresult); VDATEPTROUT_LABEL(phglobal, HANDLE, errRtn, hresult); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStream,(IUnknown **)&pstm); pCMemStm = (CMemStm FAR*) pstm; if (!IsValidReadPtrIn (&(pCMemStm->m_dwSig), sizeof(ULONG)) || pCMemStm->m_dwSig != STREAM_SIG) { // we were passed someone else's implementation of ILockBytes hresult = ResultFromScode (E_INVALIDARG); goto errRtn; } pMem= pCMemStm->m_pData; if (NULL==pMem) { LEERROR(1, "Out of memory!"); hresult = ResultFromScode (E_OUTOFMEMORY); goto errRtn; } Assert (pMem->cb <= GlobalSize (pMem->hGlobal)); Verify (*phglobal = pMem->hGlobal); hresult = NOERROR; errRtn: OLETRACEOUT((API_GetHGlobalFromStream, hresult)); return hresult; } ////////////////////////////////////////////////////////////////////////// // // Shared memory ILockBytes implementation // //+------------------------------------------------------------------------- // // Member: CMemBytes::QueryInterface // // Synopsis: returns the requested interface pointer // // Effects: a CMarshalMemBytes will be created if IID_IMarshal is // requested // // Arguments: [iidInterface] -- the requested interface ID // [ppvObj] -- where to put the interface pointer // // Requires: // // Returns: NOERROR, E_OUTOFMEMORY, E_NOINTERFACE // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 11-Jan-94 alexgo removed QI for IMarshal so that // the standard marshaller will be used. // This is to enable correct operation on // 32bit platforms. // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_QueryInterface) STDMETHODIMP CMemBytes::QueryInterface(REFIID iidInterface, void FAR* FAR* ppvObj) { VDATEHEAP(); HRESULT error; VDATEPTROUT( ppvObj, LPVOID ); *ppvObj = NULL; VDATEIID( iidInterface ); if (m_pData != NULL && (IsEqualIID(iidInterface, IID_ILockBytes) || IsEqualIID(iidInterface, IID_IUnknown))) { InterlockedIncrement ((LPLONG) &m_refs); // A pointer to this object is returned *ppvObj = this; error = NOERROR; } // this is not an else if because m_pData can be NULL and we // allow creating a CMarshalMemBytes. REVIEW32: We may want // to remove this behavior. #ifndef WIN32 if (IsEqualIID(iidInterface, IID_IMarshal)) { *ppvObj = (LPVOID) CMarshalMemBytes::Create(this); if (*ppvObj != NULL) { error = NOERROR; } else { error = ReportResult(0, E_OUTOFMEMORY, 0, 0); } } #endif else { *ppvObj = NULL; error = ResultFromScode(E_NOINTERFACE); } return error; } //+------------------------------------------------------------------------- // // Member: CMemBytes::AddRef // // Synopsis: Incrememts the reference count // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CMemBytes::AddRef(void) { VDATEHEAP(); return InterlockedIncrement ((LPLONG) &m_refs); } //+------------------------------------------------------------------------- // // Member: CMemBytes::Release // // Synopsis: decrements the reference count // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 16-Dec-93 alexgo added GlobalUnlock to match the Global // Lock in Create // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CMemBytes::Release(void) { VDATEHEAP(); ULONG ulRefs = InterlockedDecrement ((LPLONG) &m_refs); if (ulRefs != 0) { return ulRefs; } // GlobalUnlock the m_hMem that we GlobalLocke'd in Create GlobalUnlock(m_hMem); ReleaseMemStm(&m_hMem); delete this; return 0; } //+------------------------------------------------------------------------- // // Member: CMemBytes::ReadAt // // Synopsis: reads [cb] bytes from starting position [ulOffset] // // Effects: // // Arguments: [ulOffset] -- the offset to start reading from // [pb] -- where to put the data // [cb] -- the number of bytes to read // [pcbRead] -- where to put the number of bytes actually // read // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: just calls xmemcpy // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_ReadAt) STDMETHODIMP CMemBytes::ReadAt(ULARGE_INTEGER ulOffset, void HUGEP* pb, ULONG cb, ULONG FAR* pcbRead) { VDATEHEAP(); HRESULT error = NOERROR; ULONG cbRead = cb; VDATEPTROUT( pb, char ); // make sure we don't offset out of the address space! AssertSz(ulOffset.HighPart == 0, "CMemBytes: offset greater than 2^32"); if (pcbRead) { *pcbRead = 0L; } if (cbRead + ulOffset.LowPart > m_pData->cb) { if (ulOffset.LowPart > m_pData->cb) { // the offset overruns the size of the memory cbRead = 0; } else { // just read what's left cbRead = m_pData->cb - ulOffset.LowPart; } } if (cbRead > 0) { BYTE HUGEP* pGlobal = (BYTE HUGEP *)GlobalLock( m_pData->hGlobal); if (NULL==pGlobal) { LEERROR(1, "GlobalLock failed!"); return ResultFromScode (STG_E_READFAULT); } _xmemcpy(pb, pGlobal + ulOffset.LowPart, cbRead); GlobalUnlock (m_pData->hGlobal); } if (pcbRead != NULL) { *pcbRead = cbRead; } return error; } //+------------------------------------------------------------------------- // // Member: CMemBytes::WriteAt // // Synopsis: writes [cb] bytes at [ulOffset] in the stream // // Effects: // // Arguments: [ulOffset] -- the offset at which to start writing // [pb] -- the buffer to read from // [cb] -- the number of bytes to write // [pcbWritten] -- where to put the number of bytes written // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_WriteAt) STDMETHODIMP CMemBytes::WriteAt(ULARGE_INTEGER ulOffset, void const HUGEP* pb, ULONG cb, ULONG FAR* pcbWritten) { VDATEHEAP(); HRESULT error = NOERROR; ULONG cbWritten = cb; BYTE HUGEP* pGlobal; VDATEPTRIN( pb, char ); // make sure the offset doesn't go beyond our address space! AssertSz(ulOffset.HighPart == 0, "WriteAt, offset greater than 2^32"); if (pcbWritten) { *pcbWritten = 0; } if (cbWritten + ulOffset.LowPart > m_pData->cb) { ULARGE_INTEGER ularge_integer; ULISet32( ularge_integer, ulOffset.LowPart + cbWritten); error = SetSize( ularge_integer ); if (error != NOERROR) { goto Exit; } } // CMemBytes does not allow zero-sized memory handles pGlobal = (BYTE HUGEP *)GlobalLock (m_pData->hGlobal); if (NULL==pGlobal) { LEERROR(1, "GlobalLock failed!"); return ResultFromScode (STG_E_WRITEFAULT); } _xmemcpy(pGlobal + ulOffset.LowPart, pb, cbWritten); GlobalUnlock (m_pData->hGlobal); if (pcbWritten != NULL) { *pcbWritten = cbWritten; } Exit: return error; } //+------------------------------------------------------------------------- // // Member: CMemBytes::Flush // // Synopsis: Flushes internal state to disk // Not needed for memory ILockBytes // // Effects: // // Arguments: void // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_Flush) STDMETHODIMP CMemBytes::Flush(void) { VDATEHEAP(); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemBytes::SetSize // // Synopsis: Sets the size of the memory buffer // // Effects: // // Arguments: [cb] -- the new size // // Requires: // // Returns: NOERROR, E_OUTOFMEMORY // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_SetSize) STDMETHODIMP CMemBytes::SetSize(ULARGE_INTEGER cb) { VDATEHEAP(); HANDLE hMemNew; AssertSz(cb.HighPart == 0, "SetSize: trying to set to more than 2^32 bytes"); if (m_pData->cb == cb.LowPart) { return NOERROR; } hMemNew = GlobalReAlloc(m_pData->hGlobal, max (cb.LowPart, 1), GMEM_SHARE | GMEM_MOVEABLE); if (hMemNew == NULL) { return ResultFromScode(E_OUTOFMEMORY); } m_pData->hGlobal = hMemNew; m_pData->cb = cb.LowPart; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemBytes::LockRegion // // Synopsis: Locks a region. Since only we have access to the memory, // nothing needs to be done (note that the *app* also may // access, but there's not much we can do about that) // // Effects: // // Arguments: [libOffset] -- offset to start with // [cb] -- the number of bytes in the locked region // [dwLockType] -- the type of lock to use // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_LockRegion) STDMETHODIMP CMemBytes::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { VDATEHEAP(); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemBytes::UnlockRegion // // Synopsis: Unlocks a region; since only we have access to the memory, // nothing needs to be done. // // Effects: // // Arguments: [libOffset] -- the offset to start with // [cb] -- the number of bytes in the region // [dwLockType] -- the lock type // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_UnlockRegion) STDMETHODIMP CMemBytes::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { VDATEHEAP(); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemBytes::Stat // // Synopsis: returns status information // // Effects: // // Arguments: [pstatstg] -- where to put the status info // [statflag] -- status flags (ignored) // // Requires: // // Returns: NOERROR, E_INVALIDARG // // Signals: // // Modifies: // // Derivation: ILockBytes // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // 01-Jun-94 AlexT Set type correctly // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_Stat) STDMETHODIMP CMemBytes::Stat(STATSTG FAR *pstatstg, DWORD statflag) { VDATEHEAP(); VDATEPTROUT( pstatstg, STATSTG ); memset ( pstatstg, 0, sizeof(STATSTG) ); pstatstg->type = STGTY_LOCKBYTES; pstatstg->cbSize.LowPart = m_pData->cb; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMemBytes::Create // // Synopsis: Creates an instance of CMemBytes // // Effects: // // Arguments: [hMem] -- handle to the memory (must be a MEMSTM block) // // Requires: // // Returns: CMemBytes * // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // 16-Dec-93 alexgo fixed bad pointer bug (took out // GlobalUnlock) // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytes_Create) STDSTATICIMP_(CMemBytes FAR*) CMemBytes::Create(HANDLE hMem) { VDATEHEAP(); CMemBytes FAR* pCMemBytes = NULL; struct MEMSTM FAR* pData; pData = (MEMSTM FAR*) GlobalLock(hMem); if (pData != NULL) { Assert (pData->hGlobal); pCMemBytes = new CMemBytes; if (pCMemBytes != NULL) { // Initialize CMemBytes pCMemBytes->m_dwSig = LOCKBYTE_SIG; pCMemBytes->m_hMem = hMem; InterlockedIncrement ((LPLONG) &(pCMemBytes->m_pData = pData)->cRef); // AddRefMemStm pCMemBytes->m_refs = 1; CALLHOOKOBJECTCREATE(S_OK,CLSID_NULL,IID_ILockBytes, (IUnknown **)&pCMemBytes); } else { // uh-oh, low on memory GlobalUnlock(hMem); } } // we don't GlobalUnlock(hMem) until we destory this CMemBytes return pCMemBytes; } //+------------------------------------------------------------------------- // // Member: CMemBytes::Dump, public (_DEBUG only) // // Synopsis: return a string containing the contents of the data members // // Effects: // // Arguments: [ppszDump] - an out pointer to a null terminated character array // [ulFlag] - flag determining prefix of all newlines of the // out character array (default is 0 - no prefix) // [nIndentLevel] - will add a indent prefix after the other prefix // for ALL newlines (including those with no prefix) // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: [ppsz] - argument // // Derivation: // // Algorithm: use dbgstream to create a string containing information on the // content of data structures // // History: dd-mmm-yy Author Comment // 20-Jan-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG HRESULT CMemBytes::Dump(char **ppszDump, ULONG ulFlag, int nIndentLevel) { int i; char *pszPrefix; char *pszMEMSTM; dbgstream dstrPrefix; dbgstream dstrDump(400); // determine prefix of newlines if ( ulFlag & DEB_VERBOSE ) { dstrPrefix << this << " _VB "; } // determine indentation prefix for all newlines for (i = 0; i < nIndentLevel; i++) { dstrPrefix << DUMPTAB; } pszPrefix = dstrPrefix.str(); // put data members in stream dstrDump << pszPrefix << "Impl. Signature = " << m_dwSig << endl; dstrDump << pszPrefix << "No. of References = " << m_refs << endl; dstrDump << pszPrefix << "Memory handle = " << m_hMem << endl; if (m_pData != NULL) { pszMEMSTM = DumpMEMSTM(m_pData, ulFlag, nIndentLevel + 1); dstrDump << pszPrefix << "MEMSTM:" << endl; dstrDump << pszMEMSTM; CoTaskMemFree(pszMEMSTM); } else { dstrDump << pszPrefix << "MEMSTM = " << m_pData << endl; } // cleanup and provide pointer to character array *ppszDump = dstrDump.str(); if (*ppszDump == NULL) { *ppszDump = UtDupStringA(szDumpErrorMessage); } CoTaskMemFree(pszPrefix); return NOERROR; } #endif // _DEBUG //+------------------------------------------------------------------------- // // Function: DumpCMemBytes, public (_DEBUG only) // // Synopsis: calls the CMemBytes::Dump method, takes care of errors and // returns the zero terminated string // // Effects: // // Arguments: [pMB] - pointer to CMemBytes // [ulFlag] - flag determining prefix of all newlines of the // out character array (default is 0 - no prefix) // [nIndentLevel] - will add a indent prefix after the other prefix // for ALL newlines (including those with no prefix) // // Requires: // // Returns: character array of structure dump or error (null terminated) // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // 20-Jan-95 t-ScottH author // // Notes: // //-------------------------------------------------------------------------- #ifdef _DEBUG char *DumpCMemBytes(CMemBytes *pMB, ULONG ulFlag, int nIndentLevel) { HRESULT hresult; char *pszDump; if (pMB == NULL) { return UtDupStringA(szDumpBadPtr); } hresult = pMB->Dump(&pszDump, ulFlag, nIndentLevel); if (hresult != NOERROR) { CoTaskMemFree(pszDump); return DumpHRESULT(hresult); } return pszDump; } #endif // _DEBUG // -------------------------------------------------------------------- // EVERYTHING BELOW HERE HAS BEEN REMOVED FROM WIN32 VERSIONS. #ifndef WIN32 // CMemStm object's IMarshal implementation // //+------------------------------------------------------------------------- // // Member: CMarshalMemStm::QueryInterface // // Synopsis: returns the requested interface ID // // Effects: // // Arguments: [iidInterface] -- the requested interface // [ppvObj] -- where to put the interface pointer // // Requires: // // Returns: NOERROR, E_NOINTERFACE // // Signals: // // Modifies: // // Derivation: IMarshal // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_QueryInterface) STDMETHODIMP CMarshalMemStm::QueryInterface(REFIID iidInterface, void FAR* FAR* ppvObj) { VDATEHEAP(); HRESULT error; VDATEPTROUT( ppvObj, LPVOID ); *ppvObj = NULL; VDATEIID( iidInterface ); // Two interfaces supported: IUnknown, IMarshal if (IsEqualIID(iidInterface, IID_IMarshal) || IsEqualIID(iidInterface, IID_IUnknown)) { InterlockedIncrement (&m_refs); // A pointer to this object is returned *ppvObj = this; error = NOERROR; } else { // Not accessible or unsupported interface *ppvObj = NULL; error = ResultFromScode (E_NOINTERFACE); } return error; } //+------------------------------------------------------------------------- // // Member: CMarshalMemstm::AddRef // // Synopsis: increments the reference count // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IMarshal // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_AddRef) STDMETHODIMP_(ULONG) CMarshalMemStm::AddRef(void) { VDATEHEAP(); return InterlockedIncrement (&m_refs); } //+------------------------------------------------------------------------- // // Member: CMarshalMemStm::Release // // Synopsis: decrements the reference count // // Effects: // // Arguments: void // // Requires: // // Returns: ULONG -- the new reference count // // Signals: // // Modifies: // // Derivation: IMarshal // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_Release) STDMETHODIMP_(ULONG) CMarshalMemStm::Release(void) { VDATEHEAP(); ULONG ulRefs = InterlockedDecrement (&m_refs); if (ulRefs != 0) { return ulRefs; } if (m_pMemStm != NULL) { m_pMemStm->Release(); } delete this; // Free storage return 0; } //+------------------------------------------------------------------------- // // Member: CMarshalMemStm::GetUnmarshalClass // // Synopsis: returns the object class that should be used to create // the proxy in the unmarshalling process (CLSID_StdMemStm) // // Effects: // // Arguments: [riid] -- the interface ID of the object to be // marshalled // [pv] -- pointer to the object // [dwDestContext] -- marshalling context (such a no shared mem) // [pvDestContext] -- reserved // [mshlfags] -- marshalling flags (e.g. NORMAL) // [pCid] -- where to put the class ID // // Requires: // // Returns: NOERROR // // Signals: // // Modifies: // // Derivation: IMarshal // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_GetUnmarshalClass) STDMETHODIMP CMarshalMemStm::GetUnmarshalClass(REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, CLSID FAR* pCid) { VDATEHEAP(); VDATEPTROUT( pCid, CLSID); *pCid = CLSID_StdMemStm; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMarshalMemStm::GetUnmarshalSizeMax // // Synopsis: returns the amount of memory needed to marshal the data // (in this case, the hglobal) // // Effects: // // Arguments: [riid] -- the interface of the object // [pv] -- pointer to the object // [dwDestContext] -- marshalling context (such a no shared mem) // [pvDestContext] -- reserved // [mshlfags] -- marshalling flags (e.g. NORMAL) // [pSize] -- where to put the size // // Requires: // // Returns: NOERROR, E_INVALIDARG // // Signals: // // Modifies: // // Derivation: IMarshal // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_GetMarshalSizeMax) STDMETHODIMP CMarshalMemStm::GetMarshalSizeMax(REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, DWORD FAR* pSize) { VDATEHEAP(); VDATEPTROUT(pSize, DWORD); *pSize = sizeof(m_pMemStm->m_hMem); return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMarshalMemStm::MarshalInterface // // Synopsis: marshals a reference of the objects interface into [pStm] // // Effects: // // Arguments: [pStm] -- the stream into which the object should // be marshalled // [riid] -- the interface ID of the object to be // marshalled // [pv] -- points to the interface // [dwDestContext] -- the marshalling context // [pvDestContext] -- unused // [mshlflags] -- marshalling flags // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IMarshal // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_MarshalInterface) STDMETHODIMP CMarshalMemStm::MarshalInterface(IStream FAR* pStm, REFIID riid, void FAR* pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags) { VDATEHEAP(); VDATEPTRIN( pStm, IStream ); VDATEIID( riid ); VDATEIFACE( pv ); if (m_pMemStm == NULL) { return ResultFromScode(E_UNSPEC); } if ((!IsEqualIID(riid, IID_IStream) && !IsEqualIID(riid, IID_IUnknown)) || pv != m_pMemStm) { return ReportResult(0, E_INVALIDARG, 0, 0); } // increase ref count on hglobal (ReleaseMarshalData has -- to match) HRESULT error; if ((error = pStm->Write(&m_pMemStm->m_hMem, sizeof(m_pMemStm->m_hMem), NULL)) == NOERROR) { InterlockedIncrement (&m_pMemStm->m_pData->cRef); // AddRefMemStm } return error; } //+------------------------------------------------------------------------- // // Member: CMarshalMemStm::UnmarshalInterface // // Synopsis: Unmarshals an the object from the given stream // // Effects: // // Arguments: [pStm] -- the stream to read data from // [riid] -- the interface the caller wants from the // object // [ppvObj] -- where to put a pointer to the object // // Requires: // // Returns: HRESULT // // Signals: // // Modifies: // // Derivation: IMarshal // // Algorithm: // // History: dd-mmm-yy Author Comment // 05-Dec-93 alexgo 32bit port // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_UnmarshalInterface) STDMETHODIMP CMarshalMemStm::UnmarshalInterface(IStream FAR* pStm, REFIID riid, void FAR* FAR* ppv) { VDATEHEAP(); HRESULT error; HANDLE hMem; VDATEPTROUT( ppv, LPVOID ); *ppv = NULL; VDATEPTRIN( pStm, IStream ); VDATEIID( riid ); if (!IsEqualIID(riid, IID_IStream) && !IsEqualIID(riid, IID_IUnknown)) { return ResultFromScode(E_INVALIDARG); } error = pStm->Read(&hMem,sizeof(hMem),NULL); if (error != NOERROR) { return error; } if (m_pMemStm != NULL) { if (hMem != m_pMemStm->m_hMem) { return ResultFromScode(E_UNSPEC); } } else { m_pMemStm = (CMemStm FAR*) CloneMemStm(hMem); if (m_pMemStm == NULL) { return ResultFromScode(E_OUTOFMEMORY); } } m_pMemStm->AddRef(); *ppv = (LPVOID) m_pMemStm; return NOERROR; } //+------------------------------------------------------------------------- // // Member: CMarshalMemStm::ReleaseMarshalData // // Synopsis: releases the marshalling data (takes away ref count on // hglobal) // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_ReleaseMarshalData) STDMETHODIMP CMarshalMemStm::ReleaseMarshalData(IStream FAR* pStm) { VDATEHEAP(); // reduce ref count on hglobal (matches that done in MarshalInterface) HRESULT error; HANDLE hMem; VDATEIFACE( pStm ); error = pStm->Read(&hMem,sizeof(hMem),NULL); if (error == NOERROR) ReleaseMemStm(&hMem); return error; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_DisconnectObject) STDMETHODIMP CMarshalMemStm::DisconnectObject(DWORD dwReserved) { VDATEHEAP(); return NOERROR; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemStm_Create) STDSTATICIMP_(CMarshalMemStm FAR*) CMarshalMemStm::Create(CMemStm FAR* pMemStm) { VDATEHEAP(); CMarshalMemStm FAR* pMMS; //VDATEPTRIN rejects NULL if( pMemStm ) GEN_VDATEPTRIN( pMemStm, CMemStm, (CMarshalMemStm FAR *) NULL ); pMMS = new(MEMCTX_TASK, NULL) CMarshalMemStm; if (pMMS == NULL) return NULL; if (pMemStm != NULL) { pMMS->m_pMemStm = pMemStm; pMMS->m_pMemStm->AddRef(); } pMMS->m_refs = 1; return pMMS; } //+------------------------------------------------------------------------- // // Function: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemStmUnMarshal) STDAPI_(IUnknown FAR*) CMemStmUnMarshal(void) { VDATEHEAP(); return CMarshalMemStm::Create(NULL); } // CMemBytes object's IMarshal implementation // //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemBytes_QueryInterface) STDMETHODIMP CMarshalMemBytes::QueryInterface(REFIID iidInterface, void FAR* FAR* ppvObj) { VDATEHEAP(); HRESULT error; VDATEIID( iidInterface ); VDATEPTROUT( ppvObj, LPVOID ); *ppvObj = NULL; // Two interfaces supported: IUnknown, IMarshal if (IsEqualIID(iidInterface, IID_IMarshal) || IsEqualIID(iidInterface, IID_IUnknown)) { InterlockedIncrement (&m_refs); // A pointer to this object is returned *ppvObj = this; error = NOERROR; } else { // Not accessible or unsupported interface *ppvObj = NULL; error = ReportResult(0, E_NOINTERFACE, 0, 0); } return error; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- // Called when CMarshalMemBytes is referenced by an additional pointer. // #pragma SEG(CMarshalMemBytes_AddRef) STDMETHODIMP_(ULONG) CMarshalMemBytes::AddRef(void) { VDATEHEAP(); return InterlockedIncrement (&m_refs); } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemBytes_Release) // Called when a pointer to this CMarshalMemBytes is discarded // STDMETHODIMP_(ULONG) CMarshalMemBytes::Release(void) { VDATEHEAP(); ULONG ulRefs = InterlockedDecrement (&m_refs); if (ulRefs != 0) // Still used by others return ulRefs; if (m_pMemBytes != NULL) m_pMemBytes->Release(); delete this; // Free storage return 0; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemBytes_GetUnmarshalClass) // Returns the clsid of the object that created this CMarshalMemBytes. // STDMETHODIMP CMarshalMemBytes::GetUnmarshalClass(REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, CLSID FAR* pCid) { VDATEHEAP(); VDATEIID( riid ); VDATEIFACE( pv ); *pCid = CLSID_StdMemBytes; return NOERROR; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemBytes_GetMarshalSizeMax) STDMETHODIMP CMarshalMemBytes::GetMarshalSizeMax(REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, DWORD FAR* pSize) { VDATEHEAP(); VDATEPTROUT( pSize, DWORD ); VDATEIID( riid ); VDATEIFACE( pv ); *pSize = sizeof(m_pMemBytes->m_hMem); return NOERROR; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemBytes_MarshalInterface) STDMETHODIMP CMarshalMemBytes::MarshalInterface(IStream FAR* pStm, REFIID riid, void FAR* pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags) { VDATEHEAP(); VDATEPTRIN(pStm, IStream ); VDATEIID( riid ); if ( pv ) VDATEPTRIN( pv , char ); if (m_pMemBytes == NULL) return ReportResult(0, E_UNSPEC, 0, 0); if ((!IsEqualIID(riid, IID_ILockBytes) && !IsEqualIID(riid, IID_IUnknown)) || pv != m_pMemBytes) return ReportResult(0, E_INVALIDARG, 0, 0); // increase ref count on hglobal (ReleaseMarshalData has -- to match) HRESULT error; if ((error = pStm->Write(&m_pMemBytes->m_hMem, sizeof(m_pMemBytes->m_hMem), NULL)) == NOERROR) InterlockedIncrement (&m_pMemBytes->m_pData->cRef); // AddRefMemStm return error; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemBytes_UnmarshalInterface) STDMETHODIMP CMarshalMemBytes::UnmarshalInterface(IStream FAR* pStm, REFIID riid, void FAR* FAR* ppv) { VDATEHEAP(); HRESULT error; HANDLE hMem; VDATEPTROUT( ppv , LPVOID ); *ppv = NULL; VDATEIFACE( pStm ); VDATEIID( riid ); if (!IsEqualIID(riid, IID_ILockBytes) && !IsEqualIID(riid, IID_IUnknown)) return ReportResult(0, E_INVALIDARG, 0, 0); error = pStm->Read(&hMem,sizeof(hMem),NULL); if (error != NOERROR) return error; if (m_pMemBytes != NULL) { if (hMem != m_pMemBytes->m_hMem) return ReportResult(0, E_UNSPEC, 0, 0); } else { m_pMemBytes = CMemBytes::Create(hMem); // Create the lockbytes if (m_pMemBytes == NULL) return ReportResult(0, E_OUTOFMEMORY, 0, 0); } m_pMemBytes->AddRef(); *ppv = (LPVOID) m_pMemBytes; return NOERROR; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- STDMETHODIMP CMarshalMemBytes::ReleaseMarshalData(IStream FAR* pStm) { VDATEHEAP(); // reduce ref count on hglobal (matches that done in MarshalInterface) HRESULT error; HANDLE hMem; VDATEIFACE( pStm ); error = pStm->Read(&hMem,sizeof(hMem),NULL); if (error == NOERROR) ReleaseMemStm(&hMem); return error; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemBytes_DisconnectObject) STDMETHODIMP CMarshalMemBytes::DisconnectObject(DWORD dwReserved) { VDATEHEAP(); return NOERROR; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMarshalMemBytes_Create) STDSTATICIMP_(CMarshalMemBytes FAR*) CMarshalMemBytes::Create( CMemBytes FAR* pMemBytes) { VDATEHEAP(); CMarshalMemBytes FAR* pMMB; //VDATEPTRIN rejects NULL if( pMemBytes ) GEN_VDATEPTRIN( pMemBytes, CMemBytes, (CMarshalMemBytes FAR *)NULL); pMMB = new(MEMCTX_TASK, NULL) CMarshalMemBytes; if (pMMB == NULL) return NULL; if (pMemBytes != NULL) { pMMB->m_pMemBytes = pMemBytes; pMMB->m_pMemBytes->AddRef(); } pMMB->m_refs = 1; return pMMB; } //+------------------------------------------------------------------------- // // Member: // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Signals: // // Modifies: // // Derivation: // // Algorithm: // // History: dd-mmm-yy Author Comment // // Notes: // //-------------------------------------------------------------------------- #pragma SEG(CMemBytesUnMarshal) STDAPI_(IUnknown FAR*) CMemBytesUnMarshal(void) { VDATEHEAP(); return CMarshalMemBytes::Create(NULL); } #endif // !WIN32 -- win32 builds use standard marshalling for // streams and lockbytes.