393 lines
8.8 KiB
C++
393 lines
8.8 KiB
C++
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
#include "resource.h"
|
|
|
|
#include "BitmapUtil.h"
|
|
#include "DataCallback.h"
|
|
|
|
namespace WiaWrap
|
|
{
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDataCallback::CDataCallback
|
|
//
|
|
|
|
CDataCallback::CDataCallback(
|
|
PFNPROGRESSCALLBACK pfnProgressCallback,
|
|
PVOID pProgressCallbackParam,
|
|
LONG *plCount,
|
|
IStream ***pppStream
|
|
)
|
|
{
|
|
m_cRef = 0;
|
|
|
|
m_bBMP = FALSE;
|
|
m_nHeaderSize = 0;
|
|
m_nDataSize = 0;
|
|
|
|
m_pfnProgressCallback = pfnProgressCallback;
|
|
m_pProgressCallbackParam = pProgressCallbackParam;
|
|
|
|
m_plCount = plCount;
|
|
m_pppStream = pppStream;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDataCallback::QueryInterface
|
|
//
|
|
|
|
STDMETHODIMP CDataCallback::QueryInterface(REFIID iid, LPVOID *ppvObj)
|
|
{
|
|
if (ppvObj == NULL)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppvObj = (IUnknown*) this;
|
|
}
|
|
else if (iid == IID_IWiaDataCallback)
|
|
{
|
|
*ppvObj = (IWiaDataCallback *) this;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDataCallback::AddRef
|
|
//
|
|
|
|
STDMETHODIMP_(ULONG) CDataCallback::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDataCallback::Release
|
|
//
|
|
|
|
STDMETHODIMP_(ULONG) CDataCallback::Release()
|
|
{
|
|
LONG cRef = InterlockedDecrement(&m_cRef);
|
|
|
|
if (cRef == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return cRef;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDataCallback::BandedDataCallback
|
|
//
|
|
|
|
STDMETHODIMP CDataCallback::BandedDataCallback(
|
|
LONG lReason,
|
|
LONG lStatus,
|
|
LONG lPercentComplete,
|
|
LONG lOffset,
|
|
LONG lLength,
|
|
LONG lReserved,
|
|
LONG lResLength,
|
|
PBYTE pbBuffer
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Parse the message
|
|
|
|
switch (lReason)
|
|
{
|
|
case IT_MSG_DATA_HEADER:
|
|
{
|
|
PWIA_DATA_CALLBACK_HEADER pHeader = (PWIA_DATA_CALLBACK_HEADER) pbBuffer;
|
|
|
|
// Determine if this is a BMP transfer
|
|
|
|
m_bBMP = pHeader->guidFormatID == WiaImgFmt_MEMORYBMP || pHeader->guidFormatID == WiaImgFmt_BMP;
|
|
|
|
// For WiaImgFmt_MEMORYBMP transfers, WIA does not send a BITMAPFILEHEADER before the data.
|
|
// In this program, we desire all BMP files to contain a BITMAPFILEHEADER, so add it manually
|
|
|
|
m_nHeaderSize = pHeader->guidFormatID == WiaImgFmt_MEMORYBMP ? sizeof(BITMAPFILEHEADER) : 0;
|
|
|
|
// Allocate memory for the image if the size is given in the header
|
|
|
|
if (pHeader != NULL && pHeader->lBufferSize != 0)
|
|
{
|
|
hr = ReAllocBuffer(m_nHeaderSize + pHeader->lBufferSize);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IT_MSG_DATA:
|
|
{
|
|
// Invoke the callback function
|
|
|
|
hr = m_pfnProgressCallback(lStatus, lPercentComplete, m_pProgressCallbackParam);
|
|
|
|
if (FAILED(hr) || hr == S_FALSE)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// If the buffer is not allocated yet and this is the first block,
|
|
// and the transferred image is in BMP format, allocate the buffer
|
|
// according to the size information in the bitmap header
|
|
|
|
if (m_pStream == NULL && lOffset == 0 && m_bBMP)
|
|
{
|
|
LONG nBufferSize = BitmapUtil::GetBitmapSize(pbBuffer);
|
|
|
|
if (nBufferSize != 0)
|
|
{
|
|
hr = ReAllocBuffer(m_nHeaderSize + nBufferSize);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the transfer goes past the buffer, try to expand it
|
|
|
|
if (m_nHeaderSize + lOffset + lLength > m_nDataSize)
|
|
{
|
|
hr = ReAllocBuffer(m_nHeaderSize + lOffset + lLength);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// copy the transfer buffer
|
|
|
|
hr = CopyToBuffer(m_nHeaderSize + lOffset, pbBuffer, lLength);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IT_MSG_STATUS:
|
|
{
|
|
// Invoke the callback function
|
|
|
|
hr = m_pfnProgressCallback(lStatus, lPercentComplete, m_pProgressCallbackParam);
|
|
|
|
if (FAILED(hr) || hr == S_FALSE)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IT_MSG_TERMINATION:
|
|
case IT_MSG_NEW_PAGE:
|
|
{
|
|
if (m_pStream != NULL)
|
|
{
|
|
// For BMP files, we should validate the the image header
|
|
// So, obtain the memory buffer from the stream
|
|
|
|
if (m_bBMP)
|
|
{
|
|
// Since the stream is created using CreateStreamOnHGlobal,
|
|
// we can get the memory buffer with GetHGlobalFromStream.
|
|
|
|
HGLOBAL hBuffer;
|
|
|
|
hr = GetHGlobalFromStream(m_pStream, &hBuffer);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
PBITMAPFILEHEADER pBuffer = (PBITMAPFILEHEADER) GlobalLock(hBuffer);
|
|
|
|
if (pBuffer == NULL)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// Some scroll-fed scanners may return 0 as the bitmap height
|
|
// In this case, calculate the image height and modify the header
|
|
|
|
BitmapUtil::FixBitmapHeight(pBuffer + 1, m_nDataSize, TRUE);
|
|
|
|
// For WiaImgFmt_MEMORYBMP transfers, the WIA service does not
|
|
// include a BITMAPFILEHEADER preceeding the bitmap data.
|
|
// In this case, fill in the BITMAPFILEHEADER structure.
|
|
|
|
if (m_nHeaderSize != 0)
|
|
{
|
|
BitmapUtil::FillBitmapFileHeader(pBuffer + 1, pBuffer);
|
|
}
|
|
|
|
GlobalUnlock(hBuffer);
|
|
}
|
|
|
|
// Store this buffer in the successfully transferred images array
|
|
|
|
hr = StoreBuffer();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDataCallback::ReAllocBuffer
|
|
//
|
|
|
|
HRESULT CDataCallback::ReAllocBuffer(ULONG nSize)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// If m_pStream is not initialized yet, create a new stream object
|
|
|
|
if (m_pStream == 0)
|
|
{
|
|
hr = CreateStreamOnHGlobal(0, TRUE, &m_pStream);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
// Next, set the size of the stream object
|
|
|
|
ULARGE_INTEGER liSize = { nSize };
|
|
|
|
hr = m_pStream->SetSize(liSize);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
m_nDataSize = nSize;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDataCallback::CopyToBuffer
|
|
//
|
|
|
|
HRESULT CDataCallback::CopyToBuffer(ULONG nOffset, LPCVOID pBuffer, ULONG nSize)
|
|
{
|
|
HRESULT hr;
|
|
|
|
// First move the stream pointer to the data offset
|
|
|
|
LARGE_INTEGER liOffset = { nOffset };
|
|
|
|
hr = m_pStream->Seek(liOffset, STREAM_SEEK_SET, 0);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// Next, write the new data to the stream
|
|
|
|
hr = m_pStream->Write(pBuffer, nSize, 0);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDataCallback::StoreBuffer
|
|
//
|
|
|
|
HRESULT CDataCallback::StoreBuffer()
|
|
{
|
|
// Increase the successfully transferred buffers array size
|
|
|
|
IStream **ppStream = (IStream **) CoTaskMemRealloc(
|
|
*m_pppStream,
|
|
(*m_plCount + 1) * sizeof(IStream *)
|
|
);
|
|
|
|
if (ppStream == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
*m_pppStream = ppStream;
|
|
|
|
// Rewind the current buffer
|
|
|
|
LARGE_INTEGER liZero = { 0 };
|
|
|
|
m_pStream->Seek(liZero, STREAM_SEEK_SET, 0);
|
|
|
|
// Store the current buffer as the last successfully transferred buffer
|
|
|
|
(*m_pppStream)[*m_plCount] = m_pStream;
|
|
(*m_pppStream)[*m_plCount]->AddRef();
|
|
|
|
*m_plCount += 1;
|
|
|
|
// Reset the current buffer
|
|
|
|
m_pStream.Release();
|
|
|
|
m_nDataSize = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
}; // namespace WiaWrap
|