A virtual file based on IStream
Briefly
A new function, BoxedAppSDK_CreateVirtualFileBasedOnIStream, has been added to BoxedApp SDK.
What For?
To provide even greater flexibility, BoxedApp SDK now allows creating virtual files based upon IStream, the standard COM interface. A programmer can now solely define the behavior of a virtual file.
The new function is declared as follows:
HANDLE BoxedAppSDK_CreateVirtualFileBasedOnIStream(
LPCTSTR szPath,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile,
LPSTREAM pStream
);
All, except the last, arguments repeat the arguments of the standard CreateFile function. The last argument is a pointer to IStream.
To check out BoxedApp SDK in action, download the demo version:
[ Download demo version ]
Included are examples for C++, C#, VB.Net, Delphi, VB6.
How does BoxedApp handle IStream data passed to it?
For reading, it will call IStream::Write; for writing, that will be IStream::Read.
Changing current position in a file — IStream::Seek. Please note: Obtaining file size is also implemented via IStream::Seek; approximately as follows:
IStream* pStream;
...
LARGE_INTEGER liZero = { 0 };
ULARGE_INTEGER CurPos;
// Save current position
pStream->Seek(liZero, STREAM_SEEK_CUR, &CurPos);
// Move to the end
ULARGE_INTEGER SizeOfFile;
pStream->Seek(liZero, STREAM_SEEK_END, &SizeOfFile);
// Restore the file pointer
LARGE_INTEGER Pos;
Pos.Quad = CurPos.Quad;
ULARGE_INTEGER temp;
pStream->Seek(Pos, STREAM_SEEK_SET, &temp);
For creating a new HANDLE of an IStream-based virtual file, IStream::Clone is to be called. The implementation of the method is to create a new instance of IStream, which would have its own pointer in the file.
Example One. C++. Implementation of IStream on the base of a static buffer.
Click on the “+ expand source” to the complete source code.
class CVirtualFilePointer;
class CMemoryFile;
class CMemoryFileLock
{
private:
CMemoryFile* m_pMemoryFile;
public:
CMemoryFileLock(CMemoryFile* pMemoryFile);
~CMemoryFileLock();
};
// A file based on fixed memory block
class CMemoryFile
{
friend class CVirtualFilePointer;
friend class CMemoryFileLock;
private:
LONG m_nRefCount;
PBYTE m_p;
DWORD m_dwSize;
CRITICAL_SECTION m_cs;
private:
CMemoryFile(PVOID p, DWORD size);
~CMemoryFile();
IStream* CreateStream();
void AddRef();
void Release();
public:
static IStream* Create(PVOID p, DWORD size);
};
class CVirtualFilePointer : public IStream
{
friend class CVirtualFile;
private:
LONG m_nRefCount;
CMemoryFile* m_pFile;
DWORD m_dwPosition;
public:
CVirtualFilePointer(CMemoryFile* pMemoryFile) :
m_nRefCount(1),
m_dwPosition(0)
{
m_pFile = pMemoryFile;
m_pFile->AddRef();
}
virtual ~CVirtualFilePointer()
{
m_pFile->Release();
}
protected:
// IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject)
{
*ppvObject = NULL;
if (IsEqualIID(IID_IUnknown, riid))
*ppvObject = this;
else if (IsEqualIID(IID_IStream, riid))
*ppvObject = this;
else if (IsEqualIID(IID_ISequentialStream, riid))
*ppvObject = this;
if (NULL != *ppvObject)
{
AddRef();
return S_OK;
}
else
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef()
{
InterlockedIncrement(&m_nRefCount);
return m_nRefCount;
}
virtual ULONG STDMETHODCALLTYPE Release()
{
InterlockedDecrement(&m_nRefCount);
LONG nRefCount = m_nRefCount;
if (0 == m_nRefCount)
delete this;
return nRefCount;
}
// ISequentialStream
virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead)
{
CMemoryFileLock lock(m_pFile);
DWORD dwBytesToRead;
if (m_dwPosition < 0 || m_dwPosition > m_pFile->m_dwSize)
dwBytesToRead = 0;
else
dwBytesToRead = cb < m_pFile->m_dwSize - m_dwPosition ?
cb : m_pFile->m_dwSize - m_dwPosition;
CopyMemory(pv, m_pFile->m_p + m_dwPosition, dwBytesToRead);
m_dwPosition += dwBytesToRead;
if (NULL != pcbRead)
*pcbRead = dwBytesToRead;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE Write(const void* pv, ULONG cb, ULONG* pcbWritten)
{
CMemoryFileLock lock(m_pFile);
DWORD dwBytesToWrite;
if (m_dwPosition < 0 || m_dwPosition > m_pFile->m_dwSize)
dwBytesToWrite = 0;
else
dwBytesToWrite = cb < m_pFile->m_dwSize - m_dwPosition ?
cb : m_pFile->m_dwSize - m_dwPosition;
CopyMemory(m_pFile->m_p + m_dwPosition, pv, dwBytesToWrite);
m_dwPosition += dwBytesToWrite;
if (NULL != pcbWritten)
*pcbWritten = dwBytesToWrite;
return S_OK;
}
// IStream
virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
{
CMemoryFileLock lock(m_pFile);
// Note: new position can be more than m_dwSize
switch (dwOrigin)
{
case STREAM_SEEK_CUR:
{
m_dwPosition += dlibMove.QuadPart;
break;
}
case STREAM_SEEK_END:
{
m_dwPosition = m_pFile->m_dwSize + dlibMove.QuadPart;
break;
}
case STREAM_SEEK_SET:
{
m_dwPosition = dlibMove.QuadPart;
break;
}
default:
{
return E_FAIL;
}
}
if (NULL != plibNewPosition)
plibNewPosition->QuadPart = m_dwPosition;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize)
{
// TODO:
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Clone(IStream** ppstm)
{
*ppstm = m_pFile->CreateStream();
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten){return E_NOTIMPL;}
virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags){return E_NOTIMPL;}
virtual HRESULT STDMETHODCALLTYPE Revert(){return E_NOTIMPL;}
virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType){return E_NOTIMPL;}
virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType){return E_NOTIMPL;}
virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pstatstg, DWORD grfStatFlag){return E_NOTIMPL;}
};
CMemoryFileLock::CMemoryFileLock(CMemoryFile* pMemoryFile) :
m_pMemoryFile(pMemoryFile)
{
EnterCriticalSection(&pMemoryFile->m_cs);
}
CMemoryFileLock::~CMemoryFileLock()
{
LeaveCriticalSection(&m_pMemoryFile->m_cs);
}
CMemoryFile::CMemoryFile(PVOID p, DWORD size) :
m_p((PBYTE)p),
m_dwSize(size),
m_nRefCount(0)
{
InitializeCriticalSection(&m_cs);
}
CMemoryFile::~CMemoryFile()
{
DeleteCriticalSection(&m_cs);
}
IStream* CMemoryFile::CreateStream()
{
return new CVirtualFilePointer(this);
}
void CMemoryFile::AddRef()
{
InterlockedIncrement(&m_nRefCount);
}
void CMemoryFile::Release()
{
InterlockedDecrement(&m_nRefCount);
if (0 == m_nRefCount)
delete this;
}
IStream* CMemoryFile::Create(PVOID p, DWORD size)
{
CMemoryFile* pMemoryFile = new CMemoryFile(p, size);
return pMemoryFile->CreateStream();
}
Example Two. C#. A virtual file as a part of the source file.
To check out this example in action, download the demo version:
[ Download demo version ]
The example is located in the samples\C#\Sample3_CustomVirtualFileSystem folder.
Click on the “+ expand source” to the complete source code.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.IO;
namespace Sample3_CustomVirtualFileSystem
{
class CustomFileStream : IStream
{
private long _Offset;
private long _Length;
private string _FilePath;
private Stream _Stream;
public CustomFileStream(string FilePath, long Offset, long Length)
{
_FilePath = FilePath;
_Stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
_Offset = Offset;
_Length = Length;
}
public CustomFileStream(string FilePath, long Offset)
{
_FilePath = FilePath;
_Stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
_Offset = Offset;
_Length = _Stream.Length - _Offset;
}
#region IStream Members
public void Read(byte[] pv, int cb, IntPtr pcbRead)
{
if (_Stream.Position > _Offset + _Length)
cb = 0;
else if (_Stream.Position + cb > _Offset + _Length)
cb = (int)(_Offset + _Length - _Stream.Position);
int nReadBytes = _Stream.Read(pv, 0, cb);
if (IntPtr.Zero != pcbRead)
Marshal.WriteIntPtr(pcbRead, new IntPtr(nReadBytes));
}
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
if (_Stream.Position > _Offset + _Length)
cb = 0;
else if (_Stream.Position + cb > _Offset + _Length)
cb = (int)(_Offset + _Length - _Stream.Position);
int nWrittenBytes = _Stream.Read(pv, 0, cb);
if (IntPtr.Zero != pcbWritten)
Marshal.WriteIntPtr(pcbWritten, new IntPtr(nWrittenBytes));
}
public void Clone(out IStream ppstm)
{
ppstm = new CustomFileStream(_FilePath, _Offset, _Length);
}
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
{
SeekOrigin Origin = (SeekOrigin)dwOrigin;
long NewPosition = 0;
switch (Origin)
{
case SeekOrigin.Begin:
{
NewPosition = _Stream.Seek(_Offset + dlibMove, Origin);
break;
}
case SeekOrigin.Current:
{
NewPosition = _Stream.Seek(dlibMove, Origin);
break;
}
case SeekOrigin.End:
{
NewPosition = _Stream.Seek(_Offset + _Length + dlibMove, SeekOrigin.Begin);
break;
}
}
NewPosition -= _Offset;
if (NewPosition < 0)
NewPosition = 0;
else if (NewPosition > _Length)
NewPosition = _Length;
if (IntPtr.Zero != plibNewPosition)
Marshal.WriteInt64(plibNewPosition, NewPosition);
}
public void Commit(int grfCommitFlags)
{
throw new Exception("The method or operation is not implemented.");
}
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
{
throw new Exception("The method or operation is not implemented.");
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
throw new Exception("The method or operation is not implemented.");
}
public void Revert()
{
throw new Exception("The method or operation is not implemented.");
}
public void SetSize(long libNewSize)
{
throw new Exception("The method or operation is not implemented.");
}
public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
{
throw new Exception("The method or operation is not implemented.");
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
throw new Exception("The method or operation is not implemented.");
}
#endregion
}
}
To check out this example in action, download the demo version:
[ Download demo version ]
Included are examples for C++, C#, VB.Net, Delphi, VB6.


Write a Comment
You must be logged in to post a comment. Log in