windows-nt/Source/XPSP1/NT/printscan/ui/wiaacmgr/thrdmsg.cpp

2532 lines
116 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*******************************************************************************
*
* (C) COPYRIGHT MICROSOFT CORPORATION, 1998
*
* TITLE: THRDMSG.CPP
*
* VERSION: 1.0
*
* AUTHOR: ShaunIv
*
* DATE: 9/28/1999
*
* DESCRIPTION: These classes are instantiated for each message posted to the
* background thread. Each is derived from CThreadMessage, and
* is sent to the thread message handler.
*
*******************************************************************************/
#include "precomp.h"
#pragma hdrstop
#include "itranhlp.h"
#include "isuppfmt.h"
#include "wiadevdp.h"
#include "acqmgrcw.h"
#include "propstrm.h"
#include "uiexthlp.h"
#include "flnfile.h"
#include "resource.h"
#include "itranspl.h"
#include "svselfil.h"
#include "uniqfile.h"
#include "mboxex.h"
#include "wiaffmt.h"
#define FILE_CREATION_MUTEX_NAME TEXT("Global\\WiaScannerAndCameraWizardFileNameCreationMutex")
#define UPLOAD_PROGRESS_GRANULARITY 10
#ifndef S_CONTINUE
#define S_CONTINUE ((HRESULT)0x00000002L)
#endif
//
// The delete progress page goes by too quickly, so we will slow it down here
//
#define DELETE_DELAY_BEFORE 1000
#define DELETE_DELAY_DURING 3000
#define DELETE_DELAY_AFTER 1000
//
// Some APIs claim to set the thread's last error, but don't
// For those which don't, we will return E_FAIL. This function
// will not return S_OK
//
inline HRESULT MY_HRESULT_FROM_WIN32( DWORD gle )
{
if (!gle)
{
return E_FAIL;
}
return HRESULT_FROM_WIN32(gle);
}
// -------------------------------------------------
// CGlobalInterfaceTableThreadMessage
// -------------------------------------------------
CGlobalInterfaceTableThreadMessage::CGlobalInterfaceTableThreadMessage( int nMessage, HWND hWndNotify, DWORD dwGlobalInterfaceTableCookie )
: CNotifyThreadMessage( nMessage, hWndNotify ),
m_dwGlobalInterfaceTableCookie(dwGlobalInterfaceTableCookie)
{
}
DWORD CGlobalInterfaceTableThreadMessage::GlobalInterfaceTableCookie(void) const
{
return(m_dwGlobalInterfaceTableCookie);
}
// -------------------------------------------------
// CDownloadThumbnailThreadMessage
// -------------------------------------------------
CDownloadThumbnailsThreadMessage::CDownloadThumbnailsThreadMessage( HWND hWndNotify, const CSimpleDynamicArray<DWORD> &Cookies, HANDLE hCancelEvent )
: CNotifyThreadMessage( TQ_DOWNLOADTHUMBNAIL, hWndNotify ),
m_Cookies(Cookies),
m_hCancelEvent(NULL)
{
DuplicateHandle( GetCurrentProcess(), hCancelEvent, GetCurrentProcess(), &m_hCancelEvent, 0, FALSE, DUPLICATE_SAME_ACCESS );
}
CDownloadThumbnailsThreadMessage::~CDownloadThumbnailsThreadMessage(void)
{
if (m_hCancelEvent)
{
CloseHandle(m_hCancelEvent);
m_hCancelEvent = NULL;
}
}
// Helper function that gets the thumbnail data and creates a DIB from it
static HRESULT DownloadAndCreateThumbnail( IWiaItem *pWiaItem, PBYTE *ppBitmapData, LONG &nWidth, LONG &nHeight, LONG &nBitmapDataLength, GUID &guidPreferredFormat, LONG &nAccessRights, LONG &nImageWidth, LONG &nImageHeight, CAnnotationType &AnnotationType, CSimpleString &strDefExt )
{
#if 0 // defined(DBG)
Sleep(3000);
#endif
WIA_PUSH_FUNCTION((TEXT("DownloadAndCreateThumbnail")));
CComPtr<IWiaPropertyStorage> pIWiaPropertyStorage;
HRESULT hr = pWiaItem->QueryInterface(IID_IWiaPropertyStorage, (void**)&pIWiaPropertyStorage);
if (SUCCEEDED(hr))
{
AnnotationType = AnnotationNone;
CComPtr<IWiaAnnotationHelpers> pWiaAnnotationHelpers;
if (SUCCEEDED(CoCreateInstance( CLSID_WiaDefaultUi, NULL,CLSCTX_INPROC_SERVER, IID_IWiaAnnotationHelpers,(void**)&pWiaAnnotationHelpers )))
{
pWiaAnnotationHelpers->GetAnnotationType( pWiaItem, AnnotationType );
}
const int c_NumProps = 7;
PROPVARIANT PropVar[c_NumProps];
PROPSPEC PropSpec[c_NumProps];
PropSpec[0].ulKind = PRSPEC_PROPID;
PropSpec[0].propid = WIA_IPC_THUMB_WIDTH;
PropSpec[1].ulKind = PRSPEC_PROPID;
PropSpec[1].propid = WIA_IPC_THUMB_HEIGHT;
PropSpec[2].ulKind = PRSPEC_PROPID;
PropSpec[2].propid = WIA_IPC_THUMBNAIL;
PropSpec[3].ulKind = PRSPEC_PROPID;
PropSpec[3].propid = WIA_IPA_PREFERRED_FORMAT;
PropSpec[4].ulKind = PRSPEC_PROPID;
PropSpec[4].propid = WIA_IPA_ACCESS_RIGHTS;
PropSpec[5].ulKind = PRSPEC_PROPID;
PropSpec[5].propid = WIA_IPA_PIXELS_PER_LINE;
PropSpec[6].ulKind = PRSPEC_PROPID;
PropSpec[6].propid = WIA_IPA_NUMBER_OF_LINES;
hr = pIWiaPropertyStorage->ReadMultiple(ARRAYSIZE(PropSpec),PropSpec,PropVar );
if (SUCCEEDED(hr))
{
//
// Save the item type
//
if (VT_CLSID == PropVar[3].vt && PropVar[3].puuid)
{
guidPreferredFormat = *(PropVar[3].puuid);
}
else
{
guidPreferredFormat = IID_NULL;
}
//
// Get the extension for the default format
//
strDefExt = CWiaFileFormat::GetExtension( guidPreferredFormat, TYMED_FILE, pWiaItem );
//
// Save the access rights
//
nAccessRights = PropVar[4].lVal;
if ((PropVar[0].vt == VT_I4 || PropVar[0].vt == VT_UI4) &&
(PropVar[1].vt == VT_I4 || PropVar[1].vt == VT_UI4) &&
(PropVar[2].vt == (VT_UI1|VT_VECTOR)))
{
if (PropVar[2].caub.cElems >= PropVar[0].ulVal * PropVar[1].ulVal)
{
//
// Allocate memory for the bitmap data. It will be freed by the main thread.
//
*ppBitmapData = reinterpret_cast<PBYTE>(LocalAlloc( LPTR, PropVar[2].caub.cElems ));
if (*ppBitmapData)
{
WIA_TRACE((TEXT("We found a thumbnail!")));
CopyMemory( *ppBitmapData, PropVar[2].caub.pElems, PropVar[2].caub.cElems );
nWidth = PropVar[0].ulVal;
nHeight = PropVar[1].ulVal;
nImageWidth = PropVar[5].ulVal;
nImageHeight = PropVar[6].ulVal;
nBitmapDataLength = PropVar[2].caub.cElems;
WIA_TRACE((TEXT("nImageWidth = %d, nImageHeight = %d!"), nImageWidth, nImageHeight ));
}
else
{
hr = E_OUTOFMEMORY;
WIA_PRINTHRESULT((hr,TEXT("Unable to allocate bitmap data")));
}
}
else
{
hr = E_FAIL;
WIA_PRINTHRESULT((hr,TEXT("Invalid bitmap data returned")));
}
}
else
{
hr = E_FAIL;
WIA_ERROR((TEXT("The bitmap data is in the wrong format! %d"),PropVar[2].vt));
}
//
// Free any properties the array contains
//
FreePropVariantArray( ARRAYSIZE(PropVar), PropVar );
}
else
{
WIA_PRINTHRESULT((hr,TEXT("ReadMultiple failed")));
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("QueryInterface on IID_IWiaPropertyStorage failed")));
}
return hr;
}
HRESULT CDownloadThumbnailsThreadMessage::Download(void)
{
WIA_PUSHFUNCTION((TEXT("CDownloadThumbnailsThreadMessage::Download")));
//
// Tell the main thread we are going to start downloading thumbnails
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadThumbnailsThreadNotifyMessage::BeginDownloadAllMessage( m_Cookies.Size() ) );
//
// Get an instance of the GIT
//
CComPtr<IGlobalInterfaceTable> pGlobalInterfaceTable;
HRESULT hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (VOID**)&pGlobalInterfaceTable );
if (SUCCEEDED(hr))
{
//
// m_Cookies.Size() contains the number of thumbnails we need to get
//
for (int i=0;i<m_Cookies.Size() && hr == S_OK;i++)
{
//
// Check to see if we're cancelled. If we are, break out of the loop
//
if (m_hCancelEvent && WAIT_OBJECT_0==WaitForSingleObject(m_hCancelEvent,0))
{
hr = S_FALSE;
break;
}
//
// Get the item from the global interface table
//
CComPtr<IWiaItem> pWiaItem = NULL;
hr = pGlobalInterfaceTable->GetInterfaceFromGlobal( m_Cookies[i], IID_IWiaItem, (void**)&pWiaItem );
if (SUCCEEDED(hr))
{
//
// Get the bitmap data and other properties we are reading now
//
PBYTE pBitmapData = NULL;
GUID guidPreferredFormat;
LONG nAccessRights = 0, nWidth = 0, nHeight = 0, nPictureWidth = 0, nPictureHeight = 0, nBitmapDataLength = 0;
CAnnotationType AnnotationType = AnnotationNone;
CSimpleString strDefExt;
//
// Notify the main thread that we are beginning to download the thumbnail and other props
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadThumbnailsThreadNotifyMessage::BeginDownloadThumbnailMessage( i, m_Cookies[i] ) );
//
// Only send an End message if we were successful
//
if (SUCCEEDED(DownloadAndCreateThumbnail( pWiaItem, &pBitmapData, nWidth, nHeight, nBitmapDataLength, guidPreferredFormat, nAccessRights, nPictureWidth, nPictureHeight, AnnotationType, strDefExt )))
{
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadThumbnailsThreadNotifyMessage::EndDownloadThumbnailMessage( i, m_Cookies[i], pBitmapData, nWidth, nHeight, nBitmapDataLength, guidPreferredFormat, nAccessRights, nPictureWidth, nPictureHeight, AnnotationType, strDefExt ) );
}
}
}
}
//
// Tell the main thread we are done
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadThumbnailsThreadNotifyMessage::EndDownloadAllMessage( hr ) );
return hr;
}
// -------------------------------------------------
// CDownloadImagesThreadMessage
// -------------------------------------------------
CDownloadImagesThreadMessage::CDownloadImagesThreadMessage(
HWND hWndNotify,
const CSimpleDynamicArray<DWORD> &Cookies,
const CSimpleDynamicArray<int> &Rotation,
LPCTSTR pszDirectory,
LPCTSTR pszFilename,
const GUID &guidFormat,
HANDLE hCancelDownloadEvent,
bool bStampTime,
HANDLE hPauseDownloadEvent
)
: CNotifyThreadMessage( TQ_DOWNLOADIMAGE, hWndNotify ),
m_Cookies(Cookies),
m_Rotation(Rotation),
m_strDirectory(pszDirectory),
m_strFilename(pszFilename),
m_guidFormat(guidFormat),
m_hCancelDownloadEvent(NULL),
m_bStampTime(bStampTime),
m_nLastStatusUpdatePercent(-1),
m_bFirstTransfer(true),
m_hPauseDownloadEvent(NULL),
m_hFilenameCreationMutex(NULL)
{
DuplicateHandle( GetCurrentProcess(), hCancelDownloadEvent, GetCurrentProcess(), &m_hCancelDownloadEvent, 0, FALSE, DUPLICATE_SAME_ACCESS );
DuplicateHandle( GetCurrentProcess(), hPauseDownloadEvent, GetCurrentProcess(), &m_hPauseDownloadEvent, 0, FALSE, DUPLICATE_SAME_ACCESS );
m_hFilenameCreationMutex = CreateMutex( NULL, FALSE, FILE_CREATION_MUTEX_NAME );
}
CDownloadImagesThreadMessage::~CDownloadImagesThreadMessage(void)
{
if (m_hCancelDownloadEvent)
{
CloseHandle(m_hCancelDownloadEvent);
m_hCancelDownloadEvent = NULL;
}
if (m_hPauseDownloadEvent)
{
CloseHandle(m_hPauseDownloadEvent);
m_hPauseDownloadEvent = NULL;
}
if (m_hFilenameCreationMutex)
{
CloseHandle(m_hFilenameCreationMutex);
m_hFilenameCreationMutex = NULL;
}
}
int CDownloadImagesThreadMessage::ReportError( HWND hWndNotify, const CSimpleString &strMessage, int nMessageBoxFlags )
{
//
// How long should we wait to find out if this is being handled?
//
const UINT c_nSecondsToWaitForHandler = 10;
//
// Cancel is the default, in case nobody handles the message, or we are out of resources
//
int nResult = CMessageBoxEx::IDMBEX_CANCEL;
//
// This event will be signalled by the handler when it is going to display some UI
//
HANDLE hHandledMessageEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if (hHandledMessageEvent)
{
//
// This event will be signalled when the user has responded
//
HANDLE hRespondedMessageEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if (hRespondedMessageEvent)
{
//
// Create a notification message class and make sure it isn't NULL
//
CDownloadErrorNotificationMessage *pDownloadErrorNotificationMessage = CDownloadErrorNotificationMessage::ReportDownloadError( strMessage, hHandledMessageEvent, hRespondedMessageEvent, nMessageBoxFlags, nResult );
if (pDownloadErrorNotificationMessage)
{
//
// Send the message
//
CThreadNotificationMessage::SendMessage( hWndNotify, pDownloadErrorNotificationMessage );
//
// Wait c_nSecondsToWaitForHandler seconds for someone to decide to handle the message
//
if (WiaUiUtil::MsgWaitForSingleObject(hHandledMessageEvent,c_nSecondsToWaitForHandler*1000))
{
//
// Wait forever for user input
//
if (WiaUiUtil::MsgWaitForSingleObject(hRespondedMessageEvent,INFINITE))
{
//
// Nothing to do.
//
}
}
}
//
// Done with this event
//
CloseHandle(hRespondedMessageEvent);
}
//
// Done with this event
//
CloseHandle(hHandledMessageEvent);
}
return nResult;
}
//
// This function will sort of arbitrarily try to decide if a
// it is possible the user chose an area that is too large
//
static bool ScannerImageSizeSeemsExcessive( IWiaItem *pWiaItem )
{
WIA_PUSHFUNCTION(TEXT("ScannerImageSizeSeemsExcessive"));
//
// Assume it isn't too large
//
bool bResult = false;
LONG nHorizontalExt=0, nVerticalExt=0;
if (PropStorageHelpers::GetProperty( pWiaItem, WIA_IPS_XEXTENT, nHorizontalExt ) && PropStorageHelpers::GetProperty( pWiaItem, WIA_IPS_YEXTENT, nVerticalExt ))
{
LONG nColorDepth=0;
if (PropStorageHelpers::GetProperty( pWiaItem, WIA_IPA_DEPTH, nColorDepth ))
{
WIA_TRACE((TEXT("Scan Size: %d"), (nHorizontalExt * nVerticalExt * nColorDepth) / 8 ));
//
// If an uncompressed scan is larger than 50 MB
//
if ((nHorizontalExt * nVerticalExt * nColorDepth) / 8 > 1024*1024*50)
{
bResult = true;
}
}
}
return bResult;
}
int CDownloadImagesThreadMessage::ReportDownloadError( HWND hWndNotify, IWiaItem *pWiaItem, HRESULT &hr, bool bAllowContinue, bool bPageFeederActive, bool bUsableMultipageFileExists, bool bMultiPageTransfer )
{
WIA_PUSH_FUNCTION((TEXT("CDownloadImagesThreadMessage::ReportDownloadError( hWndNotify: %p, pWiaItem: %p, hr: %08X, bAllowContinue: %d, bPageFeederActive: %d, bUsableMultipageFileExists: %d"), hWndNotify, pWiaItem, hr, bAllowContinue, bPageFeederActive, bUsableMultipageFileExists ));
WIA_PRINTHRESULT((hr,TEXT("HRESULT:")));
//
// Get the device type property
//
LONG lDeviceType = 0;
CComPtr<IWiaItem> pWiaItemRoot;
if (pWiaItem && SUCCEEDED(pWiaItem->GetRootItem(&pWiaItemRoot)))
{
PropStorageHelpers::GetProperty( pWiaItemRoot, WIA_DIP_DEV_TYPE, lDeviceType );
}
//
// Get the actual device type bits
//
lDeviceType = GET_STIDEVICE_TYPE(lDeviceType);
//
// Default message box buttons
//
int nMessageBoxFlags = 0;
//
// Default message
//
CSimpleString strMessage(TEXT(""));
//
// Take a first cut at getting the correct error message
//
switch (hr)
{
case WIA_ERROR_OFFLINE:
//
// The device is disconnected. Nothing we can do here, so just return.
//
return CMessageBoxEx::IDMBEX_CANCEL;
case WIA_ERROR_ITEM_DELETED:
//
// If the item has been deleted, just continue.
//
return CMessageBoxEx::IDMBEX_SKIP;
case WIA_ERROR_BUSY:
nMessageBoxFlags = CMessageBoxEx::MBEX_CANCELRETRY|CMessageBoxEx::MBEX_DEFBUTTON2|CMessageBoxEx::MBEX_ICONWARNING;
strMessage = CSimpleString( IDS_TRANSFER_DEVICEBUSY, g_hInstance );
break;
case WIA_ERROR_PAPER_EMPTY:
nMessageBoxFlags = CMessageBoxEx::MBEX_CANCELRETRY|CMessageBoxEx::MBEX_DEFBUTTON2|CMessageBoxEx::MBEX_ICONWARNING;
strMessage = CSimpleString( IDS_TRANSFER_PAPEREMPTY, g_hInstance );
break;
case E_OUTOFMEMORY:
if (StiDeviceTypeScanner == lDeviceType && ScannerImageSizeSeemsExcessive(pWiaItem))
{
//
// Handle the case where we think the user may have chosen an insane image size
//
nMessageBoxFlags = CMessageBoxEx::MBEX_OK|CMessageBoxEx::MBEX_ICONWARNING;
strMessage = CSimpleString( IDS_TRANSFER_SCANNEDITEMMAYBETOOLARGE, g_hInstance );
break;
}
}
//
// If we still don't have an error message, see if this is a special case
//
if (!nMessageBoxFlags || !strMessage.Length())
{
if (bPageFeederActive)
{
if (bMultiPageTransfer)
{
if (bUsableMultipageFileExists)
{
switch (hr)
{
case WIA_ERROR_PAPER_JAM:
case WIA_ERROR_PAPER_PROBLEM:
//
// We can recover the rest of the file in these cases.
//
nMessageBoxFlags = CMessageBoxEx::MBEX_ICONINFORMATION|CMessageBoxEx::MBEX_YESNO;
strMessage = CSimpleString( IDS_MULTIPAGE_PAPER_PROBLEM, g_hInstance );
break;
default:
nMessageBoxFlags = CMessageBoxEx::MBEX_ICONERROR|CMessageBoxEx::MBEX_OK;
strMessage = CSimpleString( IDS_MULTIPAGE_FATAL_ERROR, g_hInstance );
break;
}
}
else
{
nMessageBoxFlags = CMessageBoxEx::MBEX_ICONERROR|CMessageBoxEx::MBEX_OK;
strMessage = CSimpleString( IDS_MULTIPAGE_FATAL_ERROR, g_hInstance );
}
}
}
}
//
// If we still don't have a message, use the default
//
if (!nMessageBoxFlags || !strMessage.Length())
{
if (bAllowContinue)
{
nMessageBoxFlags = CMessageBoxEx::MBEX_CANCELRETRYSKIPSKIPALL|CMessageBoxEx::MBEX_DEFBUTTON2|CMessageBoxEx::MBEX_ICONWARNING;
strMessage = CSimpleString( IDS_TRANSFER_GENERICFAILURE, g_hInstance );
}
else
{
nMessageBoxFlags = CMessageBoxEx::MBEX_CANCELRETRY|CMessageBoxEx::MBEX_DEFBUTTON1|CMessageBoxEx::MBEX_ICONWARNING;
strMessage = CSimpleString( IDS_TRANSFER_GENERICFAILURE_NO_CONTINUE, g_hInstance );
}
}
//
// Report the error
//
int nResult = ReportError( hWndNotify, strMessage, nMessageBoxFlags );
//
// Special cases, give us a chance to change the hresult and return value
//
if (bPageFeederActive && !bUsableMultipageFileExists && (CMessageBoxEx::IDMBEX_SKIP == nResult || CMessageBoxEx::IDMBEX_SKIPALL == nResult))
{
hr = WIA_ERROR_PAPER_EMPTY;
nResult = CMessageBoxEx::IDMBEX_SKIP;
}
else if (bPageFeederActive && bUsableMultipageFileExists && (WIA_ERROR_PAPER_JAM == hr || WIA_ERROR_PAPER_PROBLEM == hr))
{
if (CMessageBoxEx::IDMBEX_YES == nResult)
{
hr = S_OK;
nResult = CMessageBoxEx::IDMBEX_SKIP;
}
else
{
nResult = CMessageBoxEx::IDMBEX_CANCEL;
}
}
return nResult;
}
static void GetIdealInputFormat( IWiaSupportedFormats *pWiaSupportedFormats, const GUID &guidOutputFormat, GUID &guidInputFormat )
{
//
// If we can get the input format and the output format to be the same, that is best
// If we cannot, we will use DIB, which we can convert to the output format
//
//
// Get the format count
//
LONG nCount = 0;
HRESULT hr = pWiaSupportedFormats->GetFormatCount(&nCount);
if (SUCCEEDED(hr))
{
//
// Search for the output format
//
for (LONG i=0;i<nCount;i++)
{
GUID guidCurrentFormat;
hr = pWiaSupportedFormats->GetFormatType( i, &guidCurrentFormat );
if (SUCCEEDED(hr))
{
//
// If we've found the output format, save it as the input format and return
//
if (guidCurrentFormat == guidOutputFormat)
{
guidInputFormat = guidOutputFormat;
return;
}
}
}
}
//
// If we've gotten this far, we have to use BMP
//
guidInputFormat = WiaImgFmt_BMP;
}
HRESULT CDownloadImagesThreadMessage::GetListOfTransferItems( IWiaItem *pWiaItem, CSimpleDynamicArray<CTransferItem> &TransferItems )
{
if (pWiaItem)
{
//
// Add this item
//
TransferItems.Append(CTransferItem(pWiaItem));
//
// If this item has attachments, enumerate and add them
//
LONG nItemType = 0;
if (SUCCEEDED(pWiaItem->GetItemType(&nItemType)) && (nItemType & WiaItemTypeHasAttachments))
{
CComPtr<IEnumWiaItem> pEnumWiaItem;
if (SUCCEEDED(pWiaItem->EnumChildItems( &pEnumWiaItem )))
{
CComPtr<IWiaItem> pWiaItem;
while (S_OK == pEnumWiaItem->Next(1,&pWiaItem,NULL))
{
TransferItems.Append(CTransferItem(pWiaItem));
pWiaItem = NULL;
}
}
}
}
return TransferItems.Size() ? S_OK : E_FAIL;
}
static int Find( const CSimpleDynamicArray<CTransferItem> &TransferItems, const CSimpleString &strFilename )
{
WIA_PUSH_FUNCTION((TEXT("Find( %s )"),strFilename.String()));
for (int i=0;i<TransferItems.Size();i++)
{
WIA_TRACE((TEXT("Comparing %s to %s"), strFilename.String(), TransferItems[i].Filename().String()));
if (strFilename == TransferItems[i].Filename())
{
WIA_TRACE((TEXT("Returning %d"),i));
return i;
}
}
WIA_TRACE((TEXT("Returning -1")));
return -1;
}
HRESULT CDownloadImagesThreadMessage::ReserveTransferItemFilenames( CSimpleDynamicArray<CTransferItem> &TransferItems, LPCTSTR pszDirectory, LPCTSTR pszFilename, LPCTSTR pszNumberMask, bool bAllowUnNumberedFile, int &nPrevFileNumber )
{
WIA_PUSH_FUNCTION((TEXT("CDownloadImagesThreadMessage::ReserveTransferItemFilenames")));
//
// The maximum number of identical attachments
//
int c_nMaxDuplicateFile = 1000;
//
// Assume success
//
HRESULT hr = S_OK;
//
// We can only release the mutex if we were able to grab it.
//
bool bGrabbedMutex = false;
//
// It is not an error if we can't grab the mutex here
//
if (m_hFilenameCreationMutex && WiaUiUtil::MsgWaitForSingleObject(m_hFilenameCreationMutex,2000))
{
bGrabbedMutex = true;
}
//
// Get the root filename
//
TCHAR szFullPathname[MAX_PATH*2] = TEXT("");
int nFileNumber = NumberedFileName::GenerateLowestAvailableNumberedFileName( 0, szFullPathname, pszDirectory, pszFilename, pszNumberMask, TEXT(""), bAllowUnNumberedFile, nPrevFileNumber );
WIA_TRACE((TEXT("nFileNumber = %d"),nFileNumber));
//
// Make sure we got a valid file number
//
if (nFileNumber >= 0)
{
//
// Now loop through the transfer items for this item and create a unique filename for each
//
for (int nCurrTransferItem=0;nCurrTransferItem<TransferItems.Size();nCurrTransferItem++)
{
CSimpleString strFilename = CSimpleString(szFullPathname);
CSimpleString strExtension = CWiaFileFormat::GetExtension(TransferItems[nCurrTransferItem].OutputFormat(),TransferItems[nCurrTransferItem].MediaType(),TransferItems[nCurrTransferItem].WiaItem() );
//
// Now we are going to make sure there are no existing files with the same name
// in this transfer group. This could be caused by having multiple attachments of the same
// type. So we are going to loop until we find a filename that is unused. The first file
// will be named like this:
//
// File NNN.ext
//
// Subsequent files will be named like this:
//
// File NNN-X.ext
// File NNN-Y.ext
// File NNN-Z.ext
// ...
//
bool bFoundUniqueFile = false;
for (int nCurrDuplicateCheckIndex=1;nCurrDuplicateCheckIndex<c_nMaxDuplicateFile && !bFoundUniqueFile;nCurrDuplicateCheckIndex++)
{
//
// Append the extension, if there is one
//
if (strExtension.Length())
{
strFilename += TEXT(".");
strFilename += strExtension;
}
//
// If this filename exists in the transfer items list, append the new suffix, and remain in the loop.
//
WIA_TRACE((TEXT("Calling Find on %s"), strFilename.String()));
if (Find(TransferItems,strFilename) >= 0)
{
strFilename = szFullPathname;
strFilename += CSimpleString().Format(IDS_DUPLICATE_FILENAME_MASK,g_hInstance,nCurrDuplicateCheckIndex);
}
//
// Otherwise, we are done.
//
else
{
bFoundUniqueFile = true;
}
}
//
// Make sure we found a unique filename for this set of files
//
WIA_TRACE((TEXT("strFilename = %s"), strFilename.String()));
if (bFoundUniqueFile && strFilename.Length())
{
TransferItems[nCurrTransferItem].Filename(strFilename);
hr = TransferItems[nCurrTransferItem].OpenPlaceholderFile();
if (FAILED(hr))
{
break;
}
}
//
// If we didn't find a valid name, exit the loop and set the return value to an error
//
else
{
hr = E_FAIL;
break;
}
}
//
// Save this number so the next search starts here
//
nPrevFileNumber = nFileNumber;
}
else
{
WIA_ERROR((TEXT("NumberedFileName::GenerateLowestAvailableNumberedFileName returned %d"), nFileNumber ));
hr = E_FAIL;
}
//
// If we grabbed the mutex, release it
//
if (bGrabbedMutex)
{
ReleaseMutex(m_hFilenameCreationMutex);
}
WIA_CHECK_HR(hr,"CDownloadImagesThreadMessage::ReserveTransferItemFilenames");
return hr;
}
BOOL CDownloadImagesThreadMessage::GetCancelledState()
{
BOOL bResult = FALSE;
//
// First, wait until we are not paused
//
if (m_hPauseDownloadEvent)
{
WiaUiUtil::MsgWaitForSingleObject(m_hPauseDownloadEvent,INFINITE);
}
//
// Then check to see if we have been cancelled
//
if (m_hCancelDownloadEvent && WAIT_TIMEOUT!=WaitForSingleObject(m_hCancelDownloadEvent,0))
{
bResult = TRUE;
}
return bResult;
}
static void CloseAndDeletePlaceholderFiles(CSimpleDynamicArray<CTransferItem> &TransferItems)
{
for (int nCurrTransferItem=0;nCurrTransferItem<TransferItems.Size();nCurrTransferItem++)
{
if (TransferItems[nCurrTransferItem].Filename().Length())
{
TransferItems[nCurrTransferItem].DeleteFile();
}
}
}
static void SnapExtentToRotatableSize( IUnknown *pUnknown, const GUID &guidFormat )
{
WIA_PUSH_FUNCTION((TEXT("SnapExtentToRotatableSize")));
//
// Make sure we have a valid pointer
//
if (!pUnknown)
{
WIA_TRACE((TEXT("Invalid pointer")));
return;
}
//
// Make sure we have a JPEG file.
//
if (WiaImgFmt_JPEG != guidFormat)
{
return;
}
//
// Make sure we can read the access flags
//
ULONG nHorizontalAccessFlags=0, nVerticalAccessFlags=0;
if (!PropStorageHelpers::GetPropertyAccessFlags( pUnknown, WIA_IPS_XEXTENT, nHorizontalAccessFlags ) ||
!PropStorageHelpers::GetPropertyAccessFlags( pUnknown, WIA_IPS_YEXTENT, nVerticalAccessFlags ))
{
WIA_TRACE((TEXT("Unable to read access flags")));
return;
}
//
// Make sure we have read/write access to the extent properties
//
if ((WIA_PROP_RW & nHorizontalAccessFlags)==0 || (WIA_PROP_RW & nVerticalAccessFlags)==0)
{
WIA_TRACE((TEXT("Invalid access flags")));
return;
}
//
// Get the ranges
//
PropStorageHelpers::CPropertyRange HorizontalRange, VerticalRange;
if (!PropStorageHelpers::GetPropertyRange( pUnknown, WIA_IPS_XEXTENT, HorizontalRange ) ||
!PropStorageHelpers::GetPropertyRange( pUnknown, WIA_IPS_YEXTENT, VerticalRange ))
{
WIA_TRACE((TEXT("Unable to read ranges")));
return;
}
//
// Make sure they are valid ranges. We aren't going to mess with it if it doesn't have a step value of 1
//
if (!HorizontalRange.nMax || HorizontalRange.nStep != 1 || !VerticalRange.nMax || VerticalRange.nStep != 1)
{
WIA_TRACE((TEXT("Invalid ranges")));
return;
}
//
// Get the current values
//
LONG nHorizontalExt=0, nVerticalExt=0;
if (!PropStorageHelpers::GetProperty( pUnknown, WIA_IPS_XEXTENT, nHorizontalExt ) ||
!PropStorageHelpers::GetProperty( pUnknown, WIA_IPS_YEXTENT, nVerticalExt ))
{
WIA_TRACE((TEXT("Can't read current extents")));
return;
}
//
// Round to the nearest 8, ensuring we don't go over the maximum extent (which is often not a multiple of 16, but oh well)
//
PropStorageHelpers::SetProperty( pUnknown, WIA_IPS_XEXTENT, WiaUiUtil::Min( WiaUiUtil::Align( nHorizontalExt, 16 ), HorizontalRange.nMax ) );
PropStorageHelpers::SetProperty( pUnknown, WIA_IPS_YEXTENT, WiaUiUtil::Min( WiaUiUtil::Align( nVerticalExt, 16 ), VerticalRange.nMax ) );
}
static void UpdateFolderTime( LPCTSTR pszFolder )
{
if (pszFolder && lstrlen(pszFolder))
{
FILETIME ftCurrent = {0};
GetSystemTimeAsFileTime(&ftCurrent);
//
// Private flag 0x100 lets us open a directory in write access
//
HANDLE hFolder = CreateFile( pszFolder, GENERIC_READ | 0x100, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL );
if (INVALID_HANDLE_VALUE != hFolder)
{
SetFileTime( hFolder, NULL, NULL, &ftCurrent );
CloseHandle( hFolder );
}
}
}
static bool FileExistsAndContainsData( LPCTSTR pszFileName )
{
bool bResult = false;
//
// Make sure we have a filename
//
if (pszFileName && lstrlen(pszFileName))
{
//
// Make sure the file exists
//
if (NumberedFileName::DoesFileExist(pszFileName))
{
//
// Attempt to open the file
//
HANDLE hFile = CreateFile( pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
if (INVALID_HANDLE_VALUE != hFile)
{
//
// Get the file size and make sure we didn't have an error
//
ULARGE_INTEGER nFileSize = {0};
nFileSize.LowPart = GetFileSize( hFile, &nFileSize.HighPart );
if (nFileSize.LowPart != INVALID_FILE_SIZE && GetLastError() == NO_ERROR)
{
if (nFileSize.QuadPart != 0)
{
//
// Success
//
bResult = true;
}
}
CloseHandle( hFile );
}
}
}
return bResult;
}
HRESULT CDownloadImagesThreadMessage::Download(void)
{
WIA_PUSH_FUNCTION((TEXT("CDownloadImagesThreadMessage::Download")));
CDownloadedFileInformationList DownloadedFileInformationList;
//
// Will be set to true if the user cancels
//
bool bCancelled = false;
//
// Will be set to true if some error prevents us from continuing
//
bool bStopTransferring = false;
//
// Tell the notification window we are going to start downloading images
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::BeginDownloadAllMessage( m_Cookies.Size() ) );
//
// We will sometimes have more error information than we can fit in an HRESULT
//
CSimpleString strExtendedErrorInformation = TEXT("");
//
// Get an instance of the GIT
//
CComPtr<IGlobalInterfaceTable> pGlobalInterfaceTable;
HRESULT hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (VOID**)&pGlobalInterfaceTable );
if (SUCCEEDED(hr))
{
//
// Get an instance of our transfer helper classes
//
CComPtr<IWiaTransferHelper> pWiaTransferHelper;
hr = CoCreateInstance( CLSID_WiaDefaultUi, NULL, CLSCTX_INPROC_SERVER, IID_IWiaTransferHelper, (void**)&pWiaTransferHelper );
if (SUCCEEDED(hr))
{
//
// Get an instance of our helper class for identifying supported formats
//
CComPtr<IWiaSupportedFormats> pWiaSupportedFormats;
hr = pWiaTransferHelper->QueryInterface( IID_IWiaSupportedFormats, (void**)&pWiaSupportedFormats );
if (SUCCEEDED(hr))
{
//
// We (this) are an instance of the callback class. Get an interface pointer to it.
//
CComPtr<IWiaDataCallback> pWiaDataCallback;
hr = this->QueryInterface( IID_IWiaDataCallback, (void**)&pWiaDataCallback );
if (SUCCEEDED(hr))
{
//
// Initialize the uniqueness list
//
CFileUniquenessList FileUniquenessList( m_strDirectory );
//
// Skip all download errors?
//
bool bSkipAllDownloadErrors = false;
//
// If the most recent error was skipped, set this to true to ensure
// we don't pass back an error on the last image
//
bool bLastErrorSkipped = false;
//
// Save all or delete all duplicates?
//
int nPersistentDuplicateResult = 0;
//
// Save the previous file number, so we don't have to search the whole range of files
// to find an open section
//
int nPrevFileNumber = NumberedFileName::FindHighestNumberedFile( m_strDirectory, m_strFilename );
//
// Loop through all of the images
//
for ( int nCurrentImage=0; nCurrentImage<m_Cookies.Size() && !bCancelled && !bStopTransferring; nCurrentImage++ )
{
WIA_TRACE((TEXT("Preparing to transfer the %d'th image (Cookie %08X)"), nCurrentImage, m_Cookies[nCurrentImage] ));
//
// Assume we won't skip any transfer error that occurs
//
bLastErrorSkipped = false;
//
// Save the current cookie ID for the callback
//
m_nCurrentCookie = m_Cookies[nCurrentImage];
//
// If we have been cancelled, exit the loop
//
if (GetCancelledState())
{
hr = S_FALSE;
bCancelled = true;
break;
}
//
// Get the IWiaItem interface pointer from the GIT
//
CComPtr<IWiaItem> pWiaItem;
hr = pGlobalInterfaceTable->GetInterfaceFromGlobal(m_Cookies[nCurrentImage], IID_IWiaItem, (void**)&pWiaItem );
if (SUCCEEDED(hr))
{
bool bInPageFeederMode = false;
//
// Ensure the image is a multiple of eight pixels in size
//
SnapExtentToRotatableSize(pWiaItem,m_guidFormat);
//
// Get the root item
//
CComPtr<IWiaItem> pWiaItemRoot;
if (SUCCEEDED(pWiaItem->GetRootItem(&pWiaItemRoot)))
{
LONG nPaperSource = 0;
if (PropStorageHelpers::GetProperty( pWiaItemRoot, WIA_DPS_DOCUMENT_HANDLING_SELECT, static_cast<LONG>(nPaperSource)))
{
if (nPaperSource & FEEDER)
{
bInPageFeederMode = true;
}
}
}
//
// Download the thumbnail, in case we haven't already
//
GUID guidPreferredFormat;
LONG nAccessRights;
PBYTE pBitmapData = NULL;
LONG nWidth = 0, nHeight = 0, nPictureWidth = 0, nPictureHeight = 0, nBitmapDataLength = 0;
CAnnotationType AnnotationType = AnnotationNone;
CSimpleString strDefExt;
if (SUCCEEDED(DownloadAndCreateThumbnail( pWiaItem, &pBitmapData, nWidth, nHeight, nBitmapDataLength, guidPreferredFormat, nAccessRights, nPictureWidth, nPictureHeight, AnnotationType, strDefExt )))
{
WIA_TRACE((TEXT("DownloadAndCreateThumbnail succeeded")));
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadThumbnailsThreadNotifyMessage::EndDownloadThumbnailMessage( nCurrentImage, m_Cookies[nCurrentImage], pBitmapData, nWidth, nHeight, nBitmapDataLength, guidPreferredFormat, nAccessRights, nPictureWidth, nPictureHeight, AnnotationType, strDefExt ) );
}
//
// If we are a scanner, and we are in feeder mode,
// determine whether or not the selected format is available
// in multipage format. This won't work if the requested
// format is IID_NULL, so it rules out cameras implicitly
// (since we only transfer camera items in their default
// format.
//
LONG nMediaType = TYMED_FILE;
if (m_guidFormat != IID_NULL && bInPageFeederMode)
{
//
// Initialize the supported format helper for multipage files
//
if (SUCCEEDED(pWiaSupportedFormats->Initialize( pWiaItem, TYMED_MULTIPAGE_FILE )))
{
//
// See if there are any multipage formats supported
//
LONG nFormatCount;
if (SUCCEEDED(pWiaSupportedFormats->GetFormatCount( &nFormatCount )))
{
//
// Loop through the formats looking for the requested format
//
for (LONG nCurrFormat = 0;nCurrFormat<nFormatCount;nCurrFormat++)
{
//
// Get the format
//
GUID guidCurrFormat = IID_NULL;
if (SUCCEEDED(pWiaSupportedFormats->GetFormatType(nCurrFormat,&guidCurrFormat)))
{
//
// If this is the same format, store the media type
//
if (guidCurrFormat == m_guidFormat)
{
nMediaType = TYMED_MULTIPAGE_FILE;
}
}
}
}
}
}
//
// Initialize the supported formats helper by telling it we are saving to a file or a multipage
// file
//
hr = WIA_FORCE_ERROR(FE_WIAACMGR,1,pWiaSupportedFormats->Initialize( pWiaItem, nMediaType ));
if (SUCCEEDED(hr))
{
//
// Get the default format
//
GUID guidDefaultFormat = IID_NULL;
hr = pWiaSupportedFormats->GetDefaultClipboardFileFormat( &guidDefaultFormat );
if (SUCCEEDED(hr))
{
//
// Get the output format. If the requested format is IID_NULL, we want to use the default format as the input format and output format (i.e., the preferrerd format).
//
GUID guidOutputFormat, guidInputFormat;
if (IID_NULL == m_guidFormat)
{
guidOutputFormat = guidInputFormat = guidDefaultFormat;
}
else
{
guidOutputFormat = m_guidFormat;
GetIdealInputFormat( pWiaSupportedFormats, m_guidFormat, guidInputFormat );
}
//
// Verify the rotation setting is legal. If it isn't, clear the rotation angle.
//
if (m_Rotation[nCurrentImage] && guidOutputFormat != IID_NULL && nPictureWidth && nPictureHeight && !WiaUiUtil::CanWiaImageBeSafelyRotated(guidOutputFormat,nPictureWidth,nPictureHeight))
{
m_Rotation[nCurrentImage] = 0;
}
//
// Number of pages per scan. We increment it at the end of each successful acquire.
// This way, if we get an out of paper error when we haven't scanned any pages,
// we can tell the user
//
int nPageCount = 0;
//
// We will store all of the successfully downloaded files for the following loop
// in this list.
//
// If this is a single image transfer, that will be one filename.
// If it is a multi-page transfer, it will be:
//
// (a) if the file format supports multi-page files (TIFF)--it will be one
// filename
// (b) if the file format doesn't support multi-page files (everything but TIFF)--
// it will be multiple files
//
CDownloadedFileInformationList CurrentFileInformationList;
//
// This is the loop where we keep scanning pages until either:
//
// (a) the ADF-active device returns out-of-paper or
// (b) we have retrieved one image from a non-ADF-active device
//
bool bContinueScanningPages = true;
while (bContinueScanningPages)
{
//
// Just before retrieving, let's make sure we've not been cancelled. If we are, break out of the loop
// and set the HRESULT to S_FALSE (which signifies cancel)
//
if (GetCancelledState())
{
hr = S_FALSE;
bCancelled = true;
break;
}
//
// Get the item and the list of any attached items
//
CSimpleDynamicArray<CTransferItem> TransferItems;
hr = WIA_FORCE_ERROR(FE_WIAACMGR,2,GetListOfTransferItems( pWiaItem, TransferItems ));
if (SUCCEEDED(hr))
{
//
// Set the output format of the first item (the image) to the desired output format
//
TransferItems[0].InputFormat(guidInputFormat);
TransferItems[0].OutputFormat(guidOutputFormat);
TransferItems[0].MediaType(nMediaType);
//
// Try to create the sub-directory. Don't worry about failing. We will catch any failures on the next call.
//
CAcquisitionManagerControllerWindow::RecursiveCreateDirectory(m_strDirectory);
//
// Find the correct filename, and reserve it
//
hr = WIA_FORCE_ERROR(FE_WIAACMGR,3,ReserveTransferItemFilenames( TransferItems, m_strDirectory, m_strFilename, CSimpleString(IDS_NUMBER_MASK,g_hInstance), m_Cookies.Size()==1 && !bInPageFeederMode, nPrevFileNumber ));
if (SUCCEEDED(hr))
{
//
// Loop through each item in the transfer list
//
for (int nCurrentTransferItem=0;nCurrentTransferItem<TransferItems.Size();nCurrentTransferItem++)
{
//
// Save the final filename
//
CSimpleString strFinalFilename = TransferItems[nCurrentTransferItem].Filename();
//
// Create a temporary file to hold the unrotated/unconverted image
//
CSimpleString strIntermediateFilename = WiaUiUtil::CreateTempFileName();
//
// Make sure the filenames are valid
//
if (strFinalFilename.Length() && strIntermediateFilename.Length())
{
//
// Tell the notification window we are starting to download this item
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::BeginDownloadImageMessage( nCurrentImage, m_Cookies[nCurrentImage], strFinalFilename ) );
//
// We are going to repeat failed images in this loop until the user cancels or skips them
//
bool bRetry = false;
do
{
//
// If this is a multi-page file transfer, set the page count to 0 (transfer all pages)
//
if (TYMED_MULTIPAGE_FILE == TransferItems[nCurrentTransferItem].MediaType())
{
//
// Get the root item and set the page count to 0
//
CComPtr<IWiaItem> pWiaItemRoot;
if (SUCCEEDED(TransferItems[nCurrentTransferItem].WiaItem()->GetRootItem(&pWiaItemRoot)))
{
PropStorageHelpers::SetProperty( pWiaItemRoot, WIA_DPS_PAGES, 0 );
}
}
else
{
//
// Get the root item and set the page count to 1
//
CComPtr<IWiaItem> pWiaItemRoot;
if (SUCCEEDED(TransferItems[nCurrentTransferItem].WiaItem()->GetRootItem(&pWiaItemRoot)))
{
PropStorageHelpers::SetProperty( pWiaItemRoot, WIA_DPS_PAGES, 1 );
}
}
//
// Turn off preview mode
//
PropStorageHelpers::SetProperty( pWiaItem, WIA_DPS_PREVIEW, WIA_FINAL_SCAN );
//
// Download the file using our helper class
//
hr = WIA_FORCE_ERROR(FE_WIAACMGR,4,pWiaTransferHelper->TransferItemFile( TransferItems[nCurrentTransferItem].WiaItem(), NotifyWindow(), WIA_TRANSFERHELPER_NOPROGRESS|WIA_TRANSFERHELPER_PRESERVEFAILEDFILE, TransferItems[nCurrentTransferItem].InputFormat(), CSimpleStringConvert::WideString(strIntermediateFilename).String(), pWiaDataCallback, TransferItems[nCurrentTransferItem].MediaType()));
//
// Check to if the download was cancelled
//
if (GetCancelledState())
{
hr = S_FALSE;
bCancelled = true;
CloseAndDeletePlaceholderFiles(TransferItems);
break;
}
//
// We handle this special return differently later on
//
if (nPageCount && WIA_ERROR_PAPER_EMPTY == hr)
{
break;
}
//
// We will not treat this as an error
//
if (WIA_ERROR_ITEM_DELETED == hr)
{
break;
}
WIA_PRINTHRESULT((hr,TEXT("pWiaTransferHelper->TransferItemFile returned")));
//
// If there was a failure, we are going to aler the user
//
if (FAILED(hr))
{
int nResult;
bool bUsableMultipageFileExists = false;
if (bInPageFeederMode)
{
if (TYMED_MULTIPAGE_FILE == TransferItems[nCurrentTransferItem].MediaType())
{
if (FileExistsAndContainsData(strIntermediateFilename))
{
bUsableMultipageFileExists = true;
}
}
}
//
// If the user wants to skip all the images with download errors, set the result to SKIP
//
if (bSkipAllDownloadErrors)
{
nResult = CMessageBoxEx::IDMBEX_SKIP;
if (bInPageFeederMode && !bUsableMultipageFileExists)
{
hr = WIA_ERROR_PAPER_EMPTY;
}
else if (bInPageFeederMode && bUsableMultipageFileExists && (WIA_ERROR_PAPER_JAM == hr || WIA_ERROR_PAPER_PROBLEM == hr))
{
hr = S_OK;
}
}
else
{
//
// Tell the notification window about the failure, and get a user decision
//
nResult = ReportDownloadError( NotifyWindow(), pWiaItem, hr, (m_Cookies.Size() != 1 || bInPageFeederMode), bInPageFeederMode, bUsableMultipageFileExists, TYMED_MULTIPAGE_FILE == TransferItems[nCurrentTransferItem].MediaType() );
}
if (CMessageBoxEx::IDMBEX_CANCEL == nResult)
{
bCancelled = true;
break;
}
else if (CMessageBoxEx::IDMBEX_SKIP == nResult)
{
bRetry = false;
bLastErrorSkipped = true;
}
else if (CMessageBoxEx::IDMBEX_SKIPALL == nResult)
{
bSkipAllDownloadErrors = true;
bRetry = false;
}
else if (CMessageBoxEx::IDMBEX_RETRY == nResult)
{
bRetry = true;
}
else
{
bRetry = false;
}
}
else
{
bRetry = false;
}
//
// If the transfer returned S_FALSE, the user chose to cancel
//
if (S_FALSE == hr)
{
bCancelled = true;
}
}
while (bRetry);
//
// Close the file so we can overwrite it or delete it
//
TransferItems[nCurrentTransferItem].ClosePlaceholderFile();
//
// We only do this if and if the user didn't cancel and it didn't fail
//
if (S_OK == hr)
{
//
// If we need to rotate, and we are on the anchor image, perform the rotation
//
if (nCurrentTransferItem==0 && m_Rotation[nCurrentImage] != 0)
{
hr = WIA_FORCE_ERROR(FE_WIAACMGR,5,m_GdiPlusHelper.Rotate( CSimpleStringConvert::WideString(strIntermediateFilename).String(), CSimpleStringConvert::WideString(strFinalFilename).String(), m_Rotation[nCurrentImage], guidOutputFormat ));
}
else if (nCurrentTransferItem==0 && guidInputFormat != guidOutputFormat)
{
//
// Else if are saving as a different file type, use the conversion filter
//
hr = WIA_FORCE_ERROR(FE_WIAACMGR,6,m_GdiPlusHelper.Convert( CSimpleStringConvert::WideString(strIntermediateFilename).String(), CSimpleStringConvert::WideString(strFinalFilename).String(), guidOutputFormat ));
}
else
{
//
// Otherwise copy/delete or move the actual file over
//
hr = WIA_FORCE_ERROR(FE_WIAACMGR,7,WiaUiUtil::MoveOrCopyFile( strIntermediateFilename, strFinalFilename ));
WIA_PRINTHRESULT((hr,TEXT("WiaUiUtil::MoveOrCopyFile returned")));
}
//
// If we hit an error here, we are going to stop transferring
//
if (FAILED(hr))
{
bStopTransferring = true;
switch (hr)
{
case HRESULT_FROM_WIN32(ERROR_DISK_FULL):
strExtendedErrorInformation.LoadString( IDS_DISKFULL, g_hInstance );
break;
default:
strExtendedErrorInformation.Format( IDS_UNABLE_TO_SAVE_FILE, g_hInstance, strFinalFilename.String() );
break;
}
}
}
//
// Save a flag that says whether or not this was a duplicate image. If it was, we don't want to delete
// it in case of an error or the user cancelling.
//
bool bDuplicateImage = false;
//
// Check to if the download was cancelled
//
if (GetCancelledState())
{
hr = S_FALSE;
bCancelled = true;
CloseAndDeletePlaceholderFiles(TransferItems);
break;
}
//
// Only check for duplicates if everything is OK AND
// we are not on page one of a multi-page scan AND
// we are on the anchor image (not an attachment).
//
// If we are on page 1 of a multipage scan, we have
// to save the first name, because we may be overwriting
// it with a multipage formatted file later. In other
// words, we may be changing the file after this point,
// which is not true in any other case.
//
if (nCurrentTransferItem==0 && TransferItems[nCurrentTransferItem].MediaType() == TYMED_FILE && S_OK == hr && !(bInPageFeederMode && !nPageCount))
{
//
// Check to see if this file has already been downloaded
//
int nIdenticalFileIndex = FileUniquenessList.FindIdenticalFile(strFinalFilename,true);
if (nIdenticalFileIndex != -1)
{
//
// Get the duplicate's name
//
CSimpleString strDuplicateName = FileUniquenessList.GetFileName(nIdenticalFileIndex);
//
// Make sure the name is not empty
//
if (strDuplicateName.Length())
{
//
// Create the message to give the user
//
CSimpleString strMessage;
strMessage.Format( IDS_DUPLICATE_FILE_WARNING, g_hInstance );
//
// Ask the user if they want to keep the new one
//
int nResult;
if (CMessageBoxEx::IDMBEX_YESTOALL == nPersistentDuplicateResult)
{
nResult = CMessageBoxEx::IDMBEX_YES;
}
else if (CMessageBoxEx::IDMBEX_NOTOALL == nPersistentDuplicateResult)
{
nResult = CMessageBoxEx::IDMBEX_NO;
}
else
{
nResult = ReportError( NotifyWindow(), strMessage, CMessageBoxEx::MBEX_YESYESTOALLNONOTOALL|CMessageBoxEx::MBEX_ICONQUESTION );
WIA_TRACE((TEXT("User's Response to \"Save Duplicate? %08X\""), nResult ));
}
//
// Save persistent responses
//
if (nResult == CMessageBoxEx::IDMBEX_YESTOALL)
{
nPersistentDuplicateResult = CMessageBoxEx::IDMBEX_YESTOALL;
nResult = CMessageBoxEx::IDMBEX_YES;
}
else if (nResult == CMessageBoxEx::IDMBEX_NOTOALL)
{
nPersistentDuplicateResult = CMessageBoxEx::IDMBEX_NOTOALL;
nResult = CMessageBoxEx::IDMBEX_NO;
}
//
// If the user doesn't want to keep new one, delete it, and save the filename
//
if (nResult == CMessageBoxEx::IDMBEX_NO)
{
DeleteFile( strFinalFilename );
strFinalFilename = strDuplicateName;
bDuplicateImage = true;
}
}
}
}
//
// If everything is *still* OK
//
if (S_OK == hr)
{
//
// Save the audio, if any
//
CSimpleString strAudioFilename;
HRESULT hResAudio = WiaUiUtil::SaveWiaItemAudio( pWiaItem, strFinalFilename, strAudioFilename );
if (SUCCEEDED(hResAudio) && strAudioFilename.Length())
{
CurrentFileInformationList.Append(CDownloadedFileInformation(strAudioFilename,false,m_Cookies[nCurrentImage],false));
}
else
{
WIA_PRINTHRESULT((hResAudio,TEXT("SaveWiaItemAudio failed!")));
}
//
// Set the file time to the item time
//
if (m_bStampTime)
{
HRESULT hResFileTime = WiaUiUtil::StampItemTimeOnFile( pWiaItem, strFinalFilename );
if (!SUCCEEDED(hResFileTime))
{
WIA_PRINTHRESULT((hResAudio,TEXT("StampItemTimeOnFile failed!")));
}
}
//
// Save the downloaded file information. Mark it "IncludeInFileCount" if it is the first image in the group.
//
CurrentFileInformationList.Append(CDownloadedFileInformation(strFinalFilename,bDuplicateImage==false,m_Cookies[nCurrentImage],nCurrentTransferItem==0));
}
else
{
//
// Clean up the final filename, in case of failure
//
if (!DeleteFile( strFinalFilename ))
{
WIA_PRINTHRESULT((MY_HRESULT_FROM_WIN32(GetLastError()),TEXT("DeleteFile(%s) failed!"), strFinalFilename.String()));
}
}
//
// Make sure the intermediate file is removed
//
if (!DeleteFile( strIntermediateFilename ))
{
WIA_PRINTHRESULT((MY_HRESULT_FROM_WIN32(GetLastError()),TEXT("DeleteFile(%s) failed!"), strIntermediateFilename.String()));
}
//
// Tell the notify window we are done with this image
//
if (hr != WIA_ERROR_PAPER_EMPTY)
{
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::EndDownloadImageMessage( nCurrentImage, m_Cookies[nCurrentImage], strFinalFilename, hr ) );
}
}
else
{
bStopTransferring = true;
hr = E_OUTOFMEMORY;
}
}
}
else
{
//
// Tell the user specifically why we couldn't create the placeholder file
//
WIA_PRINTHRESULT((hr,TEXT("Unable to create a placeholder file")));
bStopTransferring = true;
switch (hr)
{
case E_ACCESSDENIED:
strExtendedErrorInformation.LoadString( IDS_ERROR_ACCESS_DENIED, g_hInstance );
break;
}
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("Unable to create an output filename")));
bStopTransferring = true;
}
//
// Assume we will not be continuing
//
bContinueScanningPages = false;
//
// If we are out of paper, and we have one or more pages already, we are done, and there are no errors
//
if (nPageCount && WIA_ERROR_PAPER_EMPTY == hr)
{
//
// If we are doing single-page TIFF output, we can create a multi-page TIFF file
//
if (Gdiplus::ImageFormatTIFF == guidOutputFormat && TYMED_FILE == nMediaType)
{
//
// Get the list of all files we are going to concatenate
//
CSimpleDynamicArray<CSimpleStringWide> AllFiles;
if (SUCCEEDED(CurrentFileInformationList.GetAllFiles(AllFiles)) && AllFiles.Size())
{
//
// Create a temporary filename to save the images to, so we don't overwrite the original
//
CSimpleStringWide strwTempOutputFilename = CSimpleStringConvert::WideString(WiaUiUtil::CreateTempFileName(0));
if (strwTempOutputFilename.Length())
{
//
// Save the images as a multi-page TIFF file
//
if (SUCCEEDED(m_GdiPlusHelper.SaveMultipleImagesAsMultiPage( AllFiles, strwTempOutputFilename, Gdiplus::ImageFormatTIFF )))
{
//
// Save the first entry in the current list
//
CDownloadedFileInformation MultipageOutputFilename = CurrentFileInformationList[0];
//
// Mark the first entry as non-deletable
//
CurrentFileInformationList[0].DeleteOnError(false);
//
// Try to move the file from the temp folder to the final destination
//
if (SUCCEEDED(WiaUiUtil::MoveOrCopyFile( CSimpleStringConvert::NaturalString(strwTempOutputFilename), CurrentFileInformationList[0].Filename())))
{
//
// Delete all of the files (they are now part of the multi-page TIFF
//
CurrentFileInformationList.DeleteAllFiles();
//
// Destroy the list
//
CurrentFileInformationList.Destroy();
//
// Add the saved first image back to the list
//
CurrentFileInformationList.Append(MultipageOutputFilename);
}
else
{
//
// If we can't move the file, we have to delete it, to make sure we don't leave
// abandoned files laying around
//
DeleteFile( CSimpleStringConvert::NaturalString(strwTempOutputFilename) );
}
}
}
}
}
//
// WIA_ERROR_PAPER_EMPTY is not an error in this context, so clear it
//
hr = S_OK;
}
else if (hr == S_OK)
{
//
// if we are in page feeder mode, we should continue scanning since we didn't get any errors
//
if (bInPageFeederMode && TYMED_FILE == nMediaType)
{
bContinueScanningPages = true;
}
//
// One more page transferred
//
nPageCount++;
}
} // End while loop
//
// Add all of the successfully transferred files to the complete file list
//
DownloadedFileInformationList.Append(CurrentFileInformationList);
}
else
{
WIA_PRINTHRESULT((hr,TEXT("pWiaSupportedFormats->GetDefaultClipboardFileFormat() failed")));
bStopTransferring = true;
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("pWiaSupportedFormats->Initialize() failed")));
bStopTransferring = true;
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("Unable to retrieve interface %08X from the global interface table"),m_Cookies[nCurrentImage]));
bStopTransferring = true;
}
//
// Do this at the end of the loop, so we can pick up the item cookie it failed on
// This is only for handling errors specially
//
if (FAILED(hr))
{
//
// Make sure we send a message to the UI to alert it to errors, so it can update progress.
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::EndDownloadImageMessage( nCurrentImage, m_Cookies[nCurrentImage], TEXT(""), hr ) );
} // end if (FAILED)
} // end for
//
// If there was a download error, but the user chose to suppress these errors
// AND there were some files downloaded, don't return an error.
//
if (FAILED(hr) && bLastErrorSkipped && DownloadedFileInformationList.Size())
{
hr = S_OK;
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("QueryInterface failed on IID_IWiaDataCallback")));
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("QueryInterface failed on IID_IWiaSupportedFormats")));
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("Unable to create the transfer helper class")));
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("Unable to create the global interface table")));
}
//
// If the user cancelled, delete all the files we downloaded
//
if (FAILED(hr) || bCancelled)
{
DownloadedFileInformationList.DeleteAllFiles();
}
//
// Update the folder time, to cause the thumbnail to regenerate
//
if (SUCCEEDED(hr))
{
UpdateFolderTime( m_strDirectory );
}
//
// Print the final result to the debugger
//
WIA_PRINTHRESULT((hr,TEXT("This is the result of downloading all of the images: %s"),strExtendedErrorInformation.String()));
//
// Tell the notification window we are done downloading images
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::EndDownloadAllMessage( hr, strExtendedErrorInformation, &DownloadedFileInformationList ) );
return S_OK;
}
CSimpleString CDownloadImagesThreadMessage::GetDateString(void)
{
SYSTEMTIME LocalTime = {0};
GetLocalTime(&LocalTime);
TCHAR szDate[MAX_PATH] = {0};
if (GetDateFormat( LOCALE_USER_DEFAULT, DATE_LONGDATE, &LocalTime, NULL, szDate, ARRAYSIZE(szDate)))
{
return CSimpleString().Format( IDS_UPLOADED_STRING, g_hInstance, szDate );
}
return TEXT("");
}
STDMETHODIMP CDownloadImagesThreadMessage::QueryInterface( REFIID riid, LPVOID *ppvObject )
{
WIA_PUSHFUNCTION(TEXT("CWiaDefaultUI::QueryInterface"));
if (IsEqualIID( riid, IID_IUnknown ))
{
*ppvObject = static_cast<IWiaDataCallback*>(this);
}
else if (IsEqualIID( riid, IID_IWiaDataCallback ))
{
*ppvObject = static_cast<IWiaDataCallback*>(this);
}
else
{
*ppvObject = NULL;
return(E_NOINTERFACE);
}
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
return(S_OK);
}
STDMETHODIMP_(ULONG) CDownloadImagesThreadMessage::AddRef(void)
{
return 1;
}
STDMETHODIMP_(ULONG) CDownloadImagesThreadMessage::Release(void)
{
return 1;
}
STDMETHODIMP CDownloadImagesThreadMessage::BandedDataCallback(
LONG lReason,
LONG lStatus,
LONG lPercentComplete,
LONG lOffset,
LONG lLength,
LONG lReserved,
LONG lResLength,
PBYTE pbBuffer )
{
WIA_PUSH_FUNCTION(( TEXT("CDownloadImagesThreadMessage::BandedDataCallback(%X,%X,%d,%X,%X,%X,%X,%X"), lReason, lStatus, lPercentComplete, lOffset, lLength, lReserved, lResLength, pbBuffer ));
//
// First of all, check to see if we've been cancelled
//
if (m_hCancelDownloadEvent && WAIT_TIMEOUT!=WaitForSingleObject(m_hCancelDownloadEvent,0))
{
return S_FALSE;
}
switch (lReason)
{
case IT_MSG_STATUS:
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::UpdateDownloadImageMessage( lPercentComplete ) );
break;
case IT_MSG_FILE_PREVIEW_DATA_HEADER:
//
// Get rid of the old one
//
m_MemoryDib.Destroy();
//
// Make sure we start a new one
//
m_bFirstTransfer = true;
m_nCurrentPreviewImageLine = 0;
break;
case IT_MSG_FILE_PREVIEW_DATA:
if (lStatus & IT_STATUS_TRANSFER_TO_CLIENT)
{
if (m_bFirstTransfer)
{
//
// Assuming there is no way we could get a lLength smaller than the image header size
//
m_bFirstTransfer = false;
m_MemoryDib.Initialize( reinterpret_cast<PBITMAPINFO>(pbBuffer) );
lLength -= WiaUiUtil::GetBmiSize(reinterpret_cast<PBITMAPINFO>(pbBuffer));
lOffset += WiaUiUtil::GetBmiSize(reinterpret_cast<PBITMAPINFO>(pbBuffer));
//
// We're getting a preview image
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::BeginPreviewMessage( m_nCurrentCookie, m_MemoryDib.Bitmap()) );
}
//
// Make sure we are dealing with valid data
//
if (m_MemoryDib.IsValid())
{
//
// This should be an even number of lines. If it isn't, things are going to get messed up
//
int nLineCount = lLength / m_MemoryDib.GetUnpackedWidthInBytes();
//
// Scroll the data up if we are out of room
//
WIA_TRACE((TEXT("nLineCount = %d, nCurrentLine = %d, m_MemoryDib.GetHeight() = %d"), nLineCount, m_nCurrentPreviewImageLine, m_MemoryDib.GetHeight() ));
if (nLineCount + m_nCurrentPreviewImageLine > m_MemoryDib.GetHeight())
{
int nNumberOfLinesToScroll = (nLineCount + m_nCurrentPreviewImageLine) - m_MemoryDib.GetHeight();
m_MemoryDib.ScrollDataUp(nNumberOfLinesToScroll);
m_nCurrentPreviewImageLine = m_MemoryDib.GetHeight() - nLineCount;
}
WIA_TRACE((TEXT("nCurrentLine: %d, nLineCount: %d"), m_nCurrentPreviewImageLine, nLineCount ));
//
// Copy the data to our bitmap
//
m_MemoryDib.SetUnpackedData( pbBuffer, m_nCurrentPreviewImageLine, nLineCount );
//
// This is where we'll start next time
//
m_nCurrentPreviewImageLine += nLineCount;
//
// Tell the UI we have an update
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::UpdatePreviewMessage( m_nCurrentCookie, m_MemoryDib.Bitmap() ) );
//
// If this is it for this preview, tell the UI
//
if (lPercentComplete == 100)
{
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDownloadImagesThreadNotifyMessage::EndPreviewMessage( m_nCurrentCookie ) );
}
}
} // IT_STATUS_TRANSFER_TO_CLIENT
break;
}
return S_OK;
}
// -------------------------------------------------
// CDeleteImagesThreadMessage
// -------------------------------------------------
CDeleteImagesThreadMessage::CDeleteImagesThreadMessage(
HWND hWndNotify,
const CSimpleDynamicArray<DWORD> &Cookies,
HANDLE hCancelDeleteEvent,
HANDLE hPauseDeleteEvent,
bool bSlowItDown
)
: CNotifyThreadMessage( TQ_DELETEIMAGES, hWndNotify ),
m_Cookies(Cookies),
m_hCancelDeleteEvent(NULL),
m_hPauseDeleteEvent(NULL),
m_bSlowItDown(bSlowItDown)
{
if (hCancelDeleteEvent)
{
DuplicateHandle( GetCurrentProcess(), hCancelDeleteEvent, GetCurrentProcess(), &m_hCancelDeleteEvent, 0, FALSE, DUPLICATE_SAME_ACCESS );
}
if (hPauseDeleteEvent)
{
DuplicateHandle( GetCurrentProcess(), hPauseDeleteEvent, GetCurrentProcess(), &m_hPauseDeleteEvent, 0, FALSE, DUPLICATE_SAME_ACCESS );
}
}
CDeleteImagesThreadMessage::~CDeleteImagesThreadMessage(void)
{
if (m_hCancelDeleteEvent)
{
CloseHandle(m_hCancelDeleteEvent);
m_hCancelDeleteEvent = NULL;
}
if (m_hPauseDeleteEvent)
{
CloseHandle(m_hPauseDeleteEvent);
m_hPauseDeleteEvent = NULL;
}
}
HRESULT CDeleteImagesThreadMessage::DeleteImages(void)
{
WIA_PUSH_FUNCTION((TEXT("CDeleteImagesThreadMessage::DeleteImages")));
//
// This is the result of downloading all of the images. If any errors
// occur, we will return an error. Otherwise, we will return S_OK
//
HRESULT hrFinalResult = S_OK;
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDeleteImagesThreadNotifyMessage::BeginDeleteAllMessage( m_Cookies.Size() ) );
//
// Pause a little while, so the user can read the wizard page
//
if (m_bSlowItDown)
{
Sleep(DELETE_DELAY_BEFORE);
}
//
// Get an instance of the GIT
//
CComPtr<IGlobalInterfaceTable> pGlobalInterfaceTable;
HRESULT hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (VOID**)&pGlobalInterfaceTable );
if (SUCCEEDED(hr))
{
for (int i=0;i<m_Cookies.Size();i++)
{
//
// Tell the notification window which image we are deleting
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDeleteImagesThreadNotifyMessage::BeginDeleteImageMessage( i, m_Cookies[i] ) );
//
// Get the item from the GIT
//
CComPtr<IWiaItem> pWiaItem;
hr = pGlobalInterfaceTable->GetInterfaceFromGlobal(m_Cookies[i], IID_IWiaItem, (void**)&pWiaItem );
if (SUCCEEDED(hr))
{
//
// Wait forever if we are paused
//
if (m_hPauseDeleteEvent)
{
WiaUiUtil::MsgWaitForSingleObject(m_hPauseDeleteEvent,INFINITE);
}
//
// Check for a cancel
//
if (m_hCancelDeleteEvent && WAIT_OBJECT_0==WaitForSingleObject(m_hCancelDeleteEvent,0))
{
hr = hrFinalResult = S_FALSE;
break;
}
//
// Delete the item. Note that we don't consider delete errors to be fatal, so we continue
//
hr = WIA_FORCE_ERROR(FE_WIAACMGR,8,WiaUiUtil::DeleteItemAndChildren(pWiaItem));
if (S_OK != hr)
{
hrFinalResult = hr;
WIA_PRINTHRESULT((hr,TEXT("DeleteItemAndChildren failed")));
}
}
else
{
hrFinalResult = hr;
WIA_PRINTHRESULT((hr,TEXT("Unable to retrieve interface %08X from the global interface table"),m_Cookies[i]));
}
//
// Pause a little while, so the user can read the wizard page
//
if (m_bSlowItDown)
{
Sleep(DELETE_DELAY_DURING / m_Cookies.Size());
}
//
// Save any errors
//
if (FAILED(hr))
{
hrFinalResult = hr;
}
//
// If the device is disconnected, we may as well stop
//
if (WIA_ERROR_OFFLINE == hr)
{
break;
}
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDeleteImagesThreadNotifyMessage::EndDeleteImageMessage( i, m_Cookies[i], hr ) );
}
}
else
{
hrFinalResult = hr;
}
//
// Pause a little while, so the user can read the wizard page
//
if (m_bSlowItDown)
{
Sleep(DELETE_DELAY_AFTER);
}
CThreadNotificationMessage::SendMessage( NotifyWindow(), CDeleteImagesThreadNotifyMessage::EndDeleteAllMessage( hrFinalResult ) );
return S_OK;
}
// -------------------------------------------------
// CPreviewScanThreadMessage
// -------------------------------------------------
CPreviewScanThreadMessage::CPreviewScanThreadMessage(
HWND hWndNotify,
DWORD dwCookie,
HANDLE hCancelPreviewEvent
)
: CNotifyThreadMessage( TQ_SCANPREVIEW, hWndNotify ),
m_dwCookie(dwCookie),
m_bFirstTransfer(true)
{
DuplicateHandle( GetCurrentProcess(), hCancelPreviewEvent, GetCurrentProcess(), &m_hCancelPreviewEvent, 0, FALSE, DUPLICATE_SAME_ACCESS );
}
CPreviewScanThreadMessage::~CPreviewScanThreadMessage()
{
if (m_hCancelPreviewEvent)
{
CloseHandle(m_hCancelPreviewEvent);
m_hCancelPreviewEvent = NULL;
}
}
//
// Calculate the maximum scan size using the give DPI
//
static bool GetFullResolution( IWiaItem *pWiaItem, LONG nResX, LONG nResY, LONG &nExtX, LONG &nExtY )
{
WIA_PUSHFUNCTION(TEXT("CScannerItem::GetFullResolution"));
CComPtr<IWiaItem> pRootItem;
if (SUCCEEDED(pWiaItem->GetRootItem(&pRootItem)) && pRootItem)
{
LONG lBedSizeX, lBedSizeY, nPaperSource;
if (PropStorageHelpers::GetProperty( pRootItem, WIA_DPS_DOCUMENT_HANDLING_SELECT, static_cast<LONG>(nPaperSource)) && nPaperSource & FEEDER)
{
if (PropStorageHelpers::GetProperty( pRootItem, WIA_DPS_HORIZONTAL_SHEET_FEED_SIZE, lBedSizeX ) &&
PropStorageHelpers::GetProperty( pRootItem, WIA_DPS_VERTICAL_SHEET_FEED_SIZE, lBedSizeY ))
{
nExtX = WiaUiUtil::MulDivNoRound( nResX, lBedSizeX, 1000 );
nExtY = WiaUiUtil::MulDivNoRound( nResY, lBedSizeY, 1000 );
return(true);
}
}
if (PropStorageHelpers::GetProperty( pRootItem, WIA_DPS_HORIZONTAL_BED_SIZE, lBedSizeX ) &&
PropStorageHelpers::GetProperty( pRootItem, WIA_DPS_VERTICAL_BED_SIZE, lBedSizeY ))
{
nExtX = WiaUiUtil::MulDivNoRound( nResX, lBedSizeX, 1000 );
nExtY = WiaUiUtil::MulDivNoRound( nResY, lBedSizeY, 1000 );
return(true);
}
}
return(false);
}
static bool CalculatePreviewResolution( IWiaItem *pWiaItem, LONG &nResX, LONG &nResY )
{
const LONG nDesiredResolution = 50;
PropStorageHelpers::CPropertyRange XResolutionRange, YResolutionRange;
if (PropStorageHelpers::GetPropertyRange( pWiaItem, WIA_IPS_XRES, XResolutionRange ) &&
PropStorageHelpers::GetPropertyRange( pWiaItem, WIA_IPS_YRES, YResolutionRange ))
{
nResX = WiaUiUtil::GetMinimum<LONG>( XResolutionRange.nMin, nDesiredResolution, XResolutionRange.nStep );
nResY = WiaUiUtil::GetMinimum<LONG>( YResolutionRange.nMin, nDesiredResolution, YResolutionRange.nStep );
return(true);
}
else
{
CSimpleDynamicArray<LONG> XResolutionList, YResolutionList;
if (PropStorageHelpers::GetPropertyList( pWiaItem, WIA_IPS_XRES, XResolutionList ) &&
PropStorageHelpers::GetPropertyList( pWiaItem, WIA_IPS_YRES, YResolutionList ))
{
for (int i=0;i<XResolutionList.Size();i++)
{
nResX = XResolutionList[i];
if (nResX >= nDesiredResolution)
break;
}
for (i=0;i<YResolutionList.Size();i++)
{
nResY = YResolutionList[i];
if (nResY >= nDesiredResolution)
break;
}
return(true);
}
}
return(false);
}
HRESULT CPreviewScanThreadMessage::Scan(void)
{
WIA_PUSH_FUNCTION((TEXT("CPreviewScanThreadMessage::Download")));
CThreadNotificationMessage::SendMessage( NotifyWindow(), CPreviewScanThreadNotifyMessage::BeginDownloadMessage( m_dwCookie ) );
HRESULT hr = S_OK;
CComPtr<IGlobalInterfaceTable> pGlobalInterfaceTable;
hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (VOID**)&pGlobalInterfaceTable );
if (SUCCEEDED(hr))
{
CComPtr<IWiaTransferHelper> pWiaTransferHelper;
hr = CoCreateInstance( CLSID_WiaDefaultUi, NULL, CLSCTX_INPROC_SERVER, IID_IWiaTransferHelper, (void**)&pWiaTransferHelper );
if (SUCCEEDED(hr))
{
CComPtr<IWiaDataCallback> pWiaDataCallback;
hr = this->QueryInterface( IID_IWiaDataCallback, (void**)&pWiaDataCallback );
if (SUCCEEDED(hr))
{
CComPtr<IWiaItem> pWiaItem;
hr = pGlobalInterfaceTable->GetInterfaceFromGlobal(m_dwCookie, IID_IWiaItem, (void**)&pWiaItem );
if (SUCCEEDED(hr))
{
LONG nResX, nResY;
if (CalculatePreviewResolution(pWiaItem,nResX,nResY))
{
LONG nExtX, nExtY;
if (GetFullResolution(pWiaItem,nResX,nResY,nExtX,nExtY))
{
CPropertyStream SavedProperties;
hr = SavedProperties.AssignFromWiaItem( pWiaItem );
if (SUCCEEDED(hr))
{
if (PropStorageHelpers::SetProperty( pWiaItem, WIA_IPS_XRES, nResX ) &&
PropStorageHelpers::SetProperty( pWiaItem, WIA_IPS_YRES, nResY ) &&
PropStorageHelpers::SetProperty( pWiaItem, WIA_IPS_XPOS, 0 ) &&
PropStorageHelpers::SetProperty( pWiaItem, WIA_IPS_YPOS, 0 ) &&
PropStorageHelpers::SetProperty( pWiaItem, WIA_IPS_XEXTENT, nExtX ) &&
PropStorageHelpers::SetProperty( pWiaItem, WIA_IPS_YEXTENT, nExtY ))
{
//
// Set the preview property. Ignore failure (it is an optional property)
//
PropStorageHelpers::SetProperty( pWiaItem, WIA_DPS_PREVIEW, 1 );
hr = pWiaTransferHelper->TransferItemBanded( pWiaItem, NotifyWindow(), WIA_TRANSFERHELPER_NOPROGRESS, WiaImgFmt_MEMORYBMP, 0, pWiaDataCallback );
}
else
{
hr = E_FAIL;
}
SavedProperties.ApplyToWiaItem( pWiaItem );
}
}
else
{
WIA_ERROR((TEXT("Unable to calculate the preview resolution size")));
hr = E_FAIL;
}
}
else
{
WIA_ERROR((TEXT("Unable to calculate the preview resolution")));
hr = E_FAIL;
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("Unable to retrieve interface %08X from the global interface table"),m_dwCookie));
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("QueryInterface failed on IID_IWiaDataCallback")));
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("Unable to create the transfer helper class")));
}
}
else
{
WIA_PRINTHRESULT((hr,TEXT("Unable to create the global interface table")));
}
if (FAILED(hr) || hr == S_FALSE)
{
m_MemoryDib.Destroy();
}
CThreadNotificationMessage::SendMessage( NotifyWindow(), CPreviewScanThreadNotifyMessage::EndDownloadMessage( m_dwCookie, m_MemoryDib.DetachBitmap(), hr ) );
return S_OK;
}
STDMETHODIMP CPreviewScanThreadMessage::QueryInterface( REFIID riid, LPVOID *ppvObject )
{
WIA_PUSHFUNCTION(TEXT("CWiaDefaultUI::QueryInterface"));
if (IsEqualIID( riid, IID_IUnknown ))
{
*ppvObject = static_cast<IWiaDataCallback*>(this);
}
else if (IsEqualIID( riid, IID_IWiaDataCallback ))
{
*ppvObject = static_cast<IWiaDataCallback*>(this);
}
else
{
*ppvObject = NULL;
return(E_NOINTERFACE);
}
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
return(S_OK);
}
STDMETHODIMP_(ULONG) CPreviewScanThreadMessage::AddRef(void)
{
return 1;
}
STDMETHODIMP_(ULONG) CPreviewScanThreadMessage::Release(void)
{
return 1;
}
STDMETHODIMP CPreviewScanThreadMessage::BandedDataCallback(
LONG lReason,
LONG lStatus,
LONG lPercentComplete,
LONG lOffset,
LONG lLength,
LONG lReserved,
LONG lResLength,
PBYTE pbBuffer )
{
WIA_PUSH_FUNCTION(( TEXT("CPreviewScanThreadMessage::BandedDataCallback(%X,%X,%d,%X,%X,%X,%X,%X"), lReason, lStatus, lPercentComplete, lOffset, lLength, lReserved, lResLength, pbBuffer ));
if (m_hCancelPreviewEvent && WAIT_OBJECT_0==WaitForSingleObject(m_hCancelPreviewEvent,0))
{
return S_FALSE;
}
HRESULT hr = S_OK;
switch (lReason)
{
case IT_MSG_DATA_HEADER:
{
m_bFirstTransfer = true;
break;
} // IT_MSG_DATA_HEADER
case IT_MSG_DATA:
if (lStatus & IT_STATUS_TRANSFER_TO_CLIENT)
{
if (m_bFirstTransfer)
{
//
// Assuming there is no way we could get a lLength smaller than the image header size
//
m_bFirstTransfer = false;
m_MemoryDib.Initialize( reinterpret_cast<PBITMAPINFO>(pbBuffer) );
lLength -= WiaUiUtil::GetBmiSize(reinterpret_cast<PBITMAPINFO>(pbBuffer));
lOffset += WiaUiUtil::GetBmiSize(reinterpret_cast<PBITMAPINFO>(pbBuffer));
}
if (SUCCEEDED(hr))
{
//
// Don't bother unless we have some data
//
if (lLength)
{
//
// Figure out which line we are on
//
int nCurrentLine = (lOffset - m_MemoryDib.GetHeaderLength())/m_MemoryDib.GetUnpackedWidthInBytes();
//
// This should be an even number of lines. If it isn't, things are going to get messed up
//
int nLineCount = lLength / m_MemoryDib.GetUnpackedWidthInBytes();
//
// Copy the data to our bitmap
//
m_MemoryDib.SetUnpackedData( pbBuffer, nCurrentLine, nLineCount );
//
// Tell the notification window we have some data
//
CThreadNotificationMessage::SendMessage( NotifyWindow(), CPreviewScanThreadNotifyMessage::UpdateDownloadMessage( m_dwCookie, m_MemoryDib.Bitmap() ) );
}
}
} // IT_STATUS_TRANSFER_TO_CLIENT
break;
case IT_MSG_STATUS:
{
} // IT_MSG_STATUS
break;
case IT_MSG_TERMINATION:
{
} // IT_MSG_TERMINATION
break;
default:
WIA_ERROR((TEXT("ImageDataCallback, unknown lReason: %d"), lReason ));
break;
}
return(hr);
}