2025-11-28 00:35:46 +09:00

1077 lines
26 KiB
C++

//-----------------------------------------------------------------------------
// Microsoft OLE DB RowsetViewer
// Copyright (C) 1994 - 1999 By Microsoft Corporation.
//
// @doc
//
// @module CSTREAM.CPP
//
//-----------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////
// Includes
//
//////////////////////////////////////////////////////////////////////////////
#include "Headers.h"
///////////////////////////////////////////////////////////////////////////////
// Class CStream
//
///////////////////////////////////////////////////////////////////////////////
CStream::CStream(CMainWindow* pCMainWindow, CMDIChild* pCMDIChild)
: CAsynchBase(eCStream, pCMainWindow, pCMDIChild)
{
//Storage Interfaces
m_pISequentialStream = NULL;
m_pIStream = NULL;
m_pIStorage = NULL;
m_pILockBytes = NULL;
//Data
m_wType = DBTYPE_STR;
}
///////////////////////////////////////////////////////////////////////////////
// ~CStream
//
///////////////////////////////////////////////////////////////////////////////
CStream::~CStream()
{
ReleaseObject(0);
}
/////////////////////////////////////////////////////////////////
// IUnknown** CStream::GetInterfaceAddress
//
/////////////////////////////////////////////////////////////////
IUnknown** CStream::GetInterfaceAddress(REFIID riid)
{
HANDLE_GETINTERFACE(ISequentialStream);
HANDLE_GETINTERFACE(IStream);
HANDLE_GETINTERFACE(IStorage);
HANDLE_GETINTERFACE(ILockBytes);
//Otherwise delegate
return CAsynchBase::GetInterfaceAddress(riid);
}
/////////////////////////////////////////////////////////////////
// CStream::AutoRelease
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::AutoRelease()
{
//Stream
RELEASE_INTERFACE(ISequentialStream);
RELEASE_INTERFACE(IStream);
RELEASE_INTERFACE(IStorage);
RELEASE_INTERFACE(ILockBytes);
//Delegate
return CAsynchBase::AutoRelease();
}
////////////////////////////////////////////////////////////////
// CStream::AutoQI
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::AutoQI(DWORD dwCreateOpts)
{
//Delegate First so we have base interfaces
CAsynchBase::AutoQI(dwCreateOpts);
//[MANDATORY]
if(dwCreateOpts & CREATE_QI_MANDATORY)
{
OBTAIN_INTERFACE(ISequentialStream);
}
//AutoQI
if(dwCreateOpts & CREATE_QI_OPTIONAL)
{
//[OPTIONAL]
OBTAIN_INTERFACE(IStream);
OBTAIN_INTERFACE(IStorage);
OBTAIN_INTERFACE(ILockBytes);
}
return S_OK;
}
////////////////////////////////////////////////////////////////
// CStream::OnDefOperation
//
/////////////////////////////////////////////////////////////////
void CStream::OnDefOperation()
{
//Need to bring up the StreamViewer
if(m_pCMainWindow)
{
UINT uID = IDM_ISEQSTREAM_READ;
//Determine which interface to use for reading.
//Since the user just clicked on the object we have to guess which interface to use from those
//that are available, and most functional...
if(m_pIStream)
uID = IDM_ISTREAM_READ;
//Display the Stream Viewer...
//NOTE: This dialog could be displayed from numerous sources, either the execute dialog,
//or directly from clicking on the stream - (ie: use GetFocus to determine the parent window)
m_pCMainWindow->DisplayDialog(IDD_STREAM_VIEWER, GetFocus(), CMainWindow::StreamViewerProc, this, uID);
}
}
/////////////////////////////////////////////////////////////////
// CStream::DisplayObject
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::DisplayObject()
{
HRESULT hr = S_OK;
//Display the object...
OnDefOperation();
//Delegate
TESTC(hr = CAsynchBase::DisplayObject());
CLEANUP:
return hr;
}
////////////////////////////////////////////////////////////////
// CStream::ReadBytes
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::ReadBytes(REFIID riid, DBLENGTH ulOffset, DBLENGTH cBytes, DBLENGTH cbMaxSize, void* pBuffer, DBLENGTH* pcbRead)
{
HRESULT hr = S_OK;
BOOL bUseDefault = FALSE;
ULONG cbRead = 0;
cBytes = min(cBytes, cbMaxSize);
//Determine object type
if(riid != IID_ISequentialStream && riid != IID_ILockBytes && riid != IID_IStream)
bUseDefault = TRUE;
//IID_ISequentialStream
if((riid == IID_ISequentialStream && m_pISequentialStream) || (bUseDefault && m_pISequentialStream))
{
//ISequentialStream::Read
//TODO64: I*::Read only takes a ULONG
//NOTE: ::Read can post errorinfo objects in the case of warnings. SQLOLEDB's XML Stream
//object will post execution errors on the last read...
XTEST_(hr = m_pISequentialStream->Read(pBuffer, (ULONG)cBytes, &cbRead),S_FALSE);
TESTC(TRACE_METHOD(hr, L"ISequentialStream::Read(0x%p, %d, &%d)", pBuffer, cBytes, cbRead));
}
//IID_ILockBytes
else if((riid == IID_ILockBytes && m_pILockBytes) || (bUseDefault && m_pILockBytes))
{
ULARGE_INTEGER ulgOffset;
ulgOffset.QuadPart = ulOffset;
//ILockBytes::ReadAt
//TODO64: I*::Read only takes a ULONG
XTEST_(hr = m_pILockBytes->ReadAt(ulgOffset, pBuffer, (ULONG)cBytes, &cbRead),S_FALSE);
TESTC(TRACE_METHOD(hr, L"ILockBytes::ReadAt(%d, 0x%p, %d, &%d)", ulOffset, pBuffer, cBytes, cbRead));
}
//IID_IStream
else if((riid == IID_IStream && m_pIStream) || (bUseDefault && m_pIStream))
{
//IStream::Read
//TODO64: I*::Read only takes a ULONG
XTEST_(hr = m_pIStream->Read(pBuffer, (ULONG)cBytes, &cbRead),S_FALSE);
TESTC(TRACE_METHOD(hr, L"IStream::Read(0x%p, %d, &%d)", pBuffer, cBytes, cbRead));
}
// else if(m_pIStorage)
// {
//TODO
// }
else
{
TESTC(hr = E_FAIL);
}
CLEANUP:
if(pcbRead)
*pcbRead = cbRead;
return hr;
}
////////////////////////////////////////////////////////////////
// CStream::ReadString
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::ReadString(REFIID riid, DBLENGTH ulOffset, DBLENGTH cBytes, DBLENGTH ulMaxSize, WCHAR* pwszBuffer, DBLENGTH* pcbRead)
{
ASSERT(pwszBuffer);
HRESULT hr = S_OK;
pwszBuffer[0] = wEOL;
WCHAR pBuffer[MAX_COL_SIZE+1] = {0};
DWORD dwConvFlags = GetOptions()->m_dwConvFlags;
DBLENGTH cbRead = 0;
cBytes = min(min(sizeof(pBuffer), cBytes), ulMaxSize*sizeof(WCHAR));
//Delegate
TESTC(hr = ReadBytes(riid, ulOffset, cBytes, ulMaxSize*sizeof(WCHAR), pBuffer, &cbRead));
//What type of data are we reading
switch(m_wType)
{
case DBTYPE_WSTR:
case DBTYPE_STR:
//Add a NULL terminator on the end of the data before conversion
//since the spec indicates the stream is not NULL terminated...
//NOTE: We made sure we had a full extra WCHAR in the buffer ahead of time
memset((BYTE*)pBuffer + min(cBytes, cbRead), 0, sizeof(WCHAR));
cbRead += (m_wType == DBTYPE_WSTR) ? sizeof(WCHAR) : sizeof(CHAR);
break;
};
//Now that we have read the data, coerce it into a string for display purposes...
XTESTC_(hr = DataConvert
(
DBSTATUS_S_OK,
cbRead,
sizeof(pBuffer),
(m_wType == DBTYPE_WSTR || m_wType == DBTYPE_STR) ? m_wType : DBTYPE_BYTES,
pBuffer,
0,
0,
DBTYPE_WSTR,
NULL,
NULL,
pwszBuffer,
ulMaxSize*sizeof(WCHAR),
dwConvFlags
),S_OK);
CLEANUP:
if(pcbRead)
*pcbRead = cbRead;
return hr;
}
////////////////////////////////////////////////////////////////
// CStream::WriteBytes
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::WriteBytes(REFIID riid, DBLENGTH ulOffset, DBLENGTH cBytes, void* pBuffer, DBLENGTH* pcbWritten)
{
HRESULT hr = S_OK;
BOOL bUseDefault = FALSE;
ULONG cbWritten = 0;
//Determine object type
if(riid != IID_ISequentialStream && riid != IID_ILockBytes && riid != IID_IStream)
bUseDefault = TRUE;
//IID_ISequentialStream
if((riid == IID_ISequentialStream && m_pISequentialStream) || (bUseDefault && m_pISequentialStream))
{
//ISequentialStream::Write
//TODO64: I*::Write only takes a ULONG
XTEST(hr = m_pISequentialStream->Write(pBuffer, (ULONG)cBytes, &cbWritten));
TESTC(TRACE_METHOD(hr, L"ISequentialStream::Write(0x%p, %d, &%d)", pBuffer, cBytes, cbWritten));
}
//IID_ILockBytes
else if((riid == IID_ILockBytes && m_pILockBytes) || (bUseDefault && m_pILockBytes))
{
ULARGE_INTEGER ulgOffset;
ulgOffset.QuadPart = ulOffset;
//ILockBytes::WriteAt
//TODO64: I*::Write only takes a ULONG
XTEST(hr = m_pILockBytes->WriteAt(ulgOffset, pBuffer, (ULONG)cBytes, &cbWritten));
TESTC(TRACE_METHOD(hr, L"ILockBytes::WriteAt(%d, 0x%p, %d, &%d)", ulOffset, pBuffer, cBytes, cbWritten));
}
//IID_IStream
else if((riid == IID_IStream && m_pIStream) || (bUseDefault && m_pIStream))
{
//IStream::Write
//TODO64: I*::Write only takes a ULONG
XTEST(hr = m_pIStream->Write(pBuffer, (ULONG)cBytes, &cbWritten));
TESTC(TRACE_METHOD(hr, L"IStream::Write(0x%p, %d, &%d)", pBuffer, cBytes, cbWritten));
}
// else if(m_pIStorage)
// {
//TODO
// }
else
{
TESTC(hr = E_FAIL);
}
CLEANUP:
if(pcbWritten)
*pcbWritten = cbWritten;
return hr;
}
////////////////////////////////////////////////////////////////
// CStream::WriteString
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::WriteString(REFIID riid, DBLENGTH ulOffset, DBLENGTH cBytes, WCHAR* pwszBuffer, DBLENGTH* pcbWritten)
{
HRESULT hr = S_OK;
WCHAR pBuffer[MAX_COL_SIZE] = {0};
DWORD dwConvFlags = GetOptions()->m_dwConvFlags;
DBLENGTH cbWritten = 0;
cBytes = min(sizeof(pBuffer), cBytes);
//First Convert the user entered data into the backend format...
//So the stream is a native stream of bytes to set into the backend...
XTESTC_(hr = DataConvert
(
DBSTATUS_S_OK,
cBytes,
cBytes + sizeof(WCHAR),
DBTYPE_WSTR,
pwszBuffer,
0, //Precision
0, //Scale
(m_wType == DBTYPE_IUNKNOWN || m_wType == (DBTYPE_BYREF|DBTYPE_IUNKNOWN))
? DBTYPE_BYTES : m_wType,
NULL, //&dbDstStatus
&cBytes, //&cbDstLength
pBuffer,
sizeof(pBuffer),
dwConvFlags
),S_OK);
//Delegate
TESTC(hr = WriteBytes(riid, ulOffset, cBytes, pBuffer, &cbWritten));
CLEANUP:
if(pcbWritten)
*pcbWritten = cbWritten;
return hr;
}
////////////////////////////////////////////////////////////////
// CStream::Clone
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::Clone(IStream** ppStream)
{
HRESULT hr = S_OK;
if(m_pIStream)
{
//IStream::Clone
XTEST(hr = m_pIStream->Clone(ppStream));
TESTC(TRACE_METHOD(hr, L"IStream::Clone(&0x%p)", ppStream ? *ppStream : NULL));
}
CLEANUP:
return hr;
}
////////////////////////////////////////////////////////////////
// CStream::Seek
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
{
HRESULT hr = E_INVALIDARG;
if(m_pIStream)
{
//IStream::Seek
TEST(hr = m_pIStream->Seek(dlibMove, dwOrigin, plibNewPosition));
TESTC(TRACE_METHOD(hr, L"IStream::Seek(%lld, %d, &%lld)", dlibMove.QuadPart, dwOrigin, plibNewPosition ? plibNewPosition->QuadPart : 0));
}
CLEANUP:
return hr;
}
////////////////////////////////////////////////////////////////
// CStream::Stat
//
/////////////////////////////////////////////////////////////////
HRESULT CStream::Stat(STATSTG* pstatstg)
{
ASSERT(pstatstg);
HRESULT hr = S_OK;
WCHAR* pwszProgID = NULL;
if(m_pIStream)
{
memset(pstatstg, 0, sizeof(STATSTG));
//IStream::Stat
XTEST(hr = m_pIStream->Stat(pstatstg, STATFLAG_DEFAULT));
pwszProgID = GetProgID(pstatstg->clsid);
TESTC(TRACE_METHOD(hr, L"IStream::Stat(&{\"%s\", %d, %ld, 0x%08x, 0x%08x, 0x%08x, %d, %d, \"%s\", 0x%08x, %d}, \"%s\")", pstatstg->pwcsName, pstatstg->type, (ULONG)pstatstg->cbSize.QuadPart, &pstatstg->mtime, &pstatstg->ctime, &pstatstg->atime, pstatstg->grfMode, pstatstg->grfLocksSupported, pwszProgID, pstatstg->grfStateBits, pstatstg->reserved, L"STATFLAG_DEFAULT"));
}
CLEANUP:
SAFE_FREE(pwszProgID);
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Class CStorageBuffer
//
// My implementation of storage interfaces
///////////////////////////////////////////////////////////////////////////////
CStorageBuffer::CStorageBuffer()
{
m_iPos = 0;
m_cRef = 1;
m_pBuffer = NULL;
m_cMaxSize = 0;
}
CStorageBuffer::~CStorageBuffer()
{
SAFE_FREE(m_pBuffer);
}
ULONG CStorageBuffer::AddRef()
{
m_cRef++;
return m_cRef;
}
ULONG CStorageBuffer::Release()
{
ASSERT(m_cRef);
m_cRef--;
if(m_cRef)
return m_cRef;
delete this;
return 0;
}
HRESULT CStorageBuffer::QueryInterface(REFIID riid, void** ppv)
{
if(!ppv)
return E_INVALIDARG;
*ppv = NULL;
HRESULT hr = E_NOINTERFACE;
if (riid == IID_IUnknown)
*ppv = (IUnknown*)(ISequentialStream*)this;
else if (riid == IID_ISequentialStream)
*ppv = (ISequentialStream*)this;
else if (riid == IID_IStream)
*ppv = (IStream*)this;
else if (riid == IID_ILockBytes)
*ppv = (ILockBytes*)this;
// else if (riid == IID_IStorage)
// *ppv = (IStorage*)this;
if(*ppv)
{
//Avoid internal notiifcations
m_cRef++;
hr = S_OK;
}
return hr;
}
HRESULT CStorageBuffer::ReadBuffer(ULONG ulOffset, void *pv, ULONG cb, ULONG* pcbRead)
{
//Parameter checking
if(pcbRead)
*pcbRead = 0;
if(!pv)
return STG_E_INVALIDPOINTER;
if(cb == 0)
return S_OK;
//Actual code
ULONG cBytesLeft = m_cMaxSize > ulOffset ? m_cMaxSize - ulOffset : 0;
ULONG cBytesRead = cb > cBytesLeft ? cBytesLeft : cb;
//if no more bytes to retrive return
if(cBytesLeft == 0)
return S_FALSE;
//Copy to users buffer the number of bytes requested or remaining
memcpy(pv, (void*)((BYTE*)m_pBuffer + ulOffset), cBytesRead);
if(pcbRead)
*pcbRead = cBytesRead;
if(cb != cBytesRead)
return S_FALSE;
return S_OK;
}
HRESULT CStorageBuffer::WriteBuffer(ULONG ulOffset, const void *pv, ULONG cb, ULONG* pcbWritten)
{
//Parameter checking
if(!pv)
return STG_E_INVALIDPOINTER;
if(pcbWritten)
*pcbWritten = 0;
if(cb == 0)
return S_OK;
//May need to Enlarge the current buffer
if(ulOffset + cb >= m_cMaxSize)
{
//Need to append to the end of the stream
ULARGE_INTEGER largeSize = { ulOffset + cb };
if(FAILED(SetSize(largeSize)))
return E_OUTOFMEMORY;
}
//Copy to the buffer
memcpy((void*)((BYTE*)m_pBuffer + ulOffset), pv, cb);
if(pcbWritten)
*pcbWritten = cb;
return S_OK;
}
HRESULT CStorageBuffer::Read(void *pv, ULONG cb, ULONG* pcbRead)
{
ULONG cbRead = 0;
HRESULT hr = S_OK;
//Delegate, reading from the current position
XTEST(hr = ReadBuffer(m_iPos, pv, cb, &cbRead));
TRACE_METHOD(hr, L"\tISequentialStream::Read(0x%p, %d, &%d)", pv, cb, cbRead);
if(SUCCEEDED(hr))
m_iPos += cbRead;
if(pcbRead)
*pcbRead = cbRead;
return hr;
}
HRESULT CStorageBuffer::Write(const void *pv, ULONG cb, ULONG* pcbWritten)
{
ULONG cbWritten = 0;
HRESULT hr = S_OK;
//Delegate, writting to the current position
XTEST(hr = WriteBuffer(m_iPos, pv, cb, &cbWritten));
TRACE_METHOD(hr, L"\tISequentialStream::Write(0x%p, %d, &%d)", pv, cb, cbWritten);
if(SUCCEEDED(hr))
m_iPos += cbWritten;
if(pcbWritten)
*pcbWritten = cbWritten;
return hr;
}
HRESULT CStorageBuffer::Seek(LONG lOffset, DWORD dwOrigin, ULONG* pulNewPosition)
{
HRESULT hr = S_OK;
LONG lNewPos = 0;
//Determine with respect to what the origin is...
switch(dwOrigin)
{
case STREAM_SEEK_SET:
lNewPos = lOffset;
break;
case STREAM_SEEK_CUR:
lNewPos = m_iPos + lOffset;
break;
case STREAM_SEEK_END:
lNewPos = m_cMaxSize + lOffset;
break;
default:
//The value of the dwOrigin parameter is not valid
hr = STG_E_INVALIDFUNCTION;
goto CLEANUP;
break;
};
if(lNewPos<0 || (ULONG)lNewPos>m_cMaxSize)
return STG_E_INVALIDFUNCTION; //No good return code for this situaiton?
//Reset the current buffer position
m_iPos = lNewPos;
CLEANUP:
if(pulNewPosition)
*pulNewPosition = m_iPos;
TRACE_METHOD(hr, L"\tIStream::Seek(%d, %d, &%d)", lOffset, dwOrigin, m_iPos);
return S_OK;
}
//IStream interfaces
HRESULT CStorageBuffer::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition)
{
HRESULT hr = S_OK;
ULONG ulNewPosition = 0;
//Delegate to the simplier version
TESTC(hr = Seek((LONG)dlibMove.QuadPart, dwOrigin, &ulNewPosition));
CLEANUP:
if(plibNewPosition)
plibNewPosition->QuadPart = ulNewPosition;
return hr;
}
HRESULT CStorageBuffer::SetSize( ULARGE_INTEGER libNewSize) //Specifies the new size of the stream object
{
HRESULT hr = STG_E_MEDIUMFULL;
void* pBuffer = m_pBuffer;
//The value of the libNewSize parameter is not valid.
//Since streams cannot be greater than 2^32 bytes in the COM-provided
//implementation, the high DWORD of libNewSize must be 0.
//If it is nonzero, this parameter is not valid.
if(libNewSize.QuadPart > ULONG_MAX)
{
hr = STG_E_INVALIDFUNCTION;
goto CLEANUP;
}
//Use a copy variable, incase allocations fail...
SAFE_REALLOC(pBuffer, BYTE, libNewSize.QuadPart);
m_pBuffer = pBuffer;
m_cMaxSize = (ULONG)libNewSize.QuadPart;
hr = S_OK;
CLEANUP:
TRACE_METHOD(hr, L"\tIStream::SetSize(%d)", (ULONG)libNewSize.QuadPart);
return hr;
}
HRESULT CStorageBuffer::CopyTo( IStream * pIStream, //Points to the destination stream
ULARGE_INTEGER cb, //Specifies the number of bytes to copy
ULARGE_INTEGER * pcbRead, //Pointer to the actual number of bytes read from the source
ULARGE_INTEGER * pcbWritten) //Pointer to the actual number of bytes written to the destination
{
HRESULT hr = S_OK;
//This is equivlent of doing Read from our stream then Write into the destination.
//It also moves the seek position. Bug this would require allocating a temp buffer...
//Just access our stream directly...
//Parameter checking
if(pcbRead)
pcbRead->QuadPart = 0;
if(pcbWritten)
pcbWritten->QuadPart = 0;
//Actual code
ULONG cBytesLeft = m_cMaxSize - m_iPos;
ULONG cBytesRead = (ULONG)cb.QuadPart > cBytesLeft ? cBytesLeft : (ULONG)cb.QuadPart;
ULONG cBytesWritten = 0;
if(!pIStream)
{
hr = STG_E_INVALIDPOINTER;
goto CLEANUP;
}
if(cb.QuadPart == 0)
{
hr = S_OK;
goto CLEANUP;
}
//if no more bytes to retrive return
if(cBytesLeft == 0)
{
hr = S_FALSE;
goto CLEANUP;
}
//Copy to users buffer the number of bytes requested or remaining
pIStream->Write((BYTE*)m_pBuffer + m_iPos, cBytesRead, &cBytesWritten);
m_iPos += cBytesRead;
if(pcbRead)
pcbRead->QuadPart = cBytesRead;
if(pcbWritten)
pcbWritten->QuadPart = cBytesWritten;
if(cb.QuadPart != cBytesRead)
hr = S_FALSE;
CLEANUP:
TRACE_METHOD(hr, L"\tIStream::CopyTo(0x%p, %Id, &%d, &%d)", pIStream, cb, cBytesRead, cBytesWritten);
return hr;
}
HRESULT CStorageBuffer::Commit( DWORD grfCommitFlags) //Specifies how changes are committed
{
HRESULT hr = S_OK;
TRACE_METHOD(hr, L"\tIStream::Commit()");
return hr;
}
HRESULT CStorageBuffer::Revert( )
{
HRESULT hr = S_OK;
TRACE_METHOD(hr, L"\tIStream::Revert()");
return hr;
}
HRESULT CStorageBuffer::LockRegion(ULARGE_INTEGER libOffset, //Specifies the byte offset for the beginning of the range
ULARGE_INTEGER cb, //Specifies the length of the range in bytes
DWORD dwLockType) //Specifies the restriction on accessing the specified range
{
//Locking is optional
HRESULT hr = STG_E_INVALIDFUNCTION;
TRACE_METHOD(hr, L"\tIStream::LockRegion()");
return hr;
}
HRESULT CStorageBuffer::UnlockRegion( ULARGE_INTEGER libOffset, //Specifies the byte offset for the beginning of the range
ULARGE_INTEGER cb, //Specifies the length of the range in bytes
DWORD dwLockType) //Specifies the access restriction previously placed on the range);
{
//Locking is optional
HRESULT hr = STG_E_INVALIDFUNCTION;
TRACE_METHOD(hr, L"\tIStream::UnlockRegion()");
return hr;
}
HRESULT CStorageBuffer::Stat( STATSTG * pstatstg, //Location for STATSTG structure
DWORD grfStatFlag) //Values taken from the STATFLAG enumeration
{
HRESULT hr = S_OK;
ULARGE_INTEGER largeInteger = { m_cMaxSize };
if(!pstatstg)
{
hr = STG_E_INVALIDPOINTER;
goto CLEANUP;
}
//Initialize the struct
memset(pstatstg, 0, sizeof(STATSTG));
//Set the string...
switch(grfStatFlag)
{
case STATFLAG_NONAME:
pstatstg->pwcsName = NULL;
break;
case STATFLAG_DEFAULT:
pstatstg->pwcsName = wcsDuplicate(L"OLE DB Test Storage Object...");
break;
default:
hr = STG_E_INVALIDFLAG;
goto CLEANUP;
};
//type
pstatstg->type = STGTY_STREAM;
pstatstg->grfMode = STGM_READ;
pstatstg->grfLocksSupported = 0;
pstatstg->cbSize = largeInteger;
CLEANUP:
TRACE_METHOD(hr, L"\tIStream::Stat(0x%p, 0x%08x)", pstatstg, grfStatFlag);
return hr;
}
HRESULT CStorageBuffer::Clone(IStream** ppIStream) //Points to location for pointer to the new stream object
{
HRESULT hr = E_NOTIMPL;
TRACE_METHOD(hr, L"\tILockBytes::Clone()");
return hr;
}
HRESULT CStorageBuffer::ReadAt(
ULARGE_INTEGER ulOffset,
/* [out] */ void* pv,
/* [in] */ ULONG cb,
/* [out] */ ULONG* pcbRead)
{
HRESULT hr = S_OK;
ULONG cbRead = 0;
//Delegate, reading from the current position
XTEST(hr = ReadBuffer((ULONG)ulOffset.QuadPart, pv, cb, &cbRead));
TRACE_METHOD(hr, L"\tILockBytes::ReadAt(%d, 0x%p, %d, &%d)", (ULONG)ulOffset.QuadPart, pv, cb, cbRead);
if(pcbRead)
*pcbRead = cbRead;
return hr;
}
HRESULT CStorageBuffer::WriteAt(
ULARGE_INTEGER ulOffset,
/* [in] */ const void* pv,
/* [in] */ ULONG cb,
/* [out] */ ULONG* pcbWritten)
{
HRESULT hr = S_OK;
ULONG cbWritten = 0;
//Delegate, reading from the current position
XTEST(hr = WriteBuffer((ULONG)ulOffset.QuadPart, pv, cb, &cbWritten));
TRACE_METHOD(hr, L"\tILockBytes::WriteAt(%d, 0x%p, %d, &%d)", (ULONG)ulOffset.QuadPart, pv, cb, cbWritten);
if(pcbWritten)
*pcbWritten = cbWritten;
return hr;
}
HRESULT CStorageBuffer::Flush()
{
HRESULT hr = S_OK;
TRACE_METHOD(hr, L"\tILockBytes::Flush()");
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Class CFileStream
//
// My implementation of stream interface on top of a file
///////////////////////////////////////////////////////////////////////////////
CFileStream::CFileStream()
{
m_pFile = NULL;
m_cRef = 1;
}
CFileStream::~CFileStream()
{
CloseFile();
}
HRESULT CFileStream::OpenFile(WCHAR* pwszFile, WCHAR* pwszMode)
{
if(IsUnicodeOS())
{
_wfopen_s(&m_pFile, pwszFile, pwszMode);
}
else
{
CHAR szFileName[MAX_NAME_LEN];
ConvertToMBCS(pwszFile, szFileName, MAX_NAME_LEN);
CHAR szMode[MAX_NAME_LEN];
ConvertToMBCS(pwszMode, szMode, MAX_NAME_LEN);
fopen_s(&m_pFile, szFileName, szMode);
}
return m_pFile ? S_OK : STG_E_ACCESSDENIED;
}
HRESULT CFileStream::CloseFile()
{
if(m_pFile)
fclose(m_pFile);
return S_OK;
}
ULONG CFileStream::AddRef()
{
m_cRef++;
return m_cRef;
}
ULONG CFileStream::Release()
{
ASSERT(m_cRef);
m_cRef--;
if(m_cRef)
return m_cRef;
delete this;
return 0;
}
HRESULT CFileStream::QueryInterface(REFIID riid, void** ppv)
{
ASSERT(ppv);
HRESULT hr = E_NOINTERFACE;
*ppv = NULL;
if (riid == IID_IUnknown)
*ppv = (IUnknown*)this;
else if (riid == IID_ISequentialStream)
*ppv = (ISequentialStream*)this;
else if (riid == IID_IStream)
*ppv = (IStream*)this;
if(*ppv)
{
m_cRef++;
hr = S_OK;
}
return hr;
}
STDMETHODIMP CFileStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
ULONG cbRead = 0;
//Parameter checking
if(!m_pFile)
return STG_E_INVALIDHANDLE;
if(!pv)
return STG_E_INVALIDPOINTER;
if(cb == 0)
return S_OK;
//Read from the file...
cbRead = (ULONG)fread(pv, sizeof(BYTE), cb, m_pFile);
//Results..
if(pcbRead)
*pcbRead = cbRead;
return cbRead==cb ? S_OK : S_FALSE;
}
STDMETHODIMP CFileStream::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
{
ULONG cbWritten = 0;
//Parameter checking
if(!m_pFile)
return STG_E_INVALIDHANDLE;
if(!pv)
return STG_E_INVALIDPOINTER;
if(cb == 0)
return S_OK;
//Read from the file...
cbWritten = (ULONG)fwrite(pv, sizeof(BYTE), cb, m_pFile);
//Results..
if(pcbWritten)
*pcbWritten = cbWritten;
return cbWritten==cb ? S_OK : S_FALSE;
}
//IStream interfaces
HRESULT CFileStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition)
{
//Parameter checking
if(!m_pFile)
return STG_E_INVALIDHANDLE;
//Seek
LONG lNewPosition = fseek(m_pFile, dlibMove.LowPart, dwOrigin);
if (lNewPosition >= 0 && plibNewPosition)
plibNewPosition->LowPart = lNewPosition;
return (lNewPosition >= 0) ? S_OK : E_FAIL;
}
HRESULT CFileStream::SetSize( ULARGE_INTEGER libNewSize) //Specifies the new size of the stream object
{
return E_NOTIMPL;
}
HRESULT CFileStream::CopyTo( IStream * pIStream, //Points to the destination stream
ULARGE_INTEGER cb, //Specifies the number of bytes to copy
ULARGE_INTEGER * pcbRead, //Pointer to the actual number of bytes read from the source
ULARGE_INTEGER * pcbWritten) //Pointer to the actual number of bytes written to the destination
{
return E_NOTIMPL;
}
HRESULT CFileStream::Commit( DWORD grfCommitFlags) //Specifies how changes are committed
{
return E_NOTIMPL;
}
HRESULT CFileStream::Revert( )
{
return E_NOTIMPL;
}
HRESULT CFileStream::LockRegion(ULARGE_INTEGER libOffset, //Specifies the byte offset for the beginning of the range
ULARGE_INTEGER cb, //Specifies the length of the range in bytes
DWORD dwLockType) //Specifies the restriction on accessing the specified range
{
return E_NOTIMPL;
}
HRESULT CFileStream::UnlockRegion( ULARGE_INTEGER libOffset, //Specifies the byte offset for the beginning of the range
ULARGE_INTEGER cb, //Specifies the length of the range in bytes
DWORD dwLockType) //Specifies the access restriction previously placed on the range);
{
return E_NOTIMPL;
}
HRESULT CFileStream::Stat( STATSTG * pstatstg, //Location for STATSTG structure
DWORD grfStatFlag) //Values taken from the STATFLAG enumeration
{
return E_NOTIMPL;
}
HRESULT CFileStream::Clone(IStream** ppIStream) //Points to location for pointer to the new stream object
{
return E_NOTIMPL;
}