1077 lines
26 KiB
C++
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|