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