Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1066 lines
28 KiB

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1993
//
// File: stream.c
//
// This file contains some of the stream support code that is used by
// the shell. It also contains the shells implementation of a memory
// stream that is used by the cabinet to allow views to be serialized.
//
// History:
// 08-20-93 KurtE Added header block and memory stream.
//
//---------------------------------------------------------------------------
#include "shellprv.h"
#pragma hdrstop
// BUGBUG: turn this on after we ship and I am not so scared about breaking things
// this version is smaller and has about half the allocs
#ifdef BETTER_STRONGER_FASTER
typedef struct {
IStream stm; // Base class
UINT cRef; // Reference count
LPBYTE pBuf; // Buffer pointer
UINT cbAlloc; // The allocated size of the buffer
UINT cbData; // The used size of the buffer
UINT iSeek; // Where we are in the buffer.
// Extra variables that are used for loading and saving to ini files.
HKEY hkey; // Key for writing to registry.
TCHAR szValue[1]; // for reg stream
} CMemStream;
CMemStream *CreateMemStreamEx(LPBYTE pInit, UINT cbInit, LPCTSTR pszValue);
STDMETHODIMP CMemStream_QueryInterface(IStream *pstm, REFIID riid, void **ppvObj)
{
CMemStream *this = IToClass(CMemStream, stm, pstm);
if (IsEqualIID(riid, &IID_IStream) || IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj=this;
this->cRef++;
return S_OK;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) CMemStream_AddRef(IStream *pstm)
{
CMemStream *this = IToClass(CMemStream, stm, pstm);
this->cRef++;
return this->cRef;
}
BOOL WriteToReg(CMemStream *this)
{
return RegSetValueEx(this->hkey,
this->szValue[0] ? this->szValue : NULL, 0, REG_BINARY,
this->cbData ? this->pBuf : szNULL, this->cbData) == ERROR_SUCCESS;
}
STDMETHODIMP_(ULONG) CMemStream_Release(IStream *pstm)
{
CMemStream *this = IToClass(CMemStream, stm, pstm);
this->cRef--;
if (this->cRef > 0)
return this->cRef;
// If this is backed up by the registry serialize the data
if (this->hkey)
{
// Backed by the registry.
// Write and cleanup.
WriteToReg(this);
RegCloseKey(this->hkey);
}
// Free the data buffer that is allocated to the stream
if (this->pBuf)
LocalFree(this->pBuf);
LocalFree((HLOCAL)this);
return 0;
}
STDMETHODIMP CMemStream_Read(IStream *pstm, void *pv, ULONG cb, ULONG *pcbRead)
{
CMemStream *this = IToClass(CMemStream, stm, pstm);
Assert(pstm);
Assert(pv);
// I guess a null read is ok.
if (!cb)
{
if (pcbRead != NULL)
*pcbRead = 0;
return S_OK;
}
if (this->iSeek >= this->cbData)
{
if (pcbRead != NULL)
*pcbRead = 0; // nothing read
}
else
{
if ((this->iSeek + cb) > this->cbData)
cb = this->cbData - this->iSeek;
// Now Copy the memory
Assert(this->pBuf);
CopyMemory(pv, this->pBuf + this->iSeek, cb);
this->iSeek += (UINT)cb;
if (pcbRead != NULL)
*pcbRead = cb;
}
return S_OK;
}
LPBYTE CMemStream_GrowBuffer(CMemStream *this, ULONG cbNew)
{
if (this->pBuf == NULL)
{
this->pBuf = LocalAlloc(LPTR, cbNew);
}
else
{
LPBYTE pTemp = LocalReAlloc(this->pBuf, cbNew, LMEM_MOVEABLE | LMEM_ZEROINIT);
if (pTemp)
{
this->pBuf = pTemp;
}
else
{
DebugMsg(DM_ERROR, TEXT("stream buffer realloc failed"));
return NULL;
}
}
if (this->pBuf)
this->cbAlloc = cbNew;
return this->pBuf;
}
#define SIZEINCR 0x1000
STDMETHODIMP CMemStream_Write(IStream *pstm, void const *pv, ULONG cb, ULONG *pcbWritten)
{
CMemStream *this = IToClass(CMemStream, stm, pstm);
// I guess a null write is ok.
if (!cb)
{
if (pcbWritten != NULL)
*pcbWritten = 0;
return S_OK;
}
// See if the data will fit into our current buffer
if ((this->iSeek + cb) > this->cbAlloc)
{
// enlarge the buffer
// Give it a little slop to avoid a lot of reallocs.
if (CMemStream_GrowBuffer(this, this->iSeek + (UINT)cb + SIZEINCR) == NULL)
return STG_E_INSUFFICIENTMEMORY;
}
Assert(this->pBuf);
// See if we need to fill the area between the data size and
// the seek position
if (this->iSeek > this->cbData)
{
ZeroMemory(this->pBuf + this->cbData, this->iSeek - this->cbData);
}
CopyMemory(this->pBuf + this->iSeek, pv, cb);
this->iSeek += (UINT)cb;
if (this->iSeek > this->cbData)
this->cbData = this->iSeek;
if (pcbWritten != NULL)
*pcbWritten = cb;
return S_OK;
}
STDMETHODIMP CMemStream_Seek(IStream *pstm, LARGE_INTEGER dlibMove,
DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
CMemStream *this = IToClass(CMemStream, stm, pstm);
LONG lNewSeek;
// Note: curently not testing for error conditions for number wrap...
switch (dwOrigin)
{
case STREAM_SEEK_SET:
lNewSeek = (LONG)dlibMove.LowPart;
break;
case STREAM_SEEK_CUR:
lNewSeek = (LONG)this->iSeek + (LONG)dlibMove.LowPart;
break;
case STREAM_SEEK_END:
lNewSeek = (LONG)this->cbData + (LONG)dlibMove.LowPart;
break;
default:
return STG_E_INVALIDPARAMETER;
}
if (lNewSeek < 0)
return STG_E_INVALIDFUNCTION;
this->iSeek = (UINT)lNewSeek;
if (plibNewPosition != NULL)
{
plibNewPosition->LowPart = (DWORD)lNewSeek;
plibNewPosition->HighPart = 0;
}
return S_OK;
}
STDMETHODIMP CMemStream_SetSize(IStream *pstm, ULARGE_INTEGER libNewSize)
{
CMemStream *this = IToClass(CMemStream, stm, pstm);
UINT cbNew = (UINT)libNewSize.LowPart;
Assert(pstm);
// See if the data will fit into our current buffer
if (cbNew > this->cbData)
{
// See if we have to Enlarge the buffer.
if (cbNew > this->cbAlloc)
{
// enlarge the buffer - Does not check wrap...
// Give it a little slop to avoid a lot of reallocs.
if (CMemStream_GrowBuffer(this, cbNew) == NULL)
return STG_E_INSUFFICIENTMEMORY;
}
// Now fill some memory
ZeroMemory(this->pBuf + this->cbData, cbNew - this->cbData);
}
// Save away the new size.
this->cbData = cbNew;
return S_OK;
}
STDMETHODIMP CMemStream_CopyTo(IStream *pstm, IStream *pstmTo,
ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{
CMemStream *this = IToClass(CMemStream, stm, pstm);
HRESULT hres = S_OK;
UINT cbRead = this->cbData - this->iSeek;
UINT cbWritten = 0;
Assert(pstm);
if (cb.HighPart == 0 && cb.LowPart < cbRead)
{
cbRead = cb.LowPart;
}
if (cbRead > 0)
{
hres = pstmTo->lpVtbl->Write(pstmTo, this->pBuf + this->iSeek, cbRead, &cbWritten);
this->iSeek += cbRead;
}
if (pcbRead)
{
pcbRead->LowPart = cbRead;
pcbRead->HighPart = 0;
}
if (pcbWritten)
{
pcbWritten->LowPart = cbWritten;
pcbWritten->HighPart = 0;
}
return hres;
}
STDMETHODIMP CMemStream_Commit(IStream *pstm, DWORD grfCommitFlags)
{
return E_NOTIMPL;
}
STDMETHODIMP CMemStream_Revert(IStream *pstm)
{
return E_NOTIMPL;
}
STDMETHODIMP CMemStream_LockRegion(IStream *pstm, ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb, DWORD dwLockType)
{
return E_NOTIMPL;
}
STDMETHODIMP CMemStream_UnlockRegion(IStream *pstm, ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb, DWORD dwLockType)
{
return E_NOTIMPL;
}
STDMETHODIMP CMemStream_Stat(IStream *pstm, STATSTG *pstatstg, DWORD grfStatFlag)
{
return E_NOTIMPL;
}
STDMETHODIMP CMemStream_Clone(IStream *pstm, IStream **ppstm)
{
return E_NOTIMPL;
}
#pragma data_seg(".text", "CODE")
IStreamVtbl c_CMemStreamVtbl =
{
CMemStream_QueryInterface,
CMemStream_AddRef,
CMemStream_Release,
CMemStream_Read,
CMemStream_Write,
CMemStream_Seek,
CMemStream_SetSize,
CMemStream_CopyTo,
CMemStream_Commit,
CMemStream_Revert,
CMemStream_LockRegion,
CMemStream_UnlockRegion,
CMemStream_Stat
};
#pragma data_seg()
//----------------------------------------------------------------------------
// Open a stream to the reg file given an open key.
// NB pszValue can be NULL.
IStream * WINAPI OpenRegStream(HKEY hkey, LPCTSTR pszSubkey, LPCTSTR pszValue, DWORD grfMode)
{
CMemStream *this; // In bed with class...
// Null keys are illegal.
if (!hkey || !pszSubkey)
{
DebugMsg(DM_ERROR, TEXT("s.ors: Invalid key or subkey."));
return NULL;
}
this = CreateMemStreamEx(NULL, 0, pszValue);
if (!this)
return NULL; // Failed to allocate space
// If this stream is one the user mentioned as wanting to write to
// we need to save away the regkey and value.
if ((grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_READ)
{
// Store away the key.
if (RegCreateKey(hkey, pszSubkey, &this->hkey) != ERROR_SUCCESS)
{
DebugMsg(DM_ERROR, TEXT("s.ors: Unable to create key."));
CMemStream_Release(&this->stm);
return NULL;
}
}
// Now see if we need to initialize the stream.
if ((grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_WRITE)
{
BOOL fCloseKey = FALSE;
// Yep.
// Did we open the key already?
if (!this->hkey)
{
// Nope, do it now. It's not a problem if the key doesn't open. We
// just leave the stream empty.
if (RegOpenKey(hkey, pszSubkey, &this->hkey) == ERROR_SUCCESS)
fCloseKey = TRUE;
}
// Key should be open now, init the stream.
if (this->hkey)
{
DWORD dwType;
UINT cbData;
if ((RegQueryValueEx(this->hkey, pszValue, NULL, &dwType, NULL, &cbData) == ERROR_SUCCESS) && cbData)
{
Assert(dwType == REG_BINARY);
if (CMemStream_GrowBuffer(this, cbData) != NULL)
{
Assert(this->cbAlloc >= cbData);
// Get the data.
RegQueryValueEx(this->hkey, pszValue, NULL, &dwType, this->pBuf, &cbData);
Assert(this->cbAlloc >= cbData);
this->cbData = cbData;
}
else
{
DebugMsg(DM_ERROR, TEXT("s.ors: Unable to initialise stream to registry."));
CMemStream_Release(&this->stm);
return NULL;
}
}
// Close the key if we have to. Leaving it open implies we will be writing back
// to the registry later and the close will be done during the release.
if (fCloseKey)
{
RegCloseKey(this->hkey);
this->hkey = NULL;
}
}
}
return &this->stm;
}
CMemStream *CreateMemStreamEx(LPBYTE pInit, UINT cbInit, LPCTSTR pszValue)
{
UINT cbAlloc = SIZEOF(CMemStream) + (pszValue ? lstrlen(pszValue) * SIZEOF(TCHAR) : 0);
CMemStream *this = (CMemStream *)LocalAlloc(LPTR, cbAlloc);
if (this)
{
this->stm.lpVtbl = &c_CMemStreamVtbl;
this->cRef = 1;
// this->pBuf = NULL;
// this->cbAlloc = 0;
// this->cbData = 0;
// this->iSeek = 0;
// See if there is some initial data we should map in here.
if ((pInit != NULL) && (cbInit > 0))
{
if (CMemStream_GrowBuffer(this, cbInit) == NULL)
{
// Could not allocate buffer!
LocalFree((HLOCAL)this);
return NULL;
}
this->cbData = cbInit;
CopyMemory(this->pBuf, pInit, cbInit);
}
if (pszValue)
lstrcpy(this->szValue, pszValue);
return this;
}
return NULL;
}
IStream * WINAPI CreateMemStream(LPBYTE pInit, UINT cbInit)
{
CMemStream *this = CreateMemStreamEx(pInit, cbInit, NULL);
if (this)
return &this->stm;
return NULL;
}
#else // !BETTER_STRONGER_FASTER
//
// Memory stream implemention. This also includes a buffered
// memory file stream that writes to the registry.
//
//
//
// Class definition
//
typedef struct {
IStream stm; // Base class
UINT cRef; // Reference count
LPBYTE lpBuf; // Buffer pointer
UINT cbAlloc; // The allocated size of the buffer
UINT cbData; // The used size of the buffer
UINT iSeek; // Where we are in the buffer.
// Extra variables that are used for loading and saving to ini files.
DWORD grfMode; // How the stream was opend.
// Extra variables that are used for loading and saving to ini files.
HKEY hkey; // Key for writing to registry.
LPTSTR pszValue; // Value for writing to the registry.
} CMemStream, * PMEMSTREAM;
//
// Member: CMemStream::QueryInterface
//
HRESULT STDMETHODCALLTYPE CMemStream_QueryInterface(IStream * pstm, REFIID riid, LPVOID * ppvObj)
{
CMemStream * this = IToClassN(CMemStream, stm, pstm);
if (IsEqualIID(riid, &IID_IStream) || IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj=this;
this->cRef++;
return NOERROR;
}
*ppvObj = NULL;
return ResultFromScode(E_NOINTERFACE);
}
//
// Member: CMemStream::AddRef
//
ULONG STDMETHODCALLTYPE CMemStream_AddRef(IStream * pstm)
{
CMemStream * this=IToClassN(CMemStream, stm, pstm);
this->cRef++;
return this->cRef;
}
//----------------------------------------------------------------------------
BOOL WriteToReg(CMemStream *this)
{
const LARGE_INTEGER liOffset = {0,0};
ULARGE_INTEGER liSize;
BOOL fStatus = FALSE;
LPBYTE pData;
UINT cbData;
// How much data? I'm assuming that typically, data written the
// registry will be fairly small - small enough to copy in one go
// anyway.
this->stm.lpVtbl->Seek(&this->stm, liOffset, STREAM_SEEK_END, &liSize);
// Limit ourselves to 32Mb.
cbData = liSize.LowPart;
// Is there anything to write?
if (cbData)
{
// Yep.
pData = Alloc(cbData);
if (pData)
{
// Copy the data over.
this->stm.lpVtbl->Seek(&this->stm, liOffset, STREAM_SEEK_SET, NULL);
this->stm.lpVtbl->Read(&this->stm, pData, cbData, NULL);
RegSetValueEx(this->hkey, this->pszValue, 0, REG_BINARY, pData, cbData);
Free(pData);
fStatus = TRUE;
}
else
{
DebugMsg(DM_ERROR, TEXT("s.wtr: Unable to allocate buffer for writing to registry."));
}
}
else
{
// Nope.
RegSetValueEx(this->hkey, this->pszValue, 0, REG_BINARY, (LPBYTE)szNULL, 0);
fStatus = TRUE;
}
return fStatus;
}
//
// Member: CMemStream::Release
//
ULONG STDMETHODCALLTYPE CMemStream_Release(IStream * pstm)
{
CMemStream * this=IToClassN(CMemStream, stm, pstm);
this->cRef--;
if (this->cRef > 0)
return this->cRef;
// If this is backed up by the registry serialize the data
if (this->hkey)
{
// Backed by the registry.
// Write and cleanup.
WriteToReg(this);
RegCloseKey(this->hkey);
this->hkey = NULL;
}
if (this->pszValue)
Free(this->pszValue);
// Free the data buffer that is allocated to the stream
if (this->lpBuf != 0)
Free(this->lpBuf);
#ifdef DEBUG
this->stm.lpVtbl = NULL;
#endif
LocalFree((HLOCAL)this);
return 0;
}
//
// Member: CMemStream::Read
//
STDMETHODIMP CMemStream_Read(IStream * pstm, VOID *pv, ULONG cb, ULONG *pcbRead)
{
CMemStream * this=IToClassN(CMemStream, stm, pstm);
Assert(pstm);
Assert(pv);
// I guess a null read is ok.
if (!cb)
{
if (pcbRead != NULL)
*pcbRead = 0;
return NOERROR;
}
if (this->iSeek >= this->cbData)
{
if (pcbRead != NULL)
*pcbRead = 0; // nothing read
}
else
{
if ((this->iSeek + cb) > this->cbData)
cb = this->cbData - this->iSeek;
// Now Copy the memory
Assert(this->lpBuf);
hmemcpy(pv, this->lpBuf + this->iSeek, cb);
this->iSeek += (UINT)cb;
if (pcbRead != NULL)
*pcbRead = cb;
}
return NOERROR;
}
//
// Member: CMemStream::Write
//
#define SIZEINCR 0x1000
STDMETHODIMP CMemStream_Write(IStream * pstm, VOID const *pv, ULONG cb, ULONG *pcbWritten)
{
CMemStream * this=IToClassN(CMemStream, stm, pstm);
Assert(pstm);
Assert(pv);
// I guess a null write is ok.
if (!cb)
{
if (pcbWritten != NULL)
*pcbWritten = 0;
return NOERROR;
}
// See if the data will fit into our current buffer
if ((this->iSeek + cb) > this->cbAlloc)
{
// enlarge the buffer - Does not check wrap, as soon this will
// be 32 bits and that is a lot of room...
// Give it a little slop to avoid a lot of reallocs.
UINT cbNew = this->iSeek + (UINT)cb + SIZEINCR;
LPBYTE lpNew = ReAlloc(this->lpBuf, cbNew);
if (lpNew == NULL)
return ResultFromScode(STG_E_INSUFFICIENTMEMORY);
this->lpBuf = lpNew;
this->cbAlloc = cbNew;
}
Assert(this->lpBuf);
// See if we need to fill the area between the data size and
// the seek position
if (this->iSeek > this->cbData)
{
_fmemset(this->lpBuf + this->cbData, TEXT('\0'), this->iSeek - this->cbData);
}
hmemcpy(this->lpBuf + this->iSeek, pv, cb);
this->iSeek += (UINT)cb;
if (this->iSeek > this->cbData)
this->cbData = this->iSeek;
if (pcbWritten != NULL)
*pcbWritten = cb;
return NOERROR;
}
//
// Member: CMemStream::Seek
//
STDMETHODIMP CMemStream_Seek(IStream * pstm, LARGE_INTEGER dlibMove,
DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
LONG lNewSeek;
CMemStream * this=IToClassN(CMemStream, stm, pstm);
// Note: curently not testing for error conditions for number wrap...
switch (dwOrigin)
{
case STREAM_SEEK_SET:
lNewSeek = (LONG)dlibMove.LowPart;
break;
case STREAM_SEEK_CUR:
lNewSeek = (LONG)this->iSeek + (LONG)dlibMove.LowPart;
break;
case STREAM_SEEK_END:
lNewSeek = (LONG)this->cbData + (LONG)dlibMove.LowPart;
break;
default:
return ResultFromScode(STG_E_INVALIDPARAMETER);
}
if (lNewSeek < 0)
return ResultFromScode(STG_E_INVALIDFUNCTION);
this->iSeek = (UINT)lNewSeek;
if (plibNewPosition != NULL)
{
plibNewPosition->LowPart = (DWORD)lNewSeek;
plibNewPosition->HighPart = 0;
}
return NOERROR;
}
//
// Member: CMemStream::SetSize
//
STDMETHODIMP CMemStream_SetSize(IStream * pstm, ULARGE_INTEGER libNewSize)
{
UINT cbNew;
CMemStream * this=IToClassN(CMemStream, stm, pstm);
Assert(pstm);
cbNew = (UINT)libNewSize.LowPart;
// See if the data will fit into our current buffer
if (cbNew > this->cbData)
{
// See if we have to Enlarge the buffer.
if (cbNew > this->cbAlloc)
{
// enlarge the buffer - Does not check wrap...
// Give it a little slop to avoid a lot of reallocs.
LPBYTE lpNew = ReAlloc(this->lpBuf, cbNew);
if (lpNew == NULL)
return ResultFromScode(STG_E_INSUFFICIENTMEMORY);
this->lpBuf = lpNew;
this->cbAlloc = cbNew;
}
// Now fill some memory
_fmemset(this->lpBuf + this->cbData, TEXT('\0'), cbNew - this->cbData);
}
// Save away the new size.
this->cbData = cbNew;
return NOERROR;
}
//
// Member: CMemStream::CopyTo
//
STDMETHODIMP CMemStream_CopyTo(IStream * pstm, IStream *pstmTo,
ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{
CMemStream * this = IToClassN(CMemStream, stm, pstm);
HRESULT hres = NOERROR;
UINT cbRead = this->cbData - this->iSeek;
UINT cbWritten = 0;
Assert(pstm);
if (cb.HighPart==0 && cb.LowPart<cbRead)
{
cbRead = cb.LowPart;
}
if (cbRead>0)
{
hres = pstmTo->lpVtbl->Write(pstmTo, this->lpBuf + this->iSeek, cbRead, &cbWritten);
this->iSeek += cbRead;
}
if (pcbRead)
{
pcbRead->LowPart = cbRead;
pcbRead->HighPart = 0;
}
if (pcbWritten)
{
pcbWritten->LowPart = cbWritten;
pcbWritten->HighPart = 0;
}
return hres;
}
//
// Member: CMemStream::Commit
//
STDMETHODIMP CMemStream_Commit(IStream * pstm, DWORD grfCommitFlags)
{
//
// Currently not supported
//
return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}
//
// Member: CMemStream::Revert
//
STDMETHODIMP CMemStream_Revert(IStream * pstm)
{
//
// Currently not supported
//
return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}
//
// Member: CMemStream::LockRegion
//
STDMETHODIMP CMemStream_LockRegion(IStream * pstm, ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb, DWORD dwLockType)
{
//
// Currently not supported
//
return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}
//
// Member: CMemStream::UnlockRegion
//
STDMETHODIMP CMemStream_UnlockRegion(IStream * pstm, ULARGE_INTEGER libOffset,
ULARGE_INTEGER cb, DWORD dwLockType)
{
//
// Currently not supported
//
return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}
//
// Member: CMemStream::Stat
//
STDMETHODIMP CMemStream_Stat(IStream * pstm, STATSTG *pstatstg, DWORD grfStatFlag)
{
//
// Currently not supported
//
return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}
//
// Member: CMemStream::Clone
//
STDMETHODIMP CMemStream_Clone(IStream * pstm, IStream **ppstm)
{
//
// Currently not supported
//
return ResultFromScode(OLE_E_ADVISENOTSUPPORTED);
}
//
// The IStream Vtable of CSHMem class
//
// History:
// 08-20-93 KurtE Created
//
#pragma data_seg(".text", "CODE")
IStreamVtbl c_CMemStreamVtbl =
{
CMemStream_QueryInterface,
CMemStream_AddRef,
CMemStream_Release,
CMemStream_Read,
CMemStream_Write,
CMemStream_Seek,
CMemStream_SetSize,
CMemStream_CopyTo,
CMemStream_Commit,
CMemStream_Revert,
CMemStream_LockRegion,
CMemStream_UnlockRegion,
CMemStream_Stat
};
#pragma data_seg()
//----------------------------------------------------------------------------
// Open a stream to the reg file given an open key.
// NB pszValue can be NULL.
LPSTREAM WINAPI OpenRegStream(HKEY hkey, LPCTSTR pszSubkey, LPCTSTR pszValue, DWORD grfMode)
{
LPSTREAM pstm;
CMemStream * this; // In bed with class...
LPBYTE pData;
UINT cbData;
const LARGE_INTEGER liOffset = {0,0};
DWORD dwType;
// Null keys are illegal.
if (!hkey || !pszSubkey)
{
DebugMsg(DM_ERROR, TEXT("s.ors: Invalid key or subkey."));
return NULL;
}
pstm = CreateMemStream(NULL, 0);
if (!pstm)
return NULL; // Failed to allocate space
this = IToClassN(CMemStream, stm, pstm);
this->grfMode = grfMode; // Save away the mode
// If this stream is one the user mentioned as wanting to write to
// we need to save away the regkey and value.
if ((grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) != STGM_READ)
{
// Store away the value if there is one.
if (pszValue)
{
this->pszValue = Alloc((lstrlen(pszValue)+1) * SIZEOF(TCHAR));
if (this->pszValue)
{
lstrcpy(this->pszValue, pszValue);
}
else
{
DebugMsg(DM_ERROR, TEXT("s.ors: Unable to allocate value."));
pstm->lpVtbl->Release(pstm);
return NULL;
}
}
// Store away the key.
if (RegCreateKey(hkey, pszSubkey, &(this->hkey)) != ERROR_SUCCESS)
{
DebugMsg(DM_ERROR, TEXT("s.ors: Unable to create key."));
pstm->lpVtbl->Release(pstm);
return NULL;
}
}
// Now see if we need to initialize the stream.
if ((grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) != STGM_WRITE)
{
BOOL fCloseKey = FALSE;
// Yep.
cbData = 0;
// Did we open the key already?
if (!this->hkey)
{
// Nope, do it now. It's not a problem if the key doesn't open. We
// just leave the stream empty.
if (RegOpenKey(hkey, pszSubkey, &(this->hkey)) == ERROR_SUCCESS)
{
fCloseKey = TRUE;
}
}
// Key should be open now, init the stream.
if (this->hkey)
{
RegQueryValueEx(this->hkey, (LPVOID)pszValue, NULL, &dwType, NULL, &cbData);
pData = Alloc(cbData);
if (pData)
{
// Get the data.
RegQueryValueEx(this->hkey, (LPVOID)pszValue, NULL, &dwType, pData, &cbData);
// Copy the data over.
pstm->lpVtbl->Write(pstm, pData, cbData, NULL);
// Rewind the buffer back to the start.
pstm->lpVtbl->Seek(pstm, liOffset, STREAM_SEEK_SET, NULL);
Free(pData);
}
else
{
DebugMsg(DM_ERROR, TEXT("s.ors: Unable to initialise stream to registry."));
pstm->lpVtbl->Release(pstm);
return NULL;
}
// Close the key if we have to. Leaving it open implies we will be writing back
// to the registry later and the close will be done during the release.
if (fCloseKey)
{
RegCloseKey(this->hkey);
this->hkey = NULL;
}
}
}
// And return the stream.
return pstm;
}
//
//
LPSTREAM WINAPI CreateMemStream(LPBYTE lpbInit, UINT cbInit)
{
IStream * pstm = NULL;
PMEMSTREAM psmstm = (void*)LocalAlloc(LPTR, SIZEOF(CMemStream));
if (psmstm) {
psmstm->stm.lpVtbl = &c_CMemStreamVtbl;
psmstm->cRef = 1;
// psmstm->lpBuf = NULL;
// psmstm->cbAlloc = 0;
// psmstm->cbData = 0;
// psmstm->iSeek = 0;
// See if there is some initial data we should map in here.
if ((lpbInit != NULL) && (cbInit > 0))
{
psmstm->lpBuf = Alloc(cbInit);
if (psmstm->lpBuf == NULL)
{
// Could not allocate buffer!
LocalFree((HLOCAL)psmstm);
return NULL;
}
psmstm->cbAlloc = psmstm->cbData = cbInit;
hmemcpy(psmstm->lpBuf, lpbInit, cbInit);
}
pstm = &psmstm->stm;
};
return pstm;
}
#endif // !BETTER_STRONGER_FASTER