/******************************************************************************* * * (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 &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 pIWiaPropertyStorage; HRESULT hr = pWiaItem->QueryInterface(IID_IWiaPropertyStorage, (void**)&pIWiaPropertyStorage); if (SUCCEEDED(hr)) { AnnotationType = AnnotationNone; CComPtr 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(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 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 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 &Cookies, const CSimpleDynamicArray &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 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;iGetFormatType( 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 &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 pEnumWiaItem; if (SUCCEEDED(pWiaItem->EnumChildItems( &pEnumWiaItem ))) { CComPtr 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 &TransferItems, const CSimpleString &strFilename ) { WIA_PUSH_FUNCTION((TEXT("Find( %s )"),strFilename.String())); for (int i=0;i &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= 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 &TransferItems) { for (int nCurrTransferItem=0;nCurrTransferItem 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 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 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 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 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 pWiaItemRoot; if (SUCCEEDED(pWiaItem->GetRootItem(&pWiaItemRoot))) { LONG nPaperSource = 0; if (PropStorageHelpers::GetProperty( pWiaItemRoot, WIA_DPS_DOCUMENT_HANDLING_SELECT, static_cast(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;nCurrFormatGetFormatType(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 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 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 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 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(this); } else if (IsEqualIID( riid, IID_IWiaDataCallback )) { *ppvObject = static_cast(this); } else { *ppvObject = NULL; return(E_NOINTERFACE); } reinterpret_cast(*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(pbBuffer) ); lLength -= WiaUiUtil::GetBmiSize(reinterpret_cast(pbBuffer)); lOffset += WiaUiUtil::GetBmiSize(reinterpret_cast(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 &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 pGlobalInterfaceTable; HRESULT hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (VOID**)&pGlobalInterfaceTable ); if (SUCCEEDED(hr)) { for (int i=0;i 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 pRootItem; if (SUCCEEDED(pWiaItem->GetRootItem(&pRootItem)) && pRootItem) { LONG lBedSizeX, lBedSizeY, nPaperSource; if (PropStorageHelpers::GetProperty( pRootItem, WIA_DPS_DOCUMENT_HANDLING_SELECT, static_cast(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( XResolutionRange.nMin, nDesiredResolution, XResolutionRange.nStep ); nResY = WiaUiUtil::GetMinimum( YResolutionRange.nMin, nDesiredResolution, YResolutionRange.nStep ); return(true); } else { CSimpleDynamicArray XResolutionList, YResolutionList; if (PropStorageHelpers::GetPropertyList( pWiaItem, WIA_IPS_XRES, XResolutionList ) && PropStorageHelpers::GetPropertyList( pWiaItem, WIA_IPS_YRES, YResolutionList )) { for (int i=0;i= nDesiredResolution) break; } for (i=0;i= 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 pGlobalInterfaceTable; hr = CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (VOID**)&pGlobalInterfaceTable ); if (SUCCEEDED(hr)) { CComPtr pWiaTransferHelper; hr = CoCreateInstance( CLSID_WiaDefaultUi, NULL, CLSCTX_INPROC_SERVER, IID_IWiaTransferHelper, (void**)&pWiaTransferHelper ); if (SUCCEEDED(hr)) { CComPtr pWiaDataCallback; hr = this->QueryInterface( IID_IWiaDataCallback, (void**)&pWiaDataCallback ); if (SUCCEEDED(hr)) { CComPtr 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(this); } else if (IsEqualIID( riid, IID_IWiaDataCallback )) { *ppvObject = static_cast(this); } else { *ppvObject = NULL; return(E_NOINTERFACE); } reinterpret_cast(*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(pbBuffer) ); lLength -= WiaUiUtil::GetBmiSize(reinterpret_cast(pbBuffer)); lOffset += WiaUiUtil::GetBmiSize(reinterpret_cast(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); }