//+------------------------------------------------------------------- // // File: memstm.cxx // // Contents: test class for IStream // // Classes: CMemStm // // History: 23-Nov-92 Rickhi Created // //-------------------------------------------------------------------- #include #pragma hdrstop #include "memstm.h" extern "C" { const GUID CLSID_StdMemStm = {0x00000301,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; const GUID CLSID_StdMemBytes = {0x00000302,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}}; } // Shared memory IStream implementation // STDMETHODIMP CMemStm::QueryInterface(REFIID iidInterface, void **ppvObj) { SCODE error; *ppvObj = NULL; // Two interfaces supported: IUnknown, IStream if (m_pData != NULL && (IsEqualIID(iidInterface,IID_IStream) || IsEqualIID(iidInterface,IID_IUnknown))) { m_refs++; // A pointer to this object is returned *ppvObj = this; error = S_OK; } else { // Not accessible or unsupported interface *ppvObj = NULL; error = E_NOINTERFACE; } return error; } STDMETHODIMP_(ULONG) CMemStm::AddRef(void) { ++ m_refs; return m_refs; } STDMETHODIMP_(ULONG) CMemStm::Release(void) { --m_refs; if (m_refs != 0) // Still used by others return m_refs; // Matches the allocation in CMemStm::Create(). // if (--m_pData->cRef == 0) { GlobalUnlock(m_hMem); GlobalFree(m_hMem); } else GlobalUnlock(m_hMem); delete this; // Free storage return 0; } STDMETHODIMP CMemStm::Read(void HUGEP* pb, ULONG cb, ULONG * pcbRead) { SCODE error = S_OK; ULONG cbRead = cb; if (pcbRead) *pcbRead = 0L; if (cbRead + m_pos > m_pData->cb) { cbRead = m_pData->cb - m_pos; error = E_FAIL; } // BUGBUG - size_t limit memcpy(pb,m_pData->buf + m_pos,(size_t) cbRead); m_pos += cbRead; if (pcbRead != NULL) *pcbRead = cbRead; return error; } STDMETHODIMP CMemStm::Write(void const HUGEP* pb, ULONG cb, ULONG * pcbWritten) { SCODE error = S_OK; ULONG cbWritten = cb; ULARGE_INTEGER ularge_integer; if (pcbWritten) *pcbWritten = 0L; if (cbWritten + m_pos > m_pData->cb) { ULISet32( ularge_integer, m_pos+cbWritten ); error = SetSize(ularge_integer); if (error != S_OK) return error; } // BUGBUG - size_t limit memcpy(m_pData->buf + m_pos,pb,(size_t) cbWritten); m_pos += cbWritten; if (pcbWritten != NULL) *pcbWritten = cbWritten; return error; } STDMETHODIMP CMemStm::Seek(LARGE_INTEGER dlibMoveIN, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition) { SCODE error = S_OK; LONG dlibMove = dlibMoveIN.LowPart ; ULONG cbNewPos = dlibMove; if (plibNewPosition != NULL) { ULISet32(*plibNewPosition, m_pos); } switch(dwOrigin) { case STREAM_SEEK_SET: if (dlibMove >= 0) m_pos = dlibMove; else error = E_FAIL; break; case STREAM_SEEK_CUR: if (!(dlibMove < 0 && ((ULONG) -dlibMove) > m_pos)) m_pos += dlibMove; else error = E_FAIL; break; case STREAM_SEEK_END: if (!(dlibMove < 0 && ((ULONG) -dlibMove) > m_pData->cb)) m_pos = m_pData->cb + dlibMove; else error = E_FAIL; break; default: error = E_FAIL; } if (plibNewPosition != NULL) ULISet32(*plibNewPosition, m_pos); return error; } STDMETHODIMP CMemStm::SetSize(ULARGE_INTEGER cb) { HANDLE hMemNew; struct MEMSTM * pDataNew; if (m_pData->cb == cb.LowPart) return S_OK; if (GlobalUnlock(m_hMem) != 0) return E_FAIL; hMemNew = GlobalReAlloc(m_hMem,sizeof(MEMSTM) - sizeof(m_pData->buf) + cb.LowPart,GMEM_DDESHARE | GMEM_MOVEABLE); if (hMemNew == NULL) { GlobalLock(m_hMem); return E_OUTOFMEMORY; } pDataNew = (MEMSTM *) GlobalLock(hMemNew); if (pDataNew == NULL) // Completely hosed return E_FAIL; m_hMem = hMemNew; pDataNew->cb = cb.LowPart; m_pData = pDataNew; return S_OK; } STDMETHODIMP CMemStm::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) { SCODE hRslt; ULONG cbWritten = 0; if (!pstm) return E_FAIL; // write our data into the stream. hRslt = pstm->Write(m_pData->buf, cb.LowPart, &cbWritten); pcbRead->LowPart = cb.LowPart; pcbRead->HighPart = 0; pcbWritten->LowPart = cbWritten; pcbWritten->HighPart = 0; return hRslt; } STDMETHODIMP CMemStm::Commit(DWORD grfCommitFlags) { return E_FAIL; } STDMETHODIMP CMemStm::Revert(void) { return E_FAIL; } STDMETHODIMP CMemStm::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return E_FAIL; } STDMETHODIMP CMemStm::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return E_FAIL; } STDMETHODIMP CMemStm::Stat(STATSTG *pstatstg, DWORD statflag) { pstatstg->pwcsName = NULL; pstatstg->type = 0; pstatstg->cbSize.HighPart = 0; pstatstg->cbSize.LowPart = m_pData->cb; pstatstg->mtime.dwLowDateTime = 0; pstatstg->mtime.dwHighDateTime = 0; pstatstg->ctime.dwLowDateTime = 0; pstatstg->ctime.dwHighDateTime = 0; pstatstg->atime.dwLowDateTime = 0; pstatstg->atime.dwHighDateTime = 0; pstatstg->grfMode = 0; pstatstg->grfLocksSupported = 0; pstatstg->clsid = CLSID_NULL; pstatstg->grfStateBits = 0; #ifdef CAIROLE_DOWNLEVEL pstatstg->reserved = 0; #else pstatstg->dwStgFmt = 0; #endif return S_OK; } STDMETHODIMP CMemStm::Clone(IStream * *ppstm) { SCODE hRslt = E_FAIL; // create a new stream IStream *pIStm = CreateMemStm(m_pData->cb, NULL); if (pIStm) { // copy data to it ULARGE_INTEGER cbRead, cbWritten; ULARGE_INTEGER cb; cb.LowPart = m_pData->cb; cb.HighPart = 0; hRslt = CopyTo(pIStm, cb, &cbRead, &cbWritten); if (hRslt == S_OK) { *ppstm = pIStm; } } return hRslt; } // Create CMemStm. // CMemStm * CMemStm::Create(HANDLE hMem) { CMemStm * pCMemStm; struct MEMSTM * pData; pData = (MEMSTM *) GlobalLock(hMem); if (pData == NULL) return NULL; pCMemStm = new CMemStm; if (pCMemStm == NULL) { GlobalUnlock(hMem); return NULL; } // Initialize CMemStm // pCMemStm->m_hMem = hMem; (pCMemStm->m_pData = pData)->cRef++; pCMemStm->m_refs = 1; return pCMemStm; } // Allocate shared memory and create CMemStm on top of it. // Return pointer to the stream if done, NULL if error. // STDAPI_(LPSTREAM) CreateMemStm(DWORD cb, LPHANDLE phMem) { HANDLE hMem; struct MEMSTM * pData; IStream * pStm; if ( phMem ) *phMem = NULL; // Get shared memory hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, sizeof(MEMSTM) - sizeof(pData->buf) + cb); if (hMem == NULL) return NULL; pData = (MEMSTM *) GlobalLock(hMem); if (pData == NULL) goto FreeMem; pData->cb = cb; // If caller doesn't ask for the memory handle // Release() should free the memory. // pData->cRef = (phMem == NULL) ? 0 : 1; GlobalUnlock(hMem); pStm = CMemStm::Create(hMem); // Create the stream if (pStm == NULL) goto FreeMem; if (phMem) *phMem = hMem; return pStm; FreeMem: GlobalFree(hMem); return NULL; } // Create CMemStm on top of the specified hMem. // Return pointer to the stream if done, NULL if error. // STDAPI_(LPSTREAM) CloneMemStm(HANDLE hMem) { return CMemStm::Create(hMem); // Create the stream } ////////////////////////////////////////////////////////////////////////// // Shared memory ILockBytes implementation // STDMETHODIMP CMemBytes::QueryInterface(REFIID iidInterface, void **ppvObj) { SCODE error = S_OK; *ppvObj = NULL; // Two interfaces supported: IUnknown, ILockBytes if (m_pData != NULL && (IsEqualIID(iidInterface,IID_ILockBytes) || IsEqualIID(iidInterface,IID_IUnknown))) { m_refs++; // A pointer to this object is returned *ppvObj = this; } else if (IsEqualIID(iidInterface,IID_IMarshal)) { *ppvObj = (LPVOID) CMarshalMemBytes::Create(this); if (*ppvObj == NULL) error = E_OUTOFMEMORY; } else { // Not accessible or unsupported interface *ppvObj = NULL; error = E_NOINTERFACE; } return error; } STDMETHODIMP_(ULONG) CMemBytes::AddRef(void) { return ++m_refs; } STDMETHODIMP_(ULONG) CMemBytes::Release(void) { if (--m_refs != 0) // Still used by others return m_refs; // Matches the allocation in CMemBytes::Create(). // if (--m_pData->cRef == 0) { if (m_pData->fDeleteOnRelease) { GlobalFree(m_pData->hGlobal); } GlobalUnlock(m_hMem); GlobalFree(m_hMem); } else GlobalUnlock(m_hMem); delete this; // Free storage return 0; } STDMETHODIMP CMemBytes::ReadAt(ULARGE_INTEGER ulOffset, void HUGEP* pb, ULONG cb, ULONG * pcbRead) { SCODE error = S_OK; ULONG cbRead = cb; if (pcbRead) *pcbRead = 0L; if (cbRead + ulOffset.LowPart > m_pData->cb) { if (ulOffset.LowPart > m_pData->cb) cbRead = 0; else cbRead = m_pData->cb - ulOffset.LowPart; error = E_FAIL; } char HUGEP* pGlobal = (char HUGEP*) GlobalLock (m_pData->hGlobal); if (NULL==pGlobal) { return STG_E_READFAULT; } memcpy(pb, pGlobal + ulOffset.LowPart, cbRead); GlobalUnlock (m_pData->hGlobal); if (pcbRead != NULL) *pcbRead = cbRead; return error; } STDMETHODIMP CMemBytes::WriteAt(ULARGE_INTEGER ulOffset, void const HUGEP* pb, ULONG cb, ULONG * pcbWritten) { SCODE error = S_OK; ULONG cbWritten = cb; char HUGEP* pGlobal; 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 != S_OK) return error; } pGlobal = (char HUGEP*) GlobalLock (m_pData->hGlobal); if (NULL==pGlobal) { return STG_E_WRITEFAULT; } memcpy(pGlobal + ulOffset.LowPart, pb, cbWritten); GlobalUnlock (m_pData->hGlobal); if (pcbWritten != NULL) *pcbWritten = cbWritten; return error; } STDMETHODIMP CMemBytes::Flush(void) { return S_OK; } STDMETHODIMP CMemBytes::SetSize(ULARGE_INTEGER cb) { HANDLE hMemNew; if (m_pData->cb == cb.LowPart) return S_OK; hMemNew = GlobalReAlloc(m_pData->hGlobal, cb.LowPart, GMEM_DDESHARE | GMEM_MOVEABLE); if (hMemNew == NULL) return E_OUTOFMEMORY; m_pData->hGlobal = hMemNew; m_pData->cb = cb.LowPart; return S_OK; } STDMETHODIMP CMemBytes::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return S_OK; } STDMETHODIMP CMemBytes::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return S_OK; } STDMETHODIMP CMemBytes::Stat(STATSTG *pstatstg, DWORD statflag) { pstatstg->pwcsName = NULL; pstatstg->type = 0; pstatstg->cbSize.HighPart = 0; pstatstg->cbSize.LowPart = m_pData->cb; pstatstg->mtime.dwLowDateTime = 0; pstatstg->mtime.dwHighDateTime = 0; pstatstg->ctime.dwLowDateTime = 0; pstatstg->ctime.dwHighDateTime = 0; pstatstg->atime.dwLowDateTime = 0; pstatstg->atime.dwHighDateTime = 0; pstatstg->grfMode = 0; pstatstg->grfLocksSupported = 0; pstatstg->clsid = CLSID_NULL; pstatstg->grfStateBits = 0; #ifdef CAIROLE_DOWNLEVEL pstatstg->reserved = 0; #else pstatstg->dwStgFmt = 0; #endif return S_OK; } // Create CMemBytes. // CMemBytes * CMemBytes::Create(HANDLE hMem) { CMemBytes * pCMemBytes; struct MEMBYTES * pData; pData = (MEMBYTES *) GlobalLock(hMem); if (pData == NULL) return NULL; pCMemBytes = new CMemBytes; if (pCMemBytes == NULL) { GlobalUnlock(hMem); return NULL; } // Initialize CMemBytes // pCMemBytes->m_dwSig = LOCKBYTE_SIG; pCMemBytes->m_hMem = hMem; (pCMemBytes->m_pData = pData)->cRef++; pCMemBytes->m_refs = 1; return pCMemBytes; } STDAPI_(LPLOCKBYTES) CreateMemLockBytes(DWORD cb, LPHANDLE phMem) { HANDLE h; LPLOCKBYTES plb = NULL; if (phMem) *phMem = NULL; h = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, cb); if (NULL==h) return NULL; if (CreateILockBytesOnHGlobal (h, phMem==NULL, &plb) != S_OK) return NULL; if (phMem) GetHGlobalFromILockBytes (plb, phMem); return plb; } // Create CMemBytes on top of the specified hMem. // Return pointer to the stream if done, NULL if error. // STDAPI_(LPLOCKBYTES) CloneMemLockbytes(HANDLE hMem) { return CMemBytes::Create(hMem); // Create the lockbytes } // CMemStm object's IMarshal implementation // STDMETHODIMP CMarshalMemStm::QueryInterface(REFIID iidInterface, void * * ppvObj) { SCODE error = S_OK; *ppvObj = NULL; // Two interfaces supported: IUnknown, IMarshal if (IsEqualIID(iidInterface,IID_IMarshal) || IsEqualIID(iidInterface,IID_IUnknown)) { m_refs++; // A pointer to this object is returned *ppvObj = this; } else { // Not accessible or unsupported interface *ppvObj = NULL; error = E_NOINTERFACE; } return error; } STDMETHODIMP_(ULONG) CMarshalMemStm::AddRef(void) { return ++m_refs; } STDMETHODIMP_(ULONG) CMarshalMemStm::Release(void) { if (--m_refs != 0) // Still used by others return m_refs; if (m_pMemStm) m_pMemStm->Release(); delete this; // Free storage return 0; } // Returns the clsid of the object that created this CMarshalMemStm. // STDMETHODIMP CMarshalMemStm::GetUnmarshalClass(REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, CLSID * pCid) { *pCid = m_clsid; return S_OK; } STDMETHODIMP CMarshalMemStm::GetMarshalSizeMax(REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, DWORD * pSize) { *pSize = sizeof(m_pMemStm->m_hMem); return S_OK; } STDMETHODIMP CMarshalMemStm::MarshalInterface(IStream * pStm, REFIID riid, void * pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags) { if (m_pMemStm == NULL) return E_FAIL; if ((!IsEqualIID(riid,IID_IStream) && !IsEqualIID(riid,IID_IUnknown)) || pv != m_pMemStm) return E_INVALIDARG; // increase ref count on hglobal (ReleaseMarshalData has -- to match) SCODE error; error = pStm->Write(&m_pMemStm->m_hMem,sizeof(m_pMemStm->m_hMem), NULL); if (error == S_OK) m_pMemStm->m_pData->cRef++; return error; } STDMETHODIMP CMarshalMemStm::UnmarshalInterface(IStream * pStm, REFIID riid, void * * ppv) { SCODE error; HANDLE hMem; *ppv = NULL; if (!IsEqualIID(riid,IID_IStream) && !IsEqualIID(riid,IID_IUnknown)) return E_INVALIDARG; error = pStm->Read(&hMem,sizeof(hMem),NULL); if (error != S_OK) return error; if (m_pMemStm != NULL) { if (hMem != m_pMemStm->m_hMem) return E_FAIL; } else { m_pMemStm = (CMemStm *) CloneMemStm(hMem); if (m_pMemStm == NULL) return E_OUTOFMEMORY; } m_pMemStm->AddRef(); *ppv = (LPVOID) m_pMemStm; return S_OK; } STDMETHODIMP CMarshalMemStm::ReleaseMarshalData(IStream * pStm) { // reduce ref count on hglobal (matches that done in MarshalInterface) SCODE error; MEMSTM * pData; HANDLE hMem; error = pStm->Read(&hMem,sizeof(hMem),NULL); if (error != S_OK) return error; pData = (MEMSTM *) GlobalLock(hMem); if (pData == NULL) return E_FAIL; if (--pData->cRef == 0) { GlobalUnlock(hMem); GlobalFree(hMem); } else // still used by one or more CMemStm in one or more processes GlobalUnlock(hMem); return S_OK; } STDMETHODIMP CMarshalMemStm::DisconnectObject(DWORD dwReserved) { return S_OK; } CMarshalMemStm * CMarshalMemStm::Create(CMemStm * pMemStm) { CMarshalMemStm * pMMS = new CMarshalMemStm; if (pMMS == NULL) return NULL; if (pMemStm != NULL) { pMMS->m_pMemStm = pMemStm; pMMS->m_pMemStm->AddRef(); } pMMS->m_clsid = CLSID_StdMemStm; pMMS->m_refs = 1; return pMMS; } STDAPI_(IUnknown *) CMemStmUnMarshal(void) { return CMarshalMemStm::Create(NULL); } // CMemBytes object's IMarshal implementation // STDMETHODIMP CMarshalMemBytes::QueryInterface(REFIID iidInterface, void * * ppvObj) { SCODE error = S_OK; *ppvObj = NULL; // Two interfaces supported: IUnknown, IMarshal if (IsEqualIID(iidInterface,IID_IMarshal) || IsEqualIID(iidInterface,IID_IUnknown)) { m_refs++; // A pointer to this object is returned *ppvObj = this; } else { // Not accessible or unsupported interface *ppvObj = NULL; error = E_NOINTERFACE; } return error; } STDMETHODIMP_(ULONG) CMarshalMemBytes::AddRef(void) { return ++m_refs; } STDMETHODIMP_(ULONG) CMarshalMemBytes::Release(void) { if (--m_refs != 0) // Still used by others return m_refs; if (m_pMemBytes != NULL) m_pMemBytes->Release(); delete this; // Free storage return 0; } // Returns the clsid of the object that created this CMarshalMemBytes. // STDMETHODIMP CMarshalMemBytes::GetUnmarshalClass(REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, CLSID * pCid) { *pCid = m_clsid; return S_OK; } STDMETHODIMP CMarshalMemBytes::GetMarshalSizeMax(REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags, DWORD * pSize) { *pSize = sizeof(m_pMemBytes->m_hMem); return S_OK; } STDMETHODIMP CMarshalMemBytes::MarshalInterface(IStream * pStm, REFIID riid, void * pv, DWORD dwDestContext, LPVOID pvDestContext, DWORD mshlflags) { if (m_pMemBytes == NULL) return E_FAIL; if ((!IsEqualIID(riid,IID_ILockBytes) && !IsEqualIID(riid,IID_IUnknown)) || pv != m_pMemBytes) return E_INVALIDARG; // increase ref count on hglobal (ReleaseMarshalData has -- to match) SCODE error; error = pStm->Write(&m_pMemBytes->m_hMem,sizeof(m_pMemBytes->m_hMem),NULL); if (error == S_OK) m_pMemBytes->m_pData->cRef++; return error; } STDMETHODIMP CMarshalMemBytes::UnmarshalInterface(IStream * pStm, REFIID riid, void * * ppv) { HANDLE hMem; *ppv = NULL; if (!IsEqualIID(riid,IID_ILockBytes) && !IsEqualIID(riid,IID_IUnknown)) return E_INVALIDARG; SCODE error = pStm->Read(&hMem,sizeof(hMem),NULL); if (error != S_OK) return error; if (m_pMemBytes != NULL) { if (hMem != m_pMemBytes->m_hMem) return E_FAIL; } else { m_pMemBytes = (CMemBytes *) CloneMemLockbytes(hMem); if (m_pMemBytes == NULL) return E_OUTOFMEMORY; } m_pMemBytes->AddRef(); *ppv = (LPVOID) m_pMemBytes; return S_OK; } STDMETHODIMP CMarshalMemBytes::ReleaseMarshalData(IStream * pStm) { // reduce ref count on hglobal (matches that done in MarshalInterface) MEMBYTES *pData; HANDLE hMem; SCODE error = pStm->Read(&hMem,sizeof(hMem),NULL); if (error != S_OK) return error; pData = (MEMBYTES *) GlobalLock(hMem); if (pData == NULL) return E_FAIL; if (--pData->cRef == 0) { GlobalUnlock(hMem); GlobalFree(hMem); } else { // still used by one or more CMemStm in one or more processes GlobalUnlock(hMem); } return S_OK; } STDMETHODIMP CMarshalMemBytes::DisconnectObject(DWORD dwReserved) { return S_OK; } CMarshalMemBytes *CMarshalMemBytes::Create(CMemBytes *pMemBytes) { CMarshalMemBytes *pMMB = new CMarshalMemBytes; if (pMMB == NULL) return NULL; if (pMemBytes != NULL) { pMMB->m_pMemBytes = pMemBytes; pMMB->m_pMemBytes->AddRef(); } pMMB->m_clsid = CLSID_StdMemBytes; pMMB->m_refs = 1; return pMMB; } STDAPI_(IUnknown *) CMemBytesUnMarshal(void) { return CMarshalMemBytes::Create(NULL); }