windows-nt/Source/XPSP1/NT/enduser/speech/common/include/streamhlp.h
2020-09-26 16:20:57 +08:00

410 lines
12 KiB
C++

#pragma once
#include <spunicode.h>
/****************************************************************************
* SpGenericCopyTo *
*-----------------*
* Description:
* This function is used by stream implementations to implement the CopyTo method of IStream.
* Note that the source stream is not parameter validated since it will be the "this" pointer
* of the object that is calling this function.
*
* Returns:
*
*****************************************************************************/
inline HRESULT SpGenericCopyTo(IStream * pSrc, IStream * pDest, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{
HRESULT hr = S_OK;
if (::IsBadReadPtr(pDest, sizeof(*pDest)))
{
hr = E_INVALIDARG;
}
else
{
if ((pcbRead && ::IsBadWritePtr(pcbRead, sizeof(*pcbRead))) ||
(pcbWritten && ::IsBadWritePtr(pcbWritten, sizeof(*pcbWritten))))
{
hr = E_POINTER;
}
else
{
BYTE aBuffer[0x1000]; // Do 4k reads
while (cb.QuadPart)
{
ULONG cbThisRead = cb.QuadPart > sizeof(aBuffer) ? sizeof(aBuffer) : cb.LowPart;
ULONG cbActuallyRead = 0;
hr = pSrc->Read(aBuffer, cbThisRead, &cbActuallyRead);
if (pcbRead)
{
pcbRead->QuadPart += cbActuallyRead;
}
if (FAILED(hr) || cbActuallyRead == 0)
{
break;
}
ULONG cbActuallyWritten = 0;
hr = pDest->Write(aBuffer, cbActuallyRead, &cbActuallyWritten);
if (pcbWritten)
{
pcbWritten->QuadPart += cbActuallyWritten;
}
if (FAILED(hr))
{
break;
}
cb.QuadPart -= cbActuallyRead;
}
}
}
return hr;
}
/****************************************************************************
* SPCreateStreamOnHGlobal *
*-------------------------*
* Description:
* Similar to CreateStreamOnHGlobal Win32 API, but allows a stream to be
* created
*
* Returns:
*
*****************************************************************************/
inline HRESULT SPCreateStreamOnHGlobal(
HGLOBAL hGlobal, //Memory handle for the stream object
BOOL fDeleteOnRelease, //Whether to free memory when the object is released
REFGUID rguidFormatId, //Format ID for stream
const WAVEFORMATEX * pwfex, //WaveFormatEx for stream
ISpStream ** ppStream) //Address of variable to receive ISpStream pointer
{
HRESULT hr;
IStream * pMemStream;
*ppStream = NULL;
hr = ::CreateStreamOnHGlobal(hGlobal, fDeleteOnRelease, &pMemStream);
if (SUCCEEDED(hr))
{
hr = ::CoCreateInstance(CLSID_SpStream, NULL, CLSCTX_ALL, __uuidof(*ppStream), (void **)ppStream);
if (SUCCEEDED(hr))
{
hr = (*ppStream)->SetBaseStream(pMemStream, rguidFormatId, pwfex);
if (FAILED(hr))
{
(*ppStream)->Release();
*ppStream = NULL;
}
}
pMemStream->Release();
}
return hr;
}
/****************************************************************************
* CSpFileStream *
*---------------*
* Description:
* This C++ class can be used by applications to turn a Win32 file into
* a COM stream. Usually it is easier to use the CLSD_SpStream implementation
* than to use this class directly, but this class allows for more fine-grained
* control over various attributes, such as security attributes. You can also
* use this class to create a stream from an existing file handle. Note that
* if you want to use this class on an existing Win32 file handle, you should
* either "give ownership" of that handle to this class (and allow this class
* to close the handle) or else you will need to use DuplicateHandle to create
* a handle that can be closed by this class.
*
* NOTE:
* Upon creation of this class, the ref count is set to 1.
*
*****************************************************************************/
class CSpFileStream : public IStream
{
private:
HANDLE m_hFile;
ULONG m_ulRef;
public:
CSpFileStream(HANDLE hFile) : m_hFile(hFile), m_ulRef(1) {}
CSpFileStream(HRESULT * pHR, const TCHAR * pFileName, DWORD dwDesiredAccess = GENERIC_READ, DWORD dwShareMode = FILE_SHARE_READ, DWORD dwCreationDisposition = OPEN_EXISTING,
LPSECURITY_ATTRIBUTES lpSecurityAttrib = NULL, DWORD dwFlagsAndAttrib = 0, HANDLE hTemplate = NULL)
{
m_hFile = ::CreateFile(pFileName, dwDesiredAccess, dwShareMode, lpSecurityAttrib, dwCreationDisposition, dwFlagsAndAttrib, hTemplate);
m_ulRef = 1;
*pHR = (m_hFile != INVALID_HANDLE_VALUE) ? S_OK : SpHrFromLastWin32Error();
}
#ifndef _UNICODE
CSpFileStream(HRESULT * pHR, const WCHAR * pFileName, DWORD dwDesiredAccess = GENERIC_READ, DWORD dwShareMode = FILE_SHARE_READ, DWORD dwCreationDisposition = OPEN_EXISTING,
LPSECURITY_ATTRIBUTES lpSecurityAttrib = NULL, DWORD dwFlagsAndAttrib = 0, HANDLE hTemplate = NULL)
{
CSpUnicodeSupport Unicode;
m_hFile = Unicode.CreateFile(pFileName, dwDesiredAccess, dwShareMode, lpSecurityAttrib, dwCreationDisposition, dwFlagsAndAttrib, hTemplate);
m_ulRef = 1;
*pHR = (m_hFile != INVALID_HANDLE_VALUE) ? S_OK : SpHrFromLastWin32Error();
}
#endif
~CSpFileStream()
{
if (m_hFile != INVALID_HANDLE_VALUE)
{
::CloseHandle(m_hFile);
}
}
HANDLE FileHandle()
{
return m_hFile;
}
HRESULT Close(void)
{
HRESULT hr = S_OK;
if (m_hFile != INVALID_HANDLE_VALUE)
{
if (!::CloseHandle(m_hFile))
{
hr = SpHrFromLastWin32Error();
}
m_hFile = INVALID_HANDLE_VALUE;
}
else
{
hr = SPERR_UNINITIALIZED;
}
return hr;
}
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
{
if (riid == __uuidof(IStream) ||
riid == IID_ISequentialStream ||
riid == __uuidof(IUnknown))
{
*ppv = (IStream *)this;
m_ulRef++;
return S_OK;
}
*ppv = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef()
{
return ++m_ulRef;
}
STDMETHODIMP_(ULONG) Release()
{
--m_ulRef;
if (m_ulRef)
{
return m_ulRef;
}
delete this;
return 0;
}
STDMETHODIMP Read(void * pv, ULONG cb, ULONG * pcbRead)
{
ULONG ulRead;
if (::ReadFile(m_hFile, pv, cb, &ulRead, NULL))
{
if (pcbRead) *pcbRead = ulRead;
return S_OK;
}
return SpHrFromLastWin32Error();
}
STDMETHODIMP Write(const void * pv, ULONG cb, ULONG * pcbWritten)
{
ULONG ulWritten;
if (::WriteFile(m_hFile, pv, cb, &ulWritten, NULL))
{
if (pcbWritten) *pcbWritten = ulWritten;
return S_OK;
}
return SpHrFromLastWin32Error();
}
STDMETHODIMP Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
long lMoveHigh = dlibMove.HighPart;
DWORD dwNewPos = ::SetFilePointer(m_hFile, dlibMove.LowPart, &lMoveHigh, dwOrigin);
if (dwNewPos == 0xFFFFFFFF && ::GetLastError() != NO_ERROR)
{
return SpHrFromLastWin32Error();
}
if (plibNewPosition)
{
plibNewPosition->LowPart = dwNewPos;
plibNewPosition->HighPart = lMoveHigh;
}
return S_OK;
}
STDMETHODIMP SetSize(ULARGE_INTEGER libNewSize)
{
HRESULT hr = S_OK;
LARGE_INTEGER Move = {0};
ULARGE_INTEGER Cur;
hr = Seek(Move, STREAM_SEEK_CUR, &Cur);
if (SUCCEEDED(hr))
{
LARGE_INTEGER li;
li.QuadPart = libNewSize.QuadPart;
hr = Seek(li, STREAM_SEEK_SET, NULL);
if (SUCCEEDED(hr))
{
if (!::SetEndOfFile(m_hFile))
{
hr = SpHrFromLastWin32Error();
}
li.QuadPart = Cur.QuadPart;
Seek(li, STREAM_SEEK_SET, NULL);
}
}
return hr;
}
STDMETHODIMP CopyTo(IStream *pStreamDest, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER __RPC_FAR *pcbWritten)
{
return SpGenericCopyTo(this, pStreamDest, cb, pcbRead, pcbWritten);
}
STDMETHODIMP Commit(DWORD /*grfCommitFlags*/)
{
return S_OK; // Direct mode streams simply ignore this
}
STDMETHODIMP Revert(void)
{
return S_OK; // Direct mode streams simply ignore this
}
STDMETHODIMP LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
#ifndef _WIN32_WCE
if (dwLockType != LOCK_WRITE && dwLockType != LOCK_EXCLUSIVE)
{
return STG_E_INVALIDFUNCTION;
}
DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
if (dwLockType == LOCK_EXCLUSIVE)
{
dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
}
OVERLAPPED Overlapped;
memset(&Overlapped, 0, sizeof(Overlapped));
Overlapped.Offset = libOffset.LowPart;
Overlapped.OffsetHigh = libOffset.HighPart;
if (::LockFileEx(m_hFile, dwFlags, 0, cb.LowPart, cb.HighPart, &Overlapped))
{
return S_OK;
}
else
{
DWORD dwErr = ::GetLastError();
if (dwErr == ERROR_LOCK_VIOLATION)
{
return STG_E_LOCKVIOLATION;
}
return SpHrFromWin32(dwErr);
}
#else // _WIN32_WCE
return E_NOTIMPL;
#endif // _WIN32_WCE
}
STDMETHODIMP UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
#ifndef _WIN32_WCE
if (dwLockType != LOCK_WRITE && dwLockType != LOCK_EXCLUSIVE)
{
return STG_E_INVALIDFUNCTION;
}
OVERLAPPED Overlapped;
memset(&Overlapped, 0, sizeof(Overlapped));
Overlapped.Offset = libOffset.LowPart;
Overlapped.OffsetHigh = libOffset.HighPart;
if (::UnlockFileEx(m_hFile, 0, cb.LowPart, cb.HighPart, &Overlapped))
{
return S_OK;
}
else
{
DWORD dwErr = ::GetLastError();
if (dwErr == ERROR_LOCK_VIOLATION)
{
return STG_E_LOCKVIOLATION;
}
return SpHrFromWin32(dwErr);
}
#else // _WIN32_WCE
return E_NOTIMPL;
#endif // _WIN32_WCE
}
STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag)
{
HRESULT hr = S_OK;
if (grfStatFlag & (~STATFLAG_NONAME))
{
hr = E_INVALIDARG;
}
else
{
ZeroMemory(pstatstg, sizeof(*pstatstg));
pstatstg->grfLocksSupported = LOCK_WRITE | LOCK_EXCLUSIVE;
pstatstg->type = STGTY_STREAM;
BY_HANDLE_FILE_INFORMATION fi;
if (::GetFileInformationByHandle(m_hFile, &fi))
{
pstatstg->cbSize.LowPart = fi.nFileSizeLow;
pstatstg->cbSize.HighPart = fi.nFileSizeHigh;
pstatstg->mtime = fi.ftLastWriteTime;
pstatstg->ctime = fi.ftCreationTime;
pstatstg->atime = fi.ftLastAccessTime;
// This implementation does not fill in the mode or the name.
}
}
return hr;
}
STDMETHODIMP Clone(IStream ** ppstm)
{
HANDLE hDupFile;
#ifndef _WIN32_WCE
HANDLE hProcess = ::GetCurrentProcess();
if (::DuplicateHandle(hProcess, m_hFile, hProcess, &hDupFile, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
*ppstm = new CSpFileStream(hDupFile);
if (*ppstm)
{
return S_OK;
}
::CloseHandle(hDupFile);
return E_OUTOFMEMORY;
}
else
{
return SpHrFromLastWin32Error();
}
#else // _WIN32_WCE
hDupFile = m_hFile;
*ppstm = new CSpFileStream(hDupFile);
if (*ppstm)
{
return S_OK;
}
return E_OUTOFMEMORY;
#endif // _WIN32_WCE
}
};