Creating a Virtual File Based on IStream
BoxedApp SDK introduces the function BoxedAppSDK_CreateVirtualFileBasedOnIStream, which allows developers to create virtual files that operate over a custom IStream implementation.
This enables full control over how a virtual file behaves — reads, writes, seeking, cloning, and size handling — using standard COM interface methods.
Purpose
To provide greater flexibility in virtualization, BoxedApp SDK supports creating virtual files based on IStream, the standard COM interface.
This allows a developer to implement a file-like object whose contents and behavior are entirely user-defined.
Function Declaration
HANDLE BoxedAppSDK_CreateVirtualFileBasedOnIStream(
LPCTSTR szPath,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile,
LPSTREAM pStream
);All parameters except the last one are identical to those of the standard CreateFile function.
The last parameter, pStream, is a pointer to an IStream interface that defines the file's behavior.
IStream Integration Details
When BoxedApp handles a virtual file created from an IStream:
- For read operations, it calls
IStream::Read. - For write operations, it calls
IStream::Write. - To change the current position in the file, it uses
IStream::Seek. - To obtain the file size, BoxedApp internally calls
Seekto determine the end of the stream.
Example of how BoxedApp determines file size:
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.QuadPart = CurPos.QuadPart;
ULARGE_INTEGER temp;
pStream->Seek(Pos, STREAM_SEEK_SET, &temp);When a new HANDLE is created for an IStream-based virtual file, IStream::Clone is called.
The implementation of this method should create a new IStream instance with an independent file pointer.
Example 1: C++ Implementation (IStream over a Static Buffer)
Below is an example of how to implement a minimal in-memory file using IStream on top of a static buffer.
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)
{
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();
}This implementation creates a virtual file that behaves like a normal file but stores data in memory rather than on disk.
Example 2: C# Implementation (Partial Stream-Based Virtual File)
The following C# sample shows how to use IStream to map a subsection of a file into a virtual file.
This approach allows the packed or virtualized application to work with only part of a physical file.
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
}
}Conclusion
By implementing IStream, developers can define completely custom storage backends for virtual files — memory buffers, embedded resources, database BLOBs, network streams, or encrypted containers.
This makes BoxedApp SDK not only a file packer but also a flexible virtualization framework for complex application scenarios.
✅ The SDK includes full C++, C#, VB.NET, Delphi, and VB6 samples demonstrating this approach.
Download the demo version