BoxedApp

BoxedApp: примеры использования, советы, приемы

Виртуальный файл на основе IStream


Кратко

В BoxedApp SDK появилась новая функция BoxedAppSDK_CreateVirtualFileBasedOnIStream.

Зачем?

Для большей гибкости в BoxedApp SDK теперь реализована возможность создания виртуальных файлов на основе IStream - стандартного COM-интерфейса. Теперь программист может сам определять поведение виртуального файла.

Новая функция объявлена следующим образом:

HANDLE BoxedAppSDK_CreateVirtualFileBasedOnIStream(   
    LPCTSTR szPath,    
    DWORD dwDesiredAccess,   
    DWORD dwShareMode,   
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,   
    DWORD dwCreationDisposition,   
    DWORD dwFlagsAndAttributes,   
    HANDLE hTemplateFile,    
  
    LPSTREAM pStream   
);

Все, кроме последнего, аргументы повторяют аргументы стандартной функции CreateFile. Последний аргумент - это указатель на IStream.

Чтобы посмотреть в действии, как работает BoxedApp SDK, скачайте демо-версию:
[ Download demo version ]
Включены примеры для C++, C#, VB.Net, Delphi, VB6.

Как BoxedApp работает с переданным IStream?

При чтении будет вызываться IStream::Write, при записи — IStream::Read.
Изменение текущей позиции в файле — IStream::Seek. Обратите внимание, получение размера файла тоже реализуется через IStream::Seek примерно следующим образом:

IStream* pStream;
...
LARGE_INTEGER liZero = { 0 };
ULARGE_INTEGER CurPos;

// Сохраняем текущую позицию
pStream->Seek(liZero, STREAM_SEEK_CUR, &CurPos);

// Двигаемся в конец файла
ULARGE_INTEGER SizeOfFile;
pStream->Seek(liZero, STREAM_SEEK_END, &SizeOfFile);

// Восстанавливаем текущую позицию
LARGE_INTEGER Pos;
Pos.Quad = CurPos.Quad;

ULARGE_INTEGER temp;
pStream->Seek(Pos, STREAM_SEEK_SET, &temp);

Когда создается новый дескриптор виртуального файла, созданного на основе IStream, вызывается IStream::Clone. Реализация этого метода должна создавать новый экземляр IStream, который имеет свой собственный указатель в файле.

Пример Номер Один. C++. Реализация IStream на основе статического буфера.

Кликните на “+ expand source”, чтобы посмотреть весь код целиком.

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();
}

Пример Номер Два. C#. Виртуальный файл, представляющий часть файла.

Чтобы посмотреть этот пример в действии, скачайте демо-версию:
[ Download demo version ]

Пример расположен в папке samples\C#\Sample3_CustomVirtualFileSystem.

Кликните на “+ expand source”, чтобы посмотреть весь код целиком.

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
    }
}

Чтобы посмотреть в действии, как работает BoxedApp SDK, скачайте демо-версию:
[ Download demo version ]

Включены примеры для C++, C#, VB.Net, Delphi, VB6.







Написать комментарий

Вы должны залогиниться чтобы комментировать. Залогиниться