2025-11-28 00:35:46 +09:00

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