/*++ Copyright (c) Microsoft Corporation. All rights reserved. --*/ #include "stdafx.h" #include "resource.h" #include "DataCallback.h" #include "ProgressDlg.h" #include "WiaWrap.h" namespace WiaWrap { ////////////////////////////////////////////////////////////////////////// // // // #ifndef MAX_GUID_STRING_LEN #define MAX_GUID_STRING_LEN 39 #endif //MAX_GUID_STRING_LEN ////////////////////////////////////////////////////////////////////////// // // ReadPropertyLong // HRESULT ReadPropertyLong( IWiaPropertyStorage *pWiaPropertyStorage, const PROPSPEC *pPropSpec, LONG *plResult ) { PROPVARIANT PropVariant; HRESULT hr = pWiaPropertyStorage->ReadMultiple( 1, pPropSpec, &PropVariant ); // Generally, the return value should be checked against S_FALSE. // If ReadMultiple returns S_FALSE, it means the property name or ID // had valid syntax, but it didn't exist in this property set, so // no properties were retrieved, and each PROPVARIANT structure is set // to VT_EMPTY. But the following switch statement will handle this case // and return E_FAIL. So the caller of ReadPropertyLong does not need // to check for S_FALSE explicitly. if (SUCCEEDED(hr)) { switch (PropVariant.vt) { case VT_I1: { *plResult = (LONG) PropVariant.cVal; hr = S_OK; break; } case VT_UI1: { *plResult = (LONG) PropVariant.bVal; hr = S_OK; break; } case VT_I2: { *plResult = (LONG) PropVariant.iVal; hr = S_OK; break; } case VT_UI2: { *plResult = (LONG) PropVariant.uiVal; hr = S_OK; break; } case VT_I4: { *plResult = (LONG) PropVariant.lVal; hr = S_OK; break; } case VT_UI4: { *plResult = (LONG) PropVariant.ulVal; hr = S_OK; break; } case VT_INT: { *plResult = (LONG) PropVariant.intVal; hr = S_OK; break; } case VT_UINT: { *plResult = (LONG) PropVariant.uintVal; hr = S_OK; break; } case VT_R4: { *plResult = (LONG) (PropVariant.fltVal + 0.5); hr = S_OK; break; } case VT_R8: { *plResult = (LONG) (PropVariant.dblVal + 0.5); hr = S_OK; break; } default: { hr = E_FAIL; break; } } } PropVariantClear(&PropVariant); return hr; } ////////////////////////////////////////////////////////////////////////// // // ReadPropertyGuid // HRESULT ReadPropertyGuid( IWiaPropertyStorage *pWiaPropertyStorage, const PROPSPEC *pPropSpec, GUID *pguidResult ) { PROPVARIANT PropVariant; HRESULT hr = pWiaPropertyStorage->ReadMultiple( 1, pPropSpec, &PropVariant ); // Generally, the return value should be checked against S_FALSE. // If ReadMultiple returns S_FALSE, it means the property name or ID // had valid syntax, but it didn't exist in this property set, so // no properties were retrieved, and each PROPVARIANT structure is set // to VT_EMPTY. But the following switch statement will handle this case // and return E_FAIL. So the caller of ReadPropertyGuid does not need // to check for S_FALSE explicitly. if (SUCCEEDED(hr)) { switch (PropVariant.vt) { case VT_CLSID: { *pguidResult = *PropVariant.puuid; hr = S_OK; break; } case VT_BSTR: { hr = CLSIDFromString(PropVariant.bstrVal, pguidResult); break; } case VT_LPWSTR: { hr = CLSIDFromString(PropVariant.pwszVal, pguidResult); break; } case VT_LPSTR: { WCHAR wszGuid[MAX_GUID_STRING_LEN]; size_t *pConvertedChars = NULL; mbstowcs_s(pConvertedChars, wszGuid, COUNTOF(wszGuid) - 1 ,PropVariant.pszVal, MAX_GUID_STRING_LEN); wszGuid[MAX_GUID_STRING_LEN - 1] = L'\0'; hr = CLSIDFromString(wszGuid, pguidResult); break; } default: { hr = E_FAIL; break; } } } PropVariantClear(&PropVariant); return hr; } ////////////////////////////////////////////////////////////////////////// // // WiaGetNumDevices // HRESULT WiaGetNumDevices( IWiaDevMgr *pSuppliedWiaDevMgr, ULONG *pulNumDevices ) { HRESULT hr; // Validate and initialize output parameters if (pulNumDevices == NULL) { return E_POINTER; } *pulNumDevices = 0; // Create a connection to the local WIA device manager CComPtr pWiaDevMgr = pSuppliedWiaDevMgr; if (pWiaDevMgr == NULL) { hr = pWiaDevMgr.CoCreateInstance(CLSID_WiaDevMgr); if (FAILED(hr)) { return hr; } } // Get a list of all the WIA devices on the system CComPtr pIEnumWIA_DEV_INFO; hr = pWiaDevMgr->EnumDeviceInfo( 0, &pIEnumWIA_DEV_INFO ); if (FAILED(hr)) { return hr; } // Get the number of WIA devices ULONG celt; hr = pIEnumWIA_DEV_INFO->GetCount(&celt); if (FAILED(hr)) { return hr; } *pulNumDevices = celt; return S_OK; } ////////////////////////////////////////////////////////////////////////// // // DefaultProgressCallback // HRESULT CALLBACK DefaultProgressCallback( LONG lStatus, LONG lPercentComplete, PVOID pParam ) { CProgressDlg *pProgressDlg = (CProgressDlg *) pParam; if (pProgressDlg == NULL) { return E_POINTER; } // If the user has pressed the cancel button, abort transfer if (pProgressDlg->Cancelled()) { return S_FALSE; } // Form the message text UINT uID; switch (lStatus) { case IT_STATUS_TRANSFER_FROM_DEVICE: { uID = IDS_STATUS_TRANSFER_FROM_DEVICE; break; } case IT_STATUS_PROCESSING_DATA: { uID = IDS_STATUS_PROCESSING_DATA; break; } case IT_STATUS_TRANSFER_TO_CLIENT: { uID = IDS_STATUS_TRANSFER_TO_CLIENT; break; } default: { return E_INVALIDARG; } } TCHAR szFormat[DEFAULT_STRING_SIZE] = _T("%d"); LoadString(g_hInstance, uID, szFormat, COUNTOF(szFormat)); TCHAR szStatusText[DEFAULT_STRING_SIZE]; _sntprintf_s(szStatusText, COUNTOF(szStatusText) - 1, _TRUNCATE, szFormat, lPercentComplete); szStatusText[COUNTOF(szStatusText) - 1] = _T('\0'); // Update the progress bar values pProgressDlg->SetMessage(szStatusText); pProgressDlg->SetPercent(lPercentComplete); return S_OK; } ////////////////////////////////////////////////////////////////////////// // // WiaGetImage // HRESULT WiaGetImage( HWND hWndParent, LONG lDeviceType, LONG lFlags, LONG lIntent, IWiaDevMgr *pSuppliedWiaDevMgr, IWiaItem *pSuppliedItemRoot, PFNPROGRESSCALLBACK pfnProgressCallback, PVOID pProgressCallbackParam, GUID *pguidFormat, LONG *plCount, IStream ***pppStream ) { HRESULT hr; // Validate and initialize output parameters if (plCount == NULL) { return E_POINTER; } if (pppStream == NULL) { return E_POINTER; } *plCount = 0; *pppStream = NULL; // Initialize the local root item variable with the supplied value. // If no value is supplied, display the device selection common dialog. CComPtr pItemRoot = pSuppliedItemRoot; if (pItemRoot == NULL) { // Initialize the device manager pointer with the supplied value // If no value is supplied, connect to the local device manager CComPtr pWiaDevMgr = pSuppliedWiaDevMgr; if (pWiaDevMgr == NULL) { hr = pWiaDevMgr.CoCreateInstance(CLSID_WiaDevMgr); if (FAILED(hr)) { return hr; } } // Display the device selection common dialog hr = pWiaDevMgr->SelectDeviceDlg( hWndParent, lDeviceType, lFlags, 0, &pItemRoot ); if (FAILED(hr) || hr == S_FALSE) { return hr; } } // Display the image selection common dialog CComPtrArray ppIWiaItem; hr = pItemRoot->DeviceDlg( hWndParent, lFlags, lIntent, &ppIWiaItem.Count(), &ppIWiaItem ); if (FAILED(hr) || hr == S_FALSE) { return hr; } // For ADF scanners, the common dialog explicitly sets the page count to one. // So in order to transfer multiple images, set the page count to ALL_PAGES // if the WIA_DEVICE_DIALOG_SINGLE_IMAGE flag is not specified, if (!(lFlags & WIA_DEVICE_DIALOG_SINGLE_IMAGE)) { // Get the property storage interface pointer for the root item CComQIPtr pWiaRootPropertyStorage(pItemRoot); if (pWiaRootPropertyStorage == NULL) { return E_NOINTERFACE; } // Determine if the selected device is a scanner or not PROPSPEC specDevType; specDevType.ulKind = PRSPEC_PROPID; specDevType.propid = WIA_DIP_DEV_TYPE; LONG nDevType; hr = ReadPropertyLong( pWiaRootPropertyStorage, &specDevType, &nDevType ); if (SUCCEEDED(hr) && (GET_STIDEVICE_TYPE(nDevType) == StiDeviceTypeScanner)) { // Determine if the document feeder is selected or not PROPSPEC specDocumentHandlingSelect; specDocumentHandlingSelect.ulKind = PRSPEC_PROPID; specDocumentHandlingSelect.propid = WIA_DPS_DOCUMENT_HANDLING_SELECT; LONG nDocumentHandlingSelect; hr = ReadPropertyLong( pWiaRootPropertyStorage, &specDocumentHandlingSelect, &nDocumentHandlingSelect ); if (SUCCEEDED(hr) && (nDocumentHandlingSelect & FEEDER)) { PROPSPEC specPages; specPages.ulKind = PRSPEC_PROPID; specPages.propid = WIA_DPS_PAGES; PROPVARIANT varPages; varPages.vt = VT_I4; varPages.lVal = ALL_PAGES; pWiaRootPropertyStorage->WriteMultiple( 1, &specPages, &varPages, WIA_DPS_FIRST ); PropVariantClear(&varPages); } } } // If a status callback function is not supplied, use the default. // The default displays a simple dialog with a progress bar and cancel button. CComPtr pProgressDlg; if (pfnProgressCallback == NULL) { pfnProgressCallback = DefaultProgressCallback; pProgressDlg = new CProgressDlg(hWndParent); pProgressCallbackParam = (CProgressDlg *) pProgressDlg; } // Create the data callback interface CComPtr pDataCallback = new CDataCallback( pfnProgressCallback, pProgressCallbackParam, plCount, pppStream ); if (pDataCallback == NULL) { return E_OUTOFMEMORY; } // Start the transfer of the selected items for (int i = 0; i < ppIWiaItem.Count(); ++i) { // Get the interface pointers CComQIPtr pWiaPropertyStorage(ppIWiaItem[i]); if (pWiaPropertyStorage == NULL) { return E_NOINTERFACE; } CComQIPtr pIWiaDataTransfer(ppIWiaItem[i]); if (pIWiaDataTransfer == NULL) { return E_NOINTERFACE; } // Set the transfer type PROPSPEC specTymed; specTymed.ulKind = PRSPEC_PROPID; specTymed.propid = WIA_IPA_TYMED; PROPVARIANT varTymed; varTymed.vt = VT_I4; varTymed.lVal = TYMED_CALLBACK; hr = pWiaPropertyStorage->WriteMultiple( 1, &specTymed, &varTymed, WIA_IPA_FIRST ); PropVariantClear(&varTymed); if (FAILED(hr)) { return hr; } // If there is no transfer format specified, use the device default GUID guidFormat = GUID_NULL; if (pguidFormat == NULL) { pguidFormat = &guidFormat; } if (*pguidFormat == GUID_NULL) { PROPSPEC specPreferredFormat; specPreferredFormat.ulKind = PRSPEC_PROPID; specPreferredFormat.propid = WIA_IPA_PREFERRED_FORMAT; hr = ReadPropertyGuid( pWiaPropertyStorage, &specPreferredFormat, pguidFormat ); if (FAILED(hr)) { return hr; } } // Set the transfer format PROPSPEC specFormat; specFormat.ulKind = PRSPEC_PROPID; specFormat.propid = WIA_IPA_FORMAT; PROPVARIANT varFormat; varFormat.vt = VT_CLSID; varFormat.puuid = (CLSID *) CoTaskMemAlloc(sizeof(CLSID)); if (varFormat.puuid == NULL) { return E_OUTOFMEMORY; } *varFormat.puuid = *pguidFormat; hr = pWiaPropertyStorage->WriteMultiple( 1, &specFormat, &varFormat, WIA_IPA_FIRST ); PropVariantClear(&varFormat); if (FAILED(hr)) { return hr; } // Read the transfer buffer size from the device, default to 64K PROPSPEC specBufferSize; specBufferSize.ulKind = PRSPEC_PROPID; specBufferSize.propid = WIA_IPA_BUFFER_SIZE; LONG nBufferSize; hr = ReadPropertyLong( pWiaPropertyStorage, &specBufferSize, &nBufferSize ); if (FAILED(hr)) { nBufferSize = 64 * 1024; } // Choose double buffered transfer for better performance WIA_DATA_TRANSFER_INFO WiaDataTransferInfo = { 0 }; WiaDataTransferInfo.ulSize = sizeof(WIA_DATA_TRANSFER_INFO); WiaDataTransferInfo.ulBufferSize = 2 * nBufferSize; WiaDataTransferInfo.bDoubleBuffer = TRUE; // Start the transfer hr = pIWiaDataTransfer->idtGetBandedData( &WiaDataTransferInfo, pDataCallback ); if (FAILED(hr) || hr == S_FALSE) { return hr; } } return S_OK; } }; // namespace WiaWrap