2532 lines
116 KiB
C++
2532 lines
116 KiB
C++
|
/*******************************************************************************
|
||
|
*
|
||
|
* (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);
|
||
|
}
|
||
|
|