BoxedApp Blog

BoxedApp: Tips'n'Tricks, Examples, Use Cases etc.

A virtual file based on IStream


Share this post Bookmark and Share

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.

Share this post Bookmark and Share






Write a Comment

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