summaryrefslogtreecommitdiff
path: root/wia/ProdScan/ScanJobs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wia/ProdScan/ScanJobs.cpp')
-rw-r--r--wia/ProdScan/ScanJobs.cpp2531
1 files changed, 2531 insertions, 0 deletions
diff --git a/wia/ProdScan/ScanJobs.cpp b/wia/ProdScan/ScanJobs.cpp
new file mode 100644
index 00000000..ad5b51ea
--- /dev/null
+++ b/wia/ProdScan/ScanJobs.cpp
@@ -0,0 +1,2531 @@
+/**************************************************************************
+*
+* Copyright � Microsoft Corporation
+*
+* Title: ScanJobs.cpp
+*
+* Description: This file contains the implementation of the IWiaMiniDrv::
+* drvAcquireItemData method and its helper methods used to
+* execute scan job requests and transfer image and metadata
+* files from or to the Production Scanner Driver Sample.
+*
+***************************************************************************/
+
+#include "stdafx.h"
+
+/**************************************************************************\
+*
+* Implements IWiaMiniDrv::drvAcquireItemData. This method is called by the
+* WIA service when the driver must transfer image data to the application.
+* This driver implements the Stream based WIA 2.0 transfer model introduced
+* in Windows Vista. This call correponds to one scan job executed at the device
+* (scaner starts, scan documents and transfers data, scanner stops).
+*
+* Parameters:
+*
+* pWiasContext - pointer to the item context
+* lFlags - reserved (set to 0)
+* pmdtc - pointer to a MINIDRV_TRANSFER_CONTEXT structure
+* containing the device transfer context
+* plDevErrVal - unused (drvGetDeviceErrorStr unsupported)
+*
+* Return Value:
+*
+* S_OK if successful, S_FALSE if the transfer is canceled
+* or an error HRESULT if an error occurrs
+*
+\**************************************************************************/
+
+HRESULT CWiaDriver::drvAcquireItemData(
+ _In_ BYTE* pWiasContext,
+ LONG lFlags,
+ _In_ PMINIDRV_TRANSFER_CONTEXT pmdtc,
+ _Out_ LONG* plDevErrVal)
+{
+ HRESULT hr = S_OK;
+
+ GUID guidItemCategory = GUID_NULL;
+ BSTR bstrItemName = NULL;
+ BSTR bstrFullItemName = NULL;
+
+ WIA_DRIVER_ITEM_CONTEXT *pWiaDriverItemContext = NULL;
+ IWiaMiniDrvTransferCallback *pTransferCallback = NULL;
+ WiaTransferParams *pCallbackTransferParams = NULL;
+
+ BYTE *pTransferBuffer = NULL;
+ ULONG ulBufferSize = 0;
+
+ BOOL bImageTransfer = TRUE;
+
+ WIAEX_TRACE_BEGIN;
+
+ //
+ // Validate parameters:
+ //
+ if ((!pWiasContext) || (!pmdtc) || (!plDevErrVal) || (!pmdtc->pIWiaMiniDrvCallBack))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr));
+ }
+
+ //
+ // Identify the item to execute the acquisition for checking WIA_IPA_ITEM_CATEGORY:
+ //
+ if (SUCCEEDED(hr))
+ {
+ hr = wiasReadPropGuid(pWiasContext, WIA_IPA_ITEM_CATEGORY, &guidItemCategory, NULL, TRUE);
+ if (SUCCEEDED(hr))
+ {
+ bImageTransfer = (IsEqualGUID(WIA_CATEGORY_FLATBED, guidItemCategory) ||
+ IsEqualGUID(WIA_CATEGORY_FEEDER, guidItemCategory) || IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory));
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Failed to read WIA_IPA_ITEM_CATEGORY property, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Get the item names, we will need them when requesting a new WIA transfer stream:
+ //
+ if (SUCCEEDED(hr))
+ {
+ hr = wiasReadPropStr(pWiasContext, WIA_IPA_ITEM_NAME, &bstrItemName, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading WIA_IPA_ITEM_NAME, hr = 0x%08X", hr));
+ }
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = wiasReadPropStr(pWiasContext, WIA_IPA_FULL_ITEM_NAME, &bstrFullItemName, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading WIA_IPA_FULL_ITEM_NAME, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Transfers from the Root item are not supported:
+ //
+ if (SUCCEEDED(hr) && (IsEqualGUID(WIA_CATEGORY_ROOT, guidItemCategory)))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Acquisition not supported from the Root item, hr = 0x%08X", hr));
+ }
+
+
+ //
+ // Get the private context data for this item. This sample driver stores in this data
+ // an image that the WIA application uploads in this session to the Imprinter or the
+ // Endorser and that should be used in subsequent downloads from the respective item:
+ //
+ if (SUCCEEDED(hr))
+ {
+ hr = wiasGetDriverItemPrivateContext(pWiasContext, (BYTE**)&pWiaDriverItemContext);
+ if (SUCCEEDED(hr) && (!pWiaDriverItemContext))
+ {
+ hr = E_POINTER;
+ }
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to retrieve the driver item context data, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Check the requested direction for this data transfer. Download means from the driver to the
+ // application (from the scanner device to the PC). Upload means from the application to the
+ // driver (from the PC to the scanner device). For a stream upload the flag value is
+ // WIA_MINIDRV_TRANSFER_UPLOAD while for a WIA 1.0 tymed-style transfer the flag value is 0:
+ //
+ if (SUCCEEDED(hr) && (bImageTransfer || IsEqualGUID(WIA_CATEGORY_BARCODE_READER, guidItemCategory) ||
+ IsEqualGUID(WIA_CATEGORY_PATCH_CODE_READER, guidItemCategory) ||
+ IsEqualGUID(WIA_CATEGORY_MICR_READER, guidItemCategory)) && (!(lFlags & WIA_MINIDRV_TRANSFER_DOWNLOAD)))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst,
+ "Invalid transfer flag parameter requested, this driver supports only download transfer direction for image data, barcode, patch code and MICR metadata, hr = 0x%08X",
+ hr));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *plDevErrVal = 0;
+ }
+
+ //
+ // Get the WIA transfer callback interface to use for these transfers:
+ //
+ if (SUCCEEDED(hr))
+ {
+ hr = pmdtc->pIWiaMiniDrvCallBack->QueryInterface(IID_IWiaMiniDrvTransferCallback, (void**)&pTransferCallback);
+ if (SUCCEEDED(hr) && (!pTransferCallback))
+ {
+ hr = E_POINTER;
+ }
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to retrieve IID_IWiaMiniDrvTransferCallback interface, scan failed, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Allocate memory for the WIA transfer parameters structure and the transfer buffer:
+ //
+ if (SUCCEEDED(hr))
+ {
+ pCallbackTransferParams = (WiaTransferParams*)CoTaskMemAlloc(sizeof(WiaTransferParams));
+ if (!pCallbackTransferParams)
+ {
+ hr = E_OUTOFMEMORY;
+ WIAEX_ERROR((g_hInst, "Memory allocation for transfer parameters failed, scan failed, hr = 0x%08X", hr));
+ }
+ else
+ {
+ memset(pCallbackTransferParams, 0, sizeof(WiaTransferParams));
+ }
+ }
+ if (SUCCEEDED(hr))
+ {
+ hr = AllocateTransferBuffer(&pTransferBuffer, &ulBufferSize);
+ if (!pTransferBuffer)
+ {
+ hr = E_OUTOFMEMORY;
+ WIAEX_ERROR((g_hInst, "Memory allocation for transfer buffer failed, scan failed, hr = 0x%08X", hr));
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ //
+ // The CWiaDriver::Download and CWiaDriver::Upload methods called below
+ // fully trace their execution, no need to trace additionally here.
+ // Also note that the pInputStrem stream is released by these functions:
+ //
+ if (lFlags & WIA_MINIDRV_TRANSFER_DOWNLOAD)
+ {
+ //
+ // 'Download' transfer direction (driver to application)
+ //
+ hr = Download(pWiasContext, guidItemCategory, bstrItemName, bstrFullItemName, pmdtc, pTransferBuffer,
+ ulBufferSize, pTransferCallback, pCallbackTransferParams, pWiaDriverItemContext);
+ }
+ else if (lFlags & WIA_MINIDRV_TRANSFER_UPLOAD)
+ {
+ //
+ // 'Upload' transfer direction (application to driver)
+ //
+ hr = Upload(pWiasContext, guidItemCategory, bstrItemName, bstrFullItemName, pTransferBuffer,
+ ulBufferSize, pTransferCallback, pCallbackTransferParams, pWiaDriverItemContext);
+
+ }
+ }
+
+ //
+ // Note that WIA_TRANSFER_MSG_END_OF_STREAM and WIA_TRANSFER_MSG_END_OF_TRANSFER
+ // are sent to the WIA application by the WIA service itself (first when the driver
+ // asks for a new stream), the driver should not send these itself.
+ //
+
+ //
+ // Clean-up for this scan job:
+ //
+
+ if (bstrItemName)
+ {
+ SysFreeString(bstrItemName);
+ }
+
+ if (bstrFullItemName)
+ {
+ SysFreeString(bstrFullItemName);
+ }
+
+ if (pTransferBuffer)
+ {
+ FreeTransferBuffer(pTransferBuffer);
+ }
+
+ if (pCallbackTransferParams)
+ {
+ CoTaskMemFree(pCallbackTransferParams);
+ }
+
+ if (pTransferCallback)
+ {
+ pTransferCallback->Release();
+ }
+
+ if (FAILED(hr))
+ {
+ m_hrLastEdviceError = hr;
+ }
+
+ WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvAcquireItemData 0x%08X", hr));
+
+ return hr;
+}
+
+/**************************************************************************\
+*
+* Helper for CWiaDriver::drvAcquireItemData. Executes the download data
+* transfer sequence for the current scan job,
+*
+* Parameters:
+*
+* pWiasContext - item context
+* guidItemCategory - item category
+* bstrItemName - item name
+* bstrFullItemName - full item name
+* pmdc - driver transfer context data
+* pTransferBuffer - pre-allocated transfer buffer
+* ulBufferSize - size of the pre-allocated transfer buffer, in bytes
+* pTransferCallback - IWiaMiniDrvTransferCallback* for WIA status
+* pCallbackTransferParams - WiaTransferParams* for WIA status callbacks
+* ulEstimatedFileSize - estimated file size, in bytes (0 if unknown)
+*
+* Return Value:
+*
+* S_OK if successful, S_FALSE if the transfer is canceled
+* or an error HRESULT if an error occurrs
+*
+\**************************************************************************/
+
+HRESULT
+CWiaDriver::Download(
+ _In_ BYTE *pWiasContext,
+ GUID guidItemCategory,
+ _In_ BSTR bstrItemName,
+ _In_ BSTR bstrFullItemName,
+ _In_ PMINIDRV_TRANSFER_CONTEXT pmdtc,
+ _In_reads_bytes_(ulBufferSize) BYTE *pTransferBuffer,
+ ULONG ulBufferSize,
+ _In_ IWiaMiniDrvTransferCallback *pTransferCallback,
+ _In_ WiaTransferParams *pCallbackTransferParams,
+ _In_ WIA_DRIVER_ITEM_CONTEXT *pWiaDriverItemContext)
+{
+ HRESULT hr = S_OK;
+
+ IStream *pInputStream = NULL;
+ IStream *pOutputStream = NULL;
+
+ LONG lPagesToScan = 1;
+ LONG lJobSeparators = WIA_SEPARATOR_DISABLED;
+ LONG lMultiFeed = WIA_MULTI_FEED_DETECT_DISABLED;
+ LONG lDataType = WIA_DATA_GRAYSCALE;
+ LONG lCompression = WIA_COMPRESSION_NONE;
+
+ LONG lPixelsPerLine = 0;
+ LONG lNumberOfLines = 0;
+ LONG lBytesPerLine = 0;
+
+ LONG lFilesToProcess = 1;
+ LONG lFilesProcessed = 0;
+ ULONG ulEstimatedFileSize = 0;
+ ULONG ulFileSize = 0;
+
+ ULONG_PTR pGDIPlusToken = NULL;
+ LONG lGDIPlus_PixelsPerLine = 0;
+ LONG lGDIPlus_NumberOfLines = 0;
+ LONG lGDIPlus_BytesPerLine = 0;
+
+ DWORD dwJobStart = 0;
+
+ IStream *pWiaStream = NULL;
+ BOOL bCancelTransfer = FALSE;
+ BOOL bSkipTransfer = FALSE;
+
+ BOOL bImageTransfer = (IsEqualGUID(WIA_CATEGORY_FLATBED, guidItemCategory) ||
+ IsEqualGUID(WIA_CATEGORY_FEEDER, guidItemCategory) || IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory));
+
+
+ WIAEX_TRACE_BEGIN;
+
+ if ((!pWiasContext) || (!bstrItemName) || (!bstrFullItemName) || (!pmdtc) || (!pTransferBuffer) || (!pTransferCallback) || (!pCallbackTransferParams))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr));
+ }
+
+ //
+ // Reset the recorded scan available input source, if any, ignoring failures:
+ //
+ if (SUCCEEDED(hr) && bImageTransfer)
+ {
+ UpdateScanAvailableItemName(NULL);
+ }
+
+ //
+ // For image transfers from feeder, use WIA_IPS_PAGES to know the number of images expected to transfer in this job:
+ //
+ if (SUCCEEDED(hr))
+ {
+ if (IsEqualGUID(guidItemCategory, WIA_CATEGORY_FEEDER))
+ {
+ //
+ // When WIA_IPS_PAGES is set to 0 (ALL_PAGES) meaning "scan as
+ // many documents as there may be loaded into the feeder" this
+ // sample driver will transfer up to MAX_SCAN_PAGES:
+ //
+ hr = wiasReadPropLong(pWiasContext, WIA_IPS_PAGES, &lPagesToScan, NULL, TRUE);
+ if (SUCCEEDED(hr))
+ {
+ if (ALL_PAGES == lPagesToScan)
+ {
+ lPagesToScan = MAX_SCAN_PAGES;
+ }
+ }
+ else
+ {
+ lPagesToScan = 1;
+ WIAEX_ERROR((g_hInst, "Failed reading the WIA_IPS_PAGES property, hr = 0x%08X", hr));
+ }
+ }
+ else if (IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory))
+ {
+ //
+ // In full automatic mode scan as many pages as the device decides to allow:
+ //
+ lPagesToScan = MAX_SCAN_PAGES;
+ }
+ else
+ {
+ //
+ // One single document page to scan from the flatbed or one single metadata file:
+ //
+ lPagesToScan = 1;
+ }
+ }
+
+ if (SUCCEEDED(hr) && bImageTransfer)
+ {
+ WIAS_TRACE((g_hInst, "Pages to scan: %u (0 means all)", lPagesToScan));
+ }
+
+ //
+ // For feeder acquisitions, this sample driver simulates a job separator every
+ // JOB_SEPARATOR_AT_PAGE page and a multi-feed every MULTI_FEED_AT_PAGE pages:
+ //
+ if (SUCCEEDED(hr) && IsEqualGUID(guidItemCategory, WIA_CATEGORY_FEEDER))
+ {
+ hr = wiasReadPropLong(pWiasContext, WIA_IPS_JOB_SEPARATORS, &lJobSeparators, NULL, TRUE);
+ if (SUCCEEDED(hr))
+ {
+ hr = wiasReadPropLong(pWiasContext, WIA_IPS_MULTI_FEED, &lMultiFeed, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading the WIA_IPS_MULTI_FEED property, hr = 0x%08X", hr));
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading the WIA_IPS_JOB_SEPARATORS property, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // For feeder acquisitions, if WIA_IPS_FEEDER_CONTROL is set to WIA_FEEDER_CONTROL_MANUAL
+ // and the feeder is not running, start the feeder and reset the feeder control to automatic
+ // mode, updating WIA_IPS_FEEDER_CONTROL to WIA_FEEDER_CONTROL_AUTO:
+ //
+ if (SUCCEEDED(hr) && IsEqualGUID(guidItemCategory, WIA_CATEGORY_FEEDER))
+ {
+ LONG lFeederControl = WIA_FEEDER_CONTROL_AUTO;
+
+ hr = wiasReadPropLong(pWiasContext, WIA_IPS_FEEDER_CONTROL, &lFeederControl, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading the WIA_IPS_FEEDER_CONTROL property, hr = 0x%08X", hr));
+ }
+
+ if (SUCCEEDED(hr) && (WIA_FEEDER_CONTROL_MANUAL == lFeederControl) && (!m_bFeederStarted))
+ {
+ hr = StartFeeder();
+ if (SUCCEEDED(hr))
+ {
+ lFeederControl = WIA_FEEDER_CONTROL_AUTO;
+
+ hr = wiasWritePropLong(pWiasContext, WIA_IPS_FEEDER_CONTROL, lFeederControl);
+ if (SUCCEEDED(hr))
+ {
+ WIAS_TRACE((g_hInst, "Reverted to WIA_FEEDER_CONTROL_AUTO"));
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Failed to reset the WIA_IPS_FEEDER_CONTROL property, hr = 0x%08X", hr));
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Cannot start the feeder, hr = 0x%08X", hr));
+ }
+ }
+ }
+
+ //
+ // Read the current data type configured for this image source:
+ //
+ if (SUCCEEDED(hr) && bImageTransfer && (!IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory)))
+ {
+ hr = wiasReadPropLong(pWiasContext, WIA_IPA_DATATYPE, &lDataType, NULL, TRUE);
+ if (SUCCEEDED(hr))
+ {
+ if (WIA_DATA_AUTO == lDataType)
+ {
+ //
+ // When WIA_DATA_AUTO is set the sample driver choses randomly between WIA_DATA_GRAYSCALE
+ // and WIA_DATA_COLOR. A real driver should base this decision on the actual scan document,
+ // each document page preferrably. This sample driver uses the same test image to transfer
+ // all single-file page scans in a job so this initialization is only performed once, for
+ // the entire job:
+ //
+ lDataType = (rand() % 2) ? WIA_DATA_GRAYSCALE : WIA_DATA_COLOR;
+ WIAS_TRACE((g_hInst, "Detected data type in automatic mode: %ws",
+ (WIA_DATA_GRAYSCALE == lDataType) ? L"8-bpp grayscale" : L"24-bpp RGB color"));
+
+ //
+ // Updated dependent image information properties:
+ //
+ hr = UpdateImageInfoProperties(pWiasContext, lDataType);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to update dependent image information properties, hr = 0x%08X", hr));
+ }
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Failed to read WIA_IPA_DATATYPE property, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // How many files and transfer streams the driver needs for this drvAcquireItemData call:
+ //
+ // a) For transfers from Feeder WIA_IPS_PAGES indicates the number of files.
+ // b) For transfers from Flatbed there is always just one file.
+ // c) For multi-page transfers (which this sample driver doesn't support) there would be just one file.
+ //
+ if (SUCCEEDED(hr))
+ {
+ if ((IsEqualGUID(guidItemCategory, WIA_CATEGORY_FEEDER)) || (IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory)))
+ {
+ lFilesToProcess = lPagesToScan;
+ }
+ else
+ {
+ lFilesToProcess = 1;
+ }
+
+ WIAS_TRACE((g_hInst, "Files to transfer: %u", lFilesToProcess));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ //
+ // Check first if we have a (previously uploaded in this session) imprinter/endorser image to use:
+ //
+ if (pWiaDriverItemContext->m_pUploadedImage)
+ {
+ pInputStream = pWiaDriverItemContext->m_pUploadedImage;
+ }
+ else
+ {
+ //
+ // Create a new global memory stream to store the transfer data file:
+ //
+ hr = CreateStreamOnHGlobal(NULL, TRUE, &pInputStream);
+ if (SUCCEEDED(hr) && (!pInputStream))
+ {
+ hr = E_FAIL;
+ }
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "CreateStreamOnHGlobal failed, hr = 0x%08X", hr));
+ }
+
+ //
+ // Read from resources or generate the test data file (image or metadata) to be downloaded
+ // to the application, storing this data into the new memory stream:
+ //
+ if (SUCCEEDED(hr))
+ {
+ ULONG ulResource = 0;
+
+ if (bImageTransfer)
+ {
+ ulResource = (WIA_DATA_COLOR == lDataType) ? IDB_TESTIMAGE_COLOR : IDB_TESTIMAGE_GRAY;
+ }
+ else if ((IsEqualGUID(WIA_CATEGORY_IMPRINTER, guidItemCategory) || IsEqualGUID(WIA_CATEGORY_ENDORSER, guidItemCategory))
+ && IsEqualGUID(WiaImgFmt_BMP, pmdtc->guidFormatID))
+ {
+ //
+ // The sample driver will generate itself the WiaImgFmt_CSV and WiaImgFmt_TXT files:
+ //
+ ulResource = IDB_TEST_IMPRINTER_IMAGE;
+ }
+ else if (IsEqualGUID(WIA_CATEGORY_BARCODE_READER, guidItemCategory) && IsEqualGUID(WiaImgFmt_XMLBAR, pmdtc->guidFormatID))
+ {
+ //
+ // The sample driver hard-codes the sample WiaImgFmt_RAWBAR file:
+ //
+ ulResource = IDB_BARCODE_SAMPLE;
+ }
+ else if (IsEqualGUID(WIA_CATEGORY_PATCH_CODE_READER, guidItemCategory) && IsEqualGUID(WiaImgFmt_XMLPAT, pmdtc->guidFormatID))
+ {
+ //
+ // The sample driver hard-codes the sample WiaImgFmt_RAWPAT file:
+ //
+ ulResource = IDB_PATCH_CODE_SAMPLE;
+ }
+ else if (IsEqualGUID(WIA_CATEGORY_MICR_READER, guidItemCategory) && IsEqualGUID(WiaImgFmt_XMLMIC, pmdtc->guidFormatID))
+ {
+ //
+ // The sample driver hard-codes the sample WiaImgFmt_RAWMIC file:
+ //
+ ulResource = IDB_MICR_SAMPLE;
+ }
+
+ if (ulResource)
+ {
+ hr = LoadTestDataResourceToStream(ulResource, pInputStream, &ulFileSize);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to load the test data file from resource %u, hr = 0x%08X", ulResource, hr));
+ }
+ }
+ else
+ {
+ hr = LoadTestDataToStream(pWiasContext, guidItemCategory, pmdtc->guidFormatID, pInputStream, &ulFileSize);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to load the test data file from resource %u, hr = 0x%08X", ulResource, hr));
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // For image transfers, if the application asks for DIB (which the driver must always support) or Raw
+ // this sample driver converts the image from the attached EXIF test image to obtain the uncompressed
+ // image before the first image transfer takes place. For this the driver first runs the test image
+ // through the GDI+ DIB encoder to generate the DIB image to transfer to the WIA application.
+ //
+ if (S_OK == hr)
+ {
+ if (bImageTransfer && IsEqualGUID(pmdtc->guidFormatID, WiaImgFmt_BMP) || IsEqualGUID(pmdtc->guidFormatID, WiaImgFmt_RAW))
+ {
+ //
+ // Initialize GDI+ if the driver must translate scanned images to the DIB format:
+ //
+ if (S_OK == hr)
+ {
+ WIAS_TRACE((g_hInst, "Initialize GDI++.."));
+ hr = InitializeGDIPlus(&pGDIPlusToken);
+ if (S_OK != hr)
+ {
+ WIAEX_ERROR((g_hInst, "Failed to initialize GDI+, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Load the image to a GDI+ Image object (GDI+ cannot convert the image directly from the stream):
+ //
+ Image *pInputImage = NULL;
+ if (S_OK == hr)
+ {
+ pInputImage = Image::FromStream(pInputStream);
+ if (!pInputImage)
+ {
+ hr = E_FAIL;
+ WIAEX_ERROR((g_hInst, "Image::FromStream failed, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Release the input stream now, no longer needed, the same image is held by the Image object:
+ //
+ pInputStream->Release();
+ pInputStream = NULL;
+
+ //
+ // Hand the Image object to GDI+ to generate the DIB image:
+ //
+ if (S_OK == hr)
+ {
+ hr = ConvertImageToDIB(pInputImage, &pOutputStream,
+ &lGDIPlus_PixelsPerLine, &lGDIPlus_NumberOfLines, &lGDIPlus_BytesPerLine);
+
+ if (S_OK != hr)
+ {
+ WIAEX_ERROR((g_hInst, "Failed to convert scanned image file to DIB, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Free the GDI+ Image copy:
+ //
+ if (pInputImage)
+ {
+ delete pInputImage;
+ }
+
+ //
+ // Done with file format conversions, if any. Shutdown GDI+, no longer needed:
+ //
+ if (pGDIPlusToken)
+ {
+ WIAS_TRACE((g_hInst, "Shutdown GDI++.."));
+ ShutdownGDIPlus(pGDIPlusToken);
+ pGDIPlusToken = NULL;
+ }
+
+ //
+ // For a Raw image transfer, further convert the DIB to an uncompressed Raw image file:
+ //
+ if ((S_OK == hr) && IsEqualGUID(pmdtc->guidFormatID, WiaImgFmt_RAW))
+ {
+ IStream *pTempOutputStream = NULL;
+
+ hr = ConvertDibToRaw(pOutputStream, &pTempOutputStream);
+ if (S_OK == hr)
+ {
+ //
+ // Release pOutputStream then reset its pointer to the new stream:
+ //
+ pOutputStream->Release();
+ pOutputStream = pTempOutputStream;
+ pTempOutputStream = NULL;
+ }
+ }
+
+ //
+ // If not in auto-config mode check the actual image dimensions reported by GDI+.
+ // In both programmed and auto-config mode update the estimated file size from
+ // the image dimensions and depth reported by GDI+ during the conversion process:
+ //
+ if (S_OK == hr)
+ {
+ if (!IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory))
+ {
+ HRESULT hrTemp = S_OK;
+
+ //
+ // Update WIA_IPA_PIXELS_PER_LINE, WIA_IPA_NUMBER_OF_LINES and WIA_IPA_BYTES_PER_LINE:
+ //
+ hrTemp = wiasWritePropLong(pWiasContext, WIA_IPA_PIXELS_PER_LINE, lGDIPlus_PixelsPerLine);
+ if (FAILED(hrTemp))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_PIXELS_PER_LINE to %u, hr = 0x%08X",
+ lGDIPlus_PixelsPerLine, hrTemp));
+ }
+ if (SUCCEEDED(hrTemp))
+ {
+ hrTemp = wiasWritePropLong(pWiasContext, WIA_IPA_NUMBER_OF_LINES, lGDIPlus_NumberOfLines);
+ if (FAILED(hrTemp))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_NUMBER_OF_LINES to %u, hr = 0x%08X",
+ lGDIPlus_NumberOfLines, hrTemp));
+ }
+ }
+ if (SUCCEEDED(hrTemp))
+ {
+ hrTemp = wiasWritePropLong(pWiasContext, WIA_IPA_BYTES_PER_LINE, lGDIPlus_BytesPerLine);
+ if (FAILED(hrTemp))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_BYTES_PER_LINE to %u, hr = 0x%08X",
+ lGDIPlus_BytesPerLine, hrTemp));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Estimate the size of uncompressed data we need to transfer for each file, assuming
+ // all images in the current acquisition sequence will be scanned using the same scan
+ // parameters. The estimate is relatively accurate for uncompressed data. Cannot use
+ // this kind of estimate for compressed data because of the unknown compression ratio:
+ //
+ if ((S_OK == hr) && bImageTransfer && (!IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory)))
+ {
+ //
+ // Read the image information properties:
+ //
+ hr = wiasReadPropLong(pWiasContext, WIA_IPA_NUMBER_OF_LINES, &lNumberOfLines, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading WIA_IPA_NUMBER_OF_LINES, hr = 0x%08X", hr));
+ }
+
+ if (S_OK == hr)
+ {
+ hr = wiasReadPropLong(pWiasContext, WIA_IPA_BYTES_PER_LINE, &lBytesPerLine, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading WIA_IPA_BYTES_PER_LINE, hr = 0x%08X", hr));
+ }
+ }
+
+ if (S_OK == hr)
+ {
+ hr = wiasReadPropLong(pWiasContext, WIA_IPA_PIXELS_PER_LINE, &lPixelsPerLine, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading WIA_IPA_PIXELS_PER_LINE, hr = 0x%08X", hr));
+ }
+ }
+
+ if (S_OK == hr)
+ {
+ hr = wiasReadPropLong(pWiasContext, WIA_IPA_COMPRESSION, &lCompression, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed reading WIA_IPA_COMPRESSION, hr = 0x%08X", hr));
+ }
+ }
+
+ if ((S_OK == hr) && (lBytesPerLine > 0) && (WIA_COMPRESSION_NONE == lCompression))
+ {
+ ulEstimatedFileSize = lBytesPerLine * lNumberOfLines;
+
+ if (IsEqualGUID(pmdtc->guidFormatID, WiaImgFmt_BMP))
+ {
+ ulEstimatedFileSize += sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
+ }
+ else if (IsEqualGUID(pmdtc->guidFormatID, WiaImgFmt_RAW))
+ {
+ ulEstimatedFileSize += sizeof(WIA_RAW_HEADER);
+ }
+
+ WIAS_TRACE((g_hInst, "Transfer file size for uncompressed image data: %02.2f KB (%u BPL, %u lines)",
+ ulEstimatedFileSize / 1024.0f, lBytesPerLine, lNumberOfLines));
+ }
+ else
+ {
+ ulEstimatedFileSize = ulFileSize;
+ WIAS_TRACE((g_hInst, "Transfer file size for compressed image data: %02.2f KB (%u uncompreessed BPL, %u lines)",
+ ulEstimatedFileSize / 1024.0f, lBytesPerLine, lNumberOfLines));
+ }
+ }
+
+ if ((S_OK == hr) && (!bImageTransfer))
+ {
+ ulEstimatedFileSize = ulFileSize;
+ WIAS_TRACE((g_hInst, "Transfer file size for metadata: %02.2f bytes", ulEstimatedFileSize));
+ }
+
+ //
+ // Begin the actual data transfer procedure to the WIA application:
+ //
+ //
+ #pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "A sample scan job duration does not exceed 49 days. If there is risk to exceed, use GetTickCount64 instead:"
+ dwJobStart = GetTickCount();
+ lFilesProcessed = 0;
+ while (S_OK == hr)
+ {
+ bSkipTransfer = FALSE;
+
+ //
+ // Request a new WIA stream from the WIA client application:
+ //
+ if (S_OK == hr)
+ {
+ _Analysis_assume_nullterminated_(bstrItemName);
+ hr = pTransferCallback->GetNextStream(0, bstrItemName, bstrFullItemName, &pWiaStream);
+ if (S_FALSE == hr)
+ {
+ bCancelTransfer = TRUE;
+ WIAS_TRACE((g_hInst, "IWiaMiniDrvTransferCallback::GetNextStream returned S_FALSE (0x%08X), transfer must be canceled", hr));
+ }
+ else if (WIA_STATUS_SKIP_ITEM == hr)
+ {
+ bSkipTransfer = TRUE;
+ WIAS_TRACE((g_hInst, "IWiaMiniDrvTransferCallback::GetNextStream returned WIA_STATUS_SKIP_ITEM (0x%08X), transfer must be skipped", hr));
+ hr = S_OK;
+ }
+ else if (SUCCEEDED(hr) && (S_OK != hr))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::GetNextStream returned an unknown success value, hr = 0x%08X", hr));
+ hr = E_UNEXPECTED;
+ }
+ else if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::GetNextStream failed, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Signal start of data transfer to the WIA application and check
+ // if the application asks already for the transfers to be cancelled:
+ //
+ if ((S_OK == hr) && (!bSkipTransfer))
+ {
+ pCallbackTransferParams->lMessage = WIA_TRANSFER_MSG_STATUS;
+ pCallbackTransferParams->hrErrorStatus = 0;
+ pCallbackTransferParams->lPercentComplete = 0;
+ pCallbackTransferParams->ulTransferredBytes = 0;
+
+ WIAS_TRACE((g_hInst, "Transfer callback: WIA_TRANSFER_MSG_STATUS, 0 bytes"));
+ hr = pTransferCallback->SendMessage(0, pCallbackTransferParams);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage failed, hr = 0x%08X", hr));
+ }
+ else if (S_FALSE == hr)
+ {
+ bCancelTransfer = TRUE;
+ }
+ else if (S_OK != hr)
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage returned unknown success value, hr = 0x%08X", hr));
+ bCancelTransfer = TRUE;
+ hr = S_FALSE;
+ }
+ }
+
+ if (S_OK == hr)
+ {
+ if (bSkipTransfer)
+ {
+ WIAS_TRACE((g_hInst, "Discarding data to skip the current file transfer (%u)..", lFilesProcessed + 1));
+ }
+ else
+ {
+ //
+ // Read the test file and transfer it to the WIA application:
+ //
+ if ((S_OK == hr) && (!bCancelTransfer))
+ {
+ hr = TransferFile(pOutputStream ? pOutputStream : pInputStream, pWiaStream, pTransferBuffer, ulBufferSize,
+ pTransferCallback, pCallbackTransferParams, ulEstimatedFileSize, &bCancelTransfer);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "File transfer to WIA stream failed, hr = 0x%08X", hr));
+ }
+ }
+ }
+ }
+
+ //
+ // The transfer of one file was successfully completed, reset WIA_STATUS_END_OF_MEDIA:
+ //
+ if (WIA_STATUS_END_OF_MEDIA == hr)
+ {
+ WIAS_TRACE((g_hInst, "File transfer complete (WIA_STATUS_END_OF_MEDIA)"));
+ hr = S_OK;
+ }
+
+ //
+ // Signal 100% transfer complete to the WIA client application (pCallbackTransferParams->ulTransferredBytes
+ // contains the total number of bytes transferred to this stream):
+ //
+ if ((S_OK == hr) && (!bSkipTransfer))
+ {
+ pCallbackTransferParams->lMessage = WIA_TRANSFER_MSG_STATUS;
+ pCallbackTransferParams->hrErrorStatus = 0;
+ pCallbackTransferParams->lPercentComplete = 100;
+
+ WIAS_TRACE((g_hInst, "Transfer callback: WIA_TRANSFER_MSG_STATUS, transfer complete, %02.2f KB total (%u)",
+ pCallbackTransferParams->ulTransferredBytes / 1024.0f, pCallbackTransferParams->lPercentComplete));
+
+ hr = pTransferCallback->SendMessage(0, pCallbackTransferParams);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage(WIA_TRANSFER_MSG_STATUS, transfer complete) failed, hr = 0x%08X", hr));
+ }
+ else if (S_FALSE == hr)
+ {
+ bCancelTransfer = TRUE;
+ }
+ else if (S_OK != hr)
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage(WIA_TRANSFER_MSG_STATUS, transfer complete) returned unknown success value, hr = 0x%08X", hr));
+ bCancelTransfer = TRUE;
+ hr = S_FALSE;
+ }
+ }
+
+ //
+ // If the transfer was canceled from the client application send a CancelJob
+ // to the scanner device to ensure scanning is stopped and all scanned image
+ // data (not read yet) is discarded from the scanner internal buffer:
+ //
+ if (bCancelTransfer)
+ {
+ WIAS_TRACE((g_hInst, "Transfer cancelled from WIA application.."));
+
+ //
+ // Make sure hr is left set to S_FALSE (cancelled):
+ //
+ hr = S_FALSE;
+ }
+ //
+ // The current job must be canceled at the device if there is an error during the data
+ // transfer phase, error which may result in leaving the current job in a processing
+ // state and risk to block the scanner from accepting new jobs until this job times out:
+ //
+ else if (FAILED(hr))
+ {
+ WIAS_TRACE((g_hInst, "Transfer failed (hr = 0x%08X), abort job..", hr));
+ }
+
+ //
+ // Clean-up after finishing each individual WIA file transfer:
+ //
+
+ pCallbackTransferParams->lPercentComplete = 0;
+ pCallbackTransferParams->ulTransferredBytes = 0;
+
+ //
+ // Release the current WIA transfer stream:
+ //
+ if (pWiaStream)
+ {
+ pWiaStream->Release();
+ pWiaStream = NULL;
+ }
+
+ //
+ // Note the loop ends when hr != S_OK.
+ //
+ // Increment the number of streams (files) transferred
+ // and check if we must end the transfers here:
+ //
+ if (S_OK == hr)
+ {
+ //
+ // If a finite number of files must be processed (transferred and/or skipped):
+ //
+ lFilesProcessed++;
+
+ WIAS_TRACE((g_hInst, "Successfully transferred %u file(s)", lFilesProcessed));
+
+ if (lFilesToProcess && (lFilesProcessed >= lFilesToProcess))
+ {
+ //
+ // Note that in general for the Feeder item the driver must signal
+ // end of paper, not S_OK (as documented by MSDN), to ensure that the
+ // application does not request another drvAcquireItemData. In this
+ // case however we transfer multiple files in a single call so we
+ // won't do this:
+ //
+ // if (IsEqualGUID(WIA_CATEGORY_FEEDER, guidItemCategory))
+ // {
+ // hr = WIA_ERROR_PAPER_EMPTY;
+ // }
+ //
+
+ //
+ // Do not transfer another file for this call:
+ //
+ hr = WIA_STATUS_END_OF_MEDIA;
+ }
+ //
+ // For feeder acquisitions, if job separators and/or multi-feed detection is enabled,
+ // check if the sample driver needs to perform an action here (such as stopping the scan):
+ //
+ else if ((!(lFilesProcessed % JOB_SEPARATOR_AT_PAGE)) && (WIA_SEPARATOR_DISABLED != lJobSeparators))
+ {
+ WIAS_TRACE((g_hInst, "Job separator detected"));
+ if ((WIA_SEPARATOR_DETECT_SCAN_STOP == lJobSeparators) || (WIA_SEPARATOR_DETECT_NOSCAN_STOP == lJobSeparators))
+ {
+ WIAS_TRACE((g_hInst, "End of scan job due to job separator"));
+ hr = WIA_STATUS_END_OF_MEDIA;
+ }
+ }
+ else if ((!(lFilesProcessed % MULTI_FEED_AT_PAGE)) && (WIA_MULTI_FEED_DETECT_DISABLED != lMultiFeed))
+ {
+ WIAS_TRACE((g_hInst, "Multi-feed detected"));
+ if (WIA_MULTI_FEED_DETECT_STOP_SUCCESS == lMultiFeed)
+ {
+ WIAS_TRACE((g_hInst, "End of scan job due to multi-feed"));
+ hr = WIA_STATUS_END_OF_MEDIA;
+ }
+ else if (WIA_MULTI_FEED_DETECT_STOP_ERROR == lMultiFeed)
+ {
+ WIAS_TRACE((g_hInst, "Failed scan job due to multi-feed"));
+ hr = WIA_ERROR_MULTI_FEED;
+ }
+ }
+
+ //
+ // For image transfers, if lFileToTransfer is 0 (ALL_PAGES) the transfers end (with S_OK) when either:
+ //
+ // 1) an error occurrs;
+ // 2) a file transfer is canceled (either from the app or the scanner);
+ // 3) when RetrieveImage would return WIA_ERROR_PAPER_EMPTY.
+ //
+ }
+ }
+
+ //
+ // Compute the number of pages per minute (PPM) transferred for this job:
+ //
+ if ((lFilesProcessed > 0) && bImageTransfer)
+ {
+ #pragma prefast(suppress:__WARNING_USE_OTHER_FUNCTION, "A sample scan job duration does not exceed 49 days. If there is risk to exceed, use GetTickCount64 instead:"
+ DWORD dwJobEnd = GetTickCount();
+
+ if (dwJobEnd > dwJobStart)
+ {
+ float fSecondsElapsed = (dwJobEnd - dwJobStart) / 1000.0f;
+
+ if (lFilesProcessed > 1)
+ {
+ DWORD dwPagesPerMinute = (DWORD)((60 * lFilesProcessed) / fSecondsElapsed);
+ WIAS_TRACE((g_hInst, "Measured scan performance for this job is %u PPM (%u single-page image files transferred in %0.2f seconds)",
+ dwPagesPerMinute, lFilesProcessed, fSecondsElapsed));
+ }
+ else
+ {
+ WIAS_TRACE((g_hInst, "1 image file transferred in %0.2f seconds", fSecondsElapsed));
+ }
+ }
+ }
+
+ //
+ // Keep the image in the item context, if there is one:
+ //
+ if (pWiaDriverItemContext->m_pUploadedImage)
+ {
+ pInputStream = NULL;
+ }
+
+ //
+ // Clean-up for memory streams (set to automatically free memory on release)
+ // used for the test image:
+ //
+ if (pInputStream)
+ {
+ pInputStream->Release();
+ }
+ if (pOutputStream)
+ {
+ pOutputStream->Release();
+ }
+
+ //
+ // The transfers (of all files) were successfully completed, reset WIA_STATUS_END_OF_MEDIA:
+ //
+ if (WIA_STATUS_END_OF_MEDIA == hr)
+ {
+ WIAS_TRACE((g_hInst, "All files successfully transferred"));
+
+ hr = S_OK;
+ }
+
+ //
+ // If this job call is about to return WIA_ERROR_PAPER_EMPTY we have to consider
+ // first the following conditions:
+ //
+ // - if an undefined number of transfers had to be performed and at least
+ // one file was transferred the return code must be changed to S_OK;
+ //
+ // - if a finite number of transfers had to be performed and all these
+ // transfers have been done the return code must be changed to S_OK;
+ //
+ // - if a finite number of transfers had to be performed and at least one
+ // of these transfers - but not all of them - has been done the return
+ // code must be changed to WIA_STATUS_END_OF_MEDIA;
+ //
+ // - WIA_ERROR_PAPER_EMPTY must be returned only when no transfers could
+ // be completed in the current drvAcquireItemData call.
+ //
+ if (WIA_ERROR_PAPER_EMPTY == hr)
+ {
+ if (((!lFilesToProcess) && (lFilesProcessed >= 1)) ||
+ ((lFilesToProcess > 0) && (lFilesProcessed >= lFilesToProcess)))
+ {
+ WIAS_TRACE((g_hInst, "Running out of paper, returning S_OK for drvAcquireItemData.."));
+ hr = S_OK;
+ }
+ else if ((lFilesToProcess > 0) && (lFilesProcessed < lFilesToProcess) && (lFilesProcessed >= 1))
+ {
+ WIAS_TRACE((g_hInst, "Running out of paper, returning WIA_STATUS_END_OF_MEDIA for drvAcquireItemData.."));
+ hr = WIA_STATUS_END_OF_MEDIA;
+ }
+ else
+ {
+ WIAS_TRACE((g_hInst, "Running out of paper, returning WIA_ERROR_PAPER_EMPTY for drvAcquireItemData.."));
+ }
+ }
+ else if (WIA_ERROR_MULTI_FEED == hr)
+ {
+ WIAS_TRACE((g_hInst, "Multi-feed error, returning WIA_ERROR_MULTI_FEED for drvAcquireItemData.."));
+ }
+
+ WIAEX_TRACE_FUNC_HR;
+
+ return hr;
+}
+
+/**************************************************************************\
+*
+* Helper for CWiaDriver::drvAcquireItemData. Executes the upload data
+* transfer sequence for the current scan job,
+*
+* Parameters:
+*
+* pWiasContext - item context
+* guidItemCategory - item category
+* bstrItemName - item name
+* bstrFullItemName - full item name
+* pmdc - driver transfer context data
+* pTransferBuffer - pre-allocated transfer buffer
+* ulBufferSize - size of the pre-allocated transfer buffer, in bytes
+* pTransferCallback - IWiaMiniDrvTransferCallback* for WIA status
+* pCallbackTransferParams - WiaTransferParams* for WIA status callbacks
+* ulEstimatedFileSize - estimated file size, in bytes (0 if unknown)
+*
+* Return Value:
+*
+* S_OK if successful, S_FALSE if the transfer is canceled
+* or an error HRESULT if an error occurrs
+*
+\**************************************************************************/
+
+HRESULT
+CWiaDriver::Upload(
+ _In_ BYTE *pWiasContext,
+ GUID guidItemCategory,
+ _In_ BSTR bstrItemName,
+ _In_ BSTR bstrFullItemName,
+ _In_reads_bytes_(ulBufferSize) BYTE *pTransferBuffer,
+ ULONG ulBufferSize,
+ _In_ IWiaMiniDrvTransferCallback *pTransferCallback,
+ _In_ WiaTransferParams *pCallbackTransferParams,
+ _In_ WIA_DRIVER_ITEM_CONTEXT *pWiaDriverItemContext)
+{
+ HRESULT hr = S_OK;
+
+ IStream *pInputStream = NULL;
+ IStream *pWiaStream = NULL;
+ BOOL bCancelTransfer = FALSE;
+ BOOL bSkipTransfer = FALSE;
+ GUID guidUploadFormat = GUID_NULL;
+
+ WIAEX_TRACE_BEGIN;
+
+ if ((!pWiasContext) || (!bstrItemName) || (!bstrFullItemName) || (!pTransferBuffer) || (!pTransferCallback) || (!pCallbackTransferParams))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr));
+ }
+
+ if ((!IsEqualGUID(WIA_CATEGORY_IMPRINTER, guidItemCategory)) && (!IsEqualGUID(WIA_CATEGORY_ENDORSER, guidItemCategory)))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "This driver does not support upload transfer direction for the requested item, hr = 0x%08X", hr));
+ }
+
+ //
+ // Create a new global memory stream to store the transfer data file:
+ //
+ if (SUCCEEDED(hr))
+ {
+ hr = CreateStreamOnHGlobal(NULL, TRUE, &pInputStream);
+ if (SUCCEEDED(hr) && (!pInputStream))
+ {
+ hr = E_FAIL;
+ }
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "CreateStreamOnHGlobal failed, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // This sample driver executes a very simple data transfer upload for the imprinter/endorser data.
+ //
+ // For WiaImgFmt_BMP transfers the driver validates that the image dimensions match
+ // WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN/MAX_WIDTH/HEIGHT dimensions.
+ //
+ // For WiaImgFmt_TXT and WiaImgFmt_CSV transfers the driver loads the transferred text and
+ // attempts to update the current WIA_IPS_PRINTER_ENDORSER_STRING value with it, performing
+ // validation as if the application would directly set WIA_IPS_PRINTER_ENDORSER_STRING.
+ //
+ // It was already validated that WIA_MINIDRV_TRANSFER_UPLOAD works only for
+ // WIA_CATEGORY_IMPRINTER and WIA_CATEGORY_ENDORSER.
+ //
+
+ //
+ // Request the WIA transfer stream from the WIA client application:
+ //
+ if (SUCCEEDED(hr))
+ {
+ _Analysis_assume_nullterminated_(bstrItemName);
+ hr = pTransferCallback->GetNextStream(0, bstrItemName, bstrFullItemName, &pWiaStream);
+ if (S_FALSE == hr)
+ {
+ bCancelTransfer = TRUE;
+ WIAS_TRACE((g_hInst, "IWiaMiniDrvTransferCallback::GetNextStream returned S_FALSE (0x%08X), transfer must be canceled", hr));
+ }
+ else if (WIA_STATUS_SKIP_ITEM == hr)
+ {
+ bSkipTransfer = TRUE;
+ WIAS_TRACE((g_hInst, "IWiaMiniDrvTransferCallback::GetNextStream returned WIA_STATUS_SKIP_ITEM (0x%08X), transfer must be skipped", hr));
+ hr = S_OK;
+ }
+ else if (SUCCEEDED(hr) && (S_OK != hr))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::GetNextStream returned an unknown success value, hr = 0x%08X", hr));
+ hr = E_UNEXPECTED;
+ }
+ else if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::GetNextStream failed, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Signal start of data transfer from the WIA application and check if the application asks for the transfer to be cancelled:
+ //
+ if ((S_OK == hr) && (!bSkipTransfer))
+ {
+ pCallbackTransferParams->lMessage = WIA_TRANSFER_MSG_STATUS;
+ pCallbackTransferParams->hrErrorStatus = 0;
+ pCallbackTransferParams->lPercentComplete = 0;
+ pCallbackTransferParams->ulTransferredBytes = 0;
+
+ WIAS_TRACE((g_hInst, "Transfer callback: WIA_TRANSFER_MSG_STATUS, 0 bytes"));
+ hr = pTransferCallback->SendMessage(0, pCallbackTransferParams);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage failed, hr = 0x%08X", hr));
+ }
+ else if (S_FALSE == hr)
+ {
+ bCancelTransfer = TRUE;
+ }
+ else if (S_OK != hr)
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage returned unknown success value, hr = 0x%08X", hr));
+ bCancelTransfer = TRUE;
+ hr = S_FALSE;
+ }
+ }
+
+ if ((S_OK == hr) && (!bSkipTransfer))
+ {
+ //
+ // Transfer the file from the WIA application:
+ //
+ if ((S_OK == hr) && (!bCancelTransfer))
+ {
+ hr = TransferFile(pWiaStream, pInputStream, pTransferBuffer, ulBufferSize,
+ pTransferCallback, pCallbackTransferParams, 0, &bCancelTransfer);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "File transfer from the WIA stream failed, hr = 0x%08X", hr));
+ }
+ }
+ }
+
+ //
+ // The transfer of one file was successfully completed, reset WIA_STATUS_END_OF_MEDIA:
+ //
+ if (WIA_STATUS_END_OF_MEDIA == hr)
+ {
+ WIAS_TRACE((g_hInst, "File transfer complete (WIA_STATUS_END_OF_MEDIA)"));
+ hr = S_OK;
+ }
+
+ //
+ // Signal 100% transfer complete to the WIA client application (pCallbackTransferParams->ulTransferredBytes
+ // contains the total number of bytes transferred to this stream):
+ //
+ if ((S_OK == hr) && (!bSkipTransfer))
+ {
+ pCallbackTransferParams->lMessage = WIA_TRANSFER_MSG_STATUS;
+ pCallbackTransferParams->hrErrorStatus = 0;
+ pCallbackTransferParams->lPercentComplete = 100;
+
+ WIAS_TRACE((g_hInst, "Transfer callback: WIA_TRANSFER_MSG_STATUS, transfer complete, %02.2f KB total (%u)",
+ pCallbackTransferParams->ulTransferredBytes / 1024.0f, pCallbackTransferParams->lPercentComplete));
+
+ hr = pTransferCallback->SendMessage(0, pCallbackTransferParams);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage(WIA_TRANSFER_MSG_STATUS, transfer complete) failed, hr = 0x%08X", hr));
+ }
+ }
+
+
+ //
+ // Clean-up after finishing each individual WIA file transfer:
+ //
+
+ pCallbackTransferParams->lPercentComplete = 0;
+ pCallbackTransferParams->ulTransferredBytes = 0;
+
+ //
+ // Release the current WIA transfer stream:
+ //
+ if (pWiaStream)
+ {
+ pWiaStream->Release();
+ pWiaStream = NULL;
+ }
+
+ //
+ // For upload transfers we need to check the current WIA_IPA_FORMAT to see what
+ // format is the file that was uploaded by the application, the MINIDRV_TRANSFER_CONTEXT
+ // structure not being initialized with the right file format in this case.
+ // Remember that the WIA application needs to set WIA_IPA_FORMAT before calling
+ // IWiaTransfer::Upload:
+ //
+ if (S_OK == hr)
+ {
+ hr = wiasReadPropGuid(pWiasContext, WIA_IPA_FORMAT, &guidUploadFormat, NULL, TRUE);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to read WIA_IPA_FORMAT, hr = 0x%08X", hr));
+ }
+ }
+
+ if (S_OK == hr)
+ {
+ //
+ // At this point pInputStream contains the data uploaded by the application
+ //
+ if (IsEqualGUID(WiaImgFmt_BMP, guidUploadFormat))
+ {
+ if (SUCCEEDED(IsDibValid(pInputStream, 1, IMPRINTER_MAX_WIDTH, IMPRINTER_MAX_HEIGHT)))
+ {
+ if (pWiaDriverItemContext->m_pUploadedImage)
+ {
+ IStream *pTemp = pWiaDriverItemContext->m_pUploadedImage;
+ pWiaDriverItemContext->m_pUploadedImage = NULL;
+ pTemp->Release();
+ }
+ pWiaDriverItemContext->m_pUploadedImage = pInputStream;
+ pInputStream = NULL;
+ }
+ else
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst,
+ "The uploaded WiaImgFmt_BMP (DIB) image is invalid. This item accepts only a 1-bpp %ux%u pixels image, bottom to top line order, hr = 0x%08X",
+ IMPRINTER_MAX_WIDTH, IMPRINTER_MAX_HEIGHT, hr));
+ }
+ }
+ else if (IsEqualGUID(WiaImgFmt_TXT, guidUploadFormat) || IsEqualGUID(WiaImgFmt_CSV, guidUploadFormat))
+ {
+ if (FAILED(IsImprinterEndorserTextValid(pWiasContext, pInputStream, IsEqualGUID(WIA_CATEGORY_IMPRINTER, guidItemCategory) ? IMPRINTER : ENDORSER)))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "The uploaded text data is invalid, hr = 0x%08X", hr));
+ }
+ }
+ else
+ {
+ USHORT *pUUID = NULL;
+ hr = E_INVALIDARG;
+
+ if (RPC_S_OK == UuidToStringW(&guidUploadFormat, &pUUID))
+ {
+ WIAEX_ERROR((g_hInst, "Unsupported upload transfer file format (%ws), hr = 0x%08X", pUUID, hr));
+ RpcStringFreeW(&pUUID);
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Unsupported upload transfer file format, hr = 0x%08X", hr));
+ }
+ }
+ }
+
+ if (pInputStream)
+ {
+ pInputStream->Release();
+ }
+
+
+ WIAEX_TRACE_FUNC_HR;
+
+ return hr;
+}
+
+/**************************************************************************\
+*
+* Helper for CWiaDriver::drvAcquireItemData. Transfers one file (image or
+* metadata) to the WIA transfer stream provided by the aplication. Note that
+* for image file transfers it is not a good solution to let GDI+ to write the
+* image directly to the WIA transfer stream as the WIA stream has special
+* requirements such as predetermined buffer size, reposition of the write
+* cursor at the beginning of the stream between writes and WIA notifications.
+*
+* Parameters:
+*
+* pInputStream - IStream object to read the file from
+* pDestinationStream - IStream object to write the file to
+* pTransferBuffer - pre-allocated transfer buffer
+* ulBufferSize - size of the pre-allocated transfer buffer, in bytes
+* pTransferCallback - optional IWiaMiniDrvTransferCallback* for WIA status
+* pCallbackTransferParams - optional WiaTransferParams* for WIA status callbacks
+* ulEstimatedFileSize - estimated file size, in bytes (0 if unknown)
+* pbCancelTransfer - on return indicates if the WIA client canceled
+* the operation
+*
+* Remarks:
+*
+* The caller may specify a non zero pCallbackTransferParams->lPercentComplete
+* value (between 0% and 49%) to have this function resume incrementing the
+* percent complete starting from this value. An invalid value is set to 0%
+* and does not cause the function to fail (still, the caller must not do this).
+*
+* Return Value:
+*
+* WIA_STATUS_END_OF_MEDIA if successful, S_FALSE if the transfer
+* is canceled or an error HRESULT if an error occurrs
+*
+\**************************************************************************/
+
+HRESULT
+CWiaDriver::TransferFile(
+ _In_ IStream *pInputStream,
+ _In_ IStream *pDestinationStream,
+ _In_reads_bytes_(ulBufferSize)
+ BYTE *pTransferBuffer,
+ ULONG ulBufferSize,
+ _In_opt_ IWiaMiniDrvTransferCallback *pTransferCallback,
+ _In_opt_ WiaTransferParams *pCallbackTransferParams,
+ ULONG ulEstimatedFileSize,
+ _Out_ BOOL *pbCancelTransfer)
+{
+ HRESULT hr = S_OK;
+ HRESULT hrTemp = S_OK;
+
+ ULONG ulBytesToRead = 0;
+ ULONG ulBytesRead = 0;
+ ULONG ulBytesToWrite = 0;
+ ULONG ulBytesWritten = 0;
+ ULONG ulFileBytesWritten = 0;
+ ULONG ulPercentComplete = 0;
+ ULONG ulStartProgressFrom = 0;
+
+ const LARGE_INTEGER liZeroOffset = {};
+
+ WIAEX_TRACE_BEGIN;
+
+ if ((!pInputStream) || (!pDestinationStream) || (!pTransferBuffer) || (!pbCancelTransfer))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr));
+ }
+ else
+ {
+ *pbCancelTransfer = FALSE;
+ }
+
+ if ((S_OK == hr) && pTransferCallback && pCallbackTransferParams && pCallbackTransferParams->lPercentComplete)
+ {
+ if ((pCallbackTransferParams->lPercentComplete < 0) || (pCallbackTransferParams->lPercentComplete > 49))
+ {
+ WIAEX_ERROR((g_hInst, "Invalid WiaTransferParams::lPercentComplete parameter (%d), reset to 0",
+ pCallbackTransferParams->lPercentComplete));
+ ulStartProgressFrom = 0;
+ }
+ else
+ {
+ ulStartProgressFrom = (ULONG)pCallbackTransferParams->lPercentComplete;
+ WIAS_TRACE((g_hInst, "Resuming progress indicator from %u", ulStartProgressFrom));
+ }
+ }
+
+ if (S_OK == hr)
+ {
+ //
+ // Make sure the input stream pointer is at the beginning of the stream before reading from it:
+ //
+ hr = pInputStream->Seek(liZeroOffset, STREAM_SEEK_SET, NULL);
+ if (S_OK != hr)
+ {
+ WIAEX_ERROR((g_hInst, "IStream::Seek(0, STREAM_SEEK_SET, NULL) failed, hr = 0x%08X", hr));
+ }
+ }
+
+
+ //
+ // Resume from current pCallbackTransferParams->lPercentComplete,
+ // do not reset to 0 pCallbackTransferParams->lPercentComplete:
+ //
+ if ((S_OK == hr) && pCallbackTransferParams)
+ {
+ pCallbackTransferParams->ulTransferredBytes = 0;
+ }
+
+ while (S_OK == hr)
+ {
+ ulBytesRead = 0;
+ ulBytesToRead = ulBufferSize;
+ ulBytesWritten = 0;
+ ulBytesToWrite = 0;
+ *pbCancelTransfer = FALSE;
+
+ //
+ // Read one buffer of data from the input stream. Note the source IStream
+ // may return S_OK and no data when the transfer is complete:
+ //
+ hr = pInputStream->Read(pTransferBuffer, ulBytesToRead, &ulBytesRead);
+ if ((S_FALSE == hr) || ((S_OK == hr) && (!ulBytesRead)))
+ {
+ //
+ // End of file, transfer of this file should be complete:
+ //
+ hr = WIA_STATUS_END_OF_MEDIA;
+ WIAS_TRACE((g_hInst, "IStream::Read: end of media, data transfer complete"));
+ }
+ else if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "IStream::Read failed, hr = 0x%08X", hr));
+ }
+ else if (ulBytesRead > ulBytesToRead)
+ {
+ hr = E_UNEXPECTED;
+ WIAEX_ERROR((g_hInst, "IStream::Read caused a possible buffer overflow (%u bytes over %u limit), hr = 0x%08X",
+ ulBytesRead - ulBytesToRead, ulBytesToRead, hr));
+ }
+
+ //
+ // Write the the read buffer content to the destination stream:
+ //
+ if ((S_OK == hr) || ((WIA_STATUS_END_OF_MEDIA == hr) && (ulBytesRead > 0)))
+ {
+ //
+ // Make sure the write stream pointer is at the end of the stream before writing to it:
+ //
+ hrTemp = pDestinationStream->Seek(liZeroOffset, STREAM_SEEK_END, NULL);
+ if (FAILED(hrTemp))
+ {
+ WIAEX_ERROR((g_hInst, "IStream::Seek(0, STREAM_SEEK_END, NULL) failed, hr = 0x%08X", hrTemp));
+ }
+
+ //
+ // Write the buffer to the destination stream:
+ //
+ if (S_OK == hrTemp)
+ {
+ ulBytesToWrite = ulBytesRead;
+
+ hrTemp = pDestinationStream->Write(pTransferBuffer, ulBytesToWrite, &ulBytesWritten);
+ if (FAILED(hrTemp))
+ {
+ WIAEX_ERROR((g_hInst, "IStream::Write(%u bytes) failed, hr = 0x%08X", ulBytesToWrite, hrTemp));
+ }
+ }
+
+ //
+ // Make progress callback to the WIA client application and check for a cancel transfer request:
+ //
+ if (S_OK == hrTemp)
+ {
+ if (pTransferCallback && pCallbackTransferParams)
+ {
+ ulFileBytesWritten += ulBytesWritten;
+
+ //
+ // Indicate to ComputeTransferProgress a direct/full transfer (0% .. 99%):
+ //
+ ComputeTransferProgress(&ulPercentComplete, ulEstimatedFileSize, ulFileBytesWritten, TRUE, ulStartProgressFrom);
+
+ pCallbackTransferParams->lMessage = WIA_TRANSFER_MSG_STATUS;
+ pCallbackTransferParams->ulTransferredBytes = ulFileBytesWritten;
+ pCallbackTransferParams->lPercentComplete = ulPercentComplete;
+
+ WIAS_TRACE((g_hInst, "Transfer callback: WIA_TRANSFER_MSG_STATUS, %u bytes, %02.2f KB total (%u)",
+ ulBytesWritten, pCallbackTransferParams->ulTransferredBytes / 1024.0f, ulPercentComplete));
+
+ hrTemp = pTransferCallback->SendMessage(0, pCallbackTransferParams);
+ if (FAILED(hrTemp))
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage(%u bytes written) failed, hr = 0x%08X",
+ ulBytesWritten, hrTemp));
+ }
+ else if (S_FALSE == hrTemp)
+ {
+ *pbCancelTransfer = TRUE;
+ }
+ else if (S_OK != hrTemp)
+ {
+ WIAEX_ERROR((g_hInst, "IWiaMiniDrvTransferCallback::SendMessage(%u bytes written) returned unknown success value, hr = 0x%08X",
+ ulBytesWritten, hrTemp));
+ *pbCancelTransfer = TRUE;
+ hrTemp = S_FALSE;
+ }
+ }
+ else
+ {
+ WIAS_TRACE((g_hInst, "Transferred %u bytes, %02.2f KB total (%u)", ulBytesWritten,
+ ulFileBytesWritten / 1024.0f, ulPercentComplete));
+ }
+ }
+
+ //
+ // Preserve WIA_STATUS_END_OF_MEDIA to allow this loop to normally end, unless a failure was encountered:
+ //
+ if ((S_OK != hrTemp) || (WIA_STATUS_END_OF_MEDIA != hr))
+ {
+ hr = hrTemp;
+ }
+ }
+ }
+
+ WIAEX_TRACE_FUNC_HR;
+
+ return hr;
+}
+
+/**************************************************************************\
+*
+* Helper for IWiaMiniDrv::drvAcquireItemData. Executes a simple validation
+* for the WiaImgFmt_BMP (DIB) image uploaded by the application and stored
+* in the specified stream.
+*
+* Parameters:
+*
+* pStream - stream source for the image to be validated
+* lBitDepth - expected pixel bit depth (e.g. 24)
+* lWidth - expected image width, in pixels
+* lHeight - expected image height, in pixels; also describes
+* the DIB line order (if negative the image data is
+* top to bottom, positive means bottom to top)
+*
+* Return Value:
+*
+* S_OK if successful and the image is valid, E_INVALIDARG if the image is
+* invalid or another error HRESULT if another error occurrs and the image
+* cannot be validated
+*
+\**************************************************************************/
+
+HRESULT
+CWiaDriver::IsDibValid(
+ _In_ IStream* pStream,
+ LONG lBitDepth,
+ LONG lWidth,
+ LONG lHeight)
+{
+ HRESULT hr = S_OK;
+ LARGE_INTEGER lStart = {};
+ BITMAPINFO bi = {};
+
+ WIAEX_TRACE_BEGIN;
+
+ if (!pStream)
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ lStart.LowPart = sizeof(BITMAPFILEHEADER);
+
+ hr = pStream->Seek(lStart, STREAM_SEEK_SET, NULL);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to reset the input stream, hr = 0x%08X", hr));
+ }
+ }
+
+ if (S_OK == hr)
+ {
+ hr = pStream->Read(&bi, sizeof(bi), NULL);
+ if (S_OK != hr)
+ {
+ WIAEX_ERROR((g_hInst, "Failed to read the DIB header from the image, hr = 0x%08X", hr));
+ if (SUCCEEDED(hr))
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr) && (bi.bmiHeader.biBitCount != lBitDepth))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid DIB pixel bit depth, got %u bits, expected %u bit(s), hr = 0x%08X",
+ bi.bmiHeader.biBitCount, lBitDepth, hr));
+ }
+
+ if (SUCCEEDED(hr) && (bi.bmiHeader.biWidth != lWidth))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid DIB width, got %u pixels, expected %u pixels, hr = 0x%08X",
+ bi.bmiHeader.biWidth, lWidth, hr));
+ }
+
+ if (SUCCEEDED(hr) && (bi.bmiHeader.biHeight != lHeight))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid DIB height and/or line order, got %l pixels, expected %l pixels, hr = 0x%08X",
+ bi.bmiHeader.biHeight, lHeight, hr));
+ }
+
+ if (S_OK == hr)
+ {
+ lStart.LowPart = 0;
+
+ hr = pStream->Seek(lStart, STREAM_SEEK_SET, NULL);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to reset the input stream, hr = 0x%08X", hr));
+ }
+ }
+
+ WIAEX_TRACE_FUNC_HR;
+
+ return hr;
+
+}
+
+/**************************************************************************\
+*
+* Helper for IWiaMiniDrv::drvAcquireItemData. Executes a simple validation
+* for the WiaImgFmt_TXT and WiaImgFmt_CSV imprinter/endorser text uploaded
+* by the application and stored in the specified stream and if the text is
+* valid it updates WIA_IPS_PRINTER_ENDORSER_STRING.
+*
+* Parameters:
+*
+* pWiasContext - item context
+* pStream - stream source for the image to be validated
+* nDocumentHandlingSelect - IMPRINTER or ENDORSER (defined in wiadef.h)
+*
+* Return Value:
+*
+* S_OK if successful and the image is valid, E_INVALIDARG or another
+* error HRESULT if validation fails or if it cannot be performed
+*
+\**************************************************************************/
+
+HRESULT
+CWiaDriver::IsImprinterEndorserTextValid(
+ _In_ BYTE* pWiasContext,
+ _In_ IStream* pStream,
+ LONG nDocumentHandlingSelect)
+{
+ HRESULT hr = S_OK;
+ LARGE_INTEGER liZero = {};
+ ULARGE_INTEGER ui = {};
+ ULONG ulNumChars = 0;
+ ULONG ulDataSize = 0;
+ PWCHAR pwData = NULL;
+ BSTR bstrData = NULL;
+
+ WIAEX_TRACE_BEGIN;
+
+ if (!pStream)
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = IStream_Size(pStream, &ui);
+ if (SUCCEEDED(hr))
+ {
+ WIAS_TRACE((g_hInst, "Stream size, low: %u bytes, high: %u bytes",
+ ui.LowPart, ui.HighPart));
+
+ if (ui.HighPart > 0)
+ {
+ WIAS_TRACE((g_hInst, "Stream size exceeeds maximum accepted by this item, last %u bytes will be ignored", ui.HighPart));
+ }
+
+ //
+ // Data must contain the two bytes BOM and at least one double-byte character:
+ //
+ if (ui.LowPart >= (2 * sizeof(WCHAR)))
+ {
+ ulDataSize = ui.LowPart;
+ }
+ else
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Insufficient data (less than one actual character), hr = 0x%08X", hr));
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "IStream_Size failed, hr = 0x%08X", hr));
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->Seek(liZero, STREAM_SEEK_SET, NULL);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to reset memory stream, hr = 0x%08X", hr));
+ }
+ }
+
+ //
+ // Read first the BOM bytes and validate:
+ //
+ if (S_OK == hr)
+ {
+ BYTE bBOM[2] = {};
+
+ hr = pStream->Read(bBOM, 2, NULL);
+ if (S_OK == hr)
+ {
+ if ((0xFF != bBOM[0]) || (0xFE != bBOM[1]))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid BOM. Expected 0xFF 0xFE, received 0x%X 0x%X, hr = 0x%08X", bBOM[0], bBOM[1], hr));
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Failed to read the BOM from the stream, hr = 0x%08X", hr));
+ if (SUCCEEDED(hr))
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+ }
+
+ //
+ // Alocate memory for the NULL terminated WCHAR string. The NULL string terminator is not
+ // expected in the stream, however the stream must contain the BOM "character" instead,
+ // thus we need to allocate space for the same number of whole double byte characters
+ // that are in the stream:
+ //
+ if (SUCCEEDED(hr))
+ {
+ ulNumChars = ulDataSize / sizeof(WCHAR);
+ pwData = new WCHAR[ulNumChars];
+ if (pwData)
+ {
+ //
+ // NULL terminate the string:
+ //
+ pwData[ulNumChars - 1] = 0;
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ WIAEX_ERROR((g_hInst, "Out of memory when trying to allocate %u bytes, hr = 0x%08X", ulDataSize, hr));
+ }
+ }
+
+ //
+ // Read the actual imprinter/endorser characters:
+ //
+ if (S_OK == hr)
+ {
+ //
+ // ulNumChars includes the length of the NULL terminator:
+ //
+ ulNumChars -= 1;
+
+ hr = pStream->Read((PBYTE)pwData, ulNumChars * sizeof(WCHAR), NULL);
+ if (S_OK != hr)
+ {
+ WIAEX_ERROR((g_hInst, "Failed to read from the stream, hr = 0x%08X", hr));
+ if (SUCCEEDED(hr))
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+ }
+
+ //
+ // Validate each character against the set of valid characters for the source:
+ //
+ if (SUCCEEDED(hr))
+ {
+ PWCHAR szValidChars = (IMPRINTER == nDocumentHandlingSelect) ? SAMPLE_IMPRINTER_VALID_CHARS : SAMPLE_ENDORSER_VALID_CHARS;
+ ULONG ulValidChars = ((IMPRINTER == nDocumentHandlingSelect) ? ARRAYSIZE(SAMPLE_IMPRINTER_VALID_CHARS) : ARRAYSIZE(SAMPLE_ENDORSER_VALID_CHARS)) - 1;
+ BOOL bFound = FALSE;
+
+ for (ULONG i = 0; i < ulNumChars; i++)
+ {
+ bFound = FALSE;
+
+ for (ULONG j = 0; j < ulValidChars; j++)
+ {
+ if (pwData[i] == szValidChars[j])
+ {
+ bFound = TRUE;
+ i++;
+ break;
+ }
+ }
+
+ if (!bFound)
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid character for WIA_IPS_PRINTER_ENDORSER_STRING: 0x%X (%wc), hr = 0x%08X", pwData[i], pwData[i], hr));
+ break;
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ bstrData = SysAllocString(pwData);
+ if (!bstrData)
+ {
+ hr = E_OUTOFMEMORY;
+ WIAEX_ERROR((g_hInst, "Out of memory when trying to allocate a BSTR of %u characters in length, hr = 0x%08X", ulNumChars, hr));
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = wiasWritePropStr(pWiasContext, WIA_IPS_PRINTER_ENDORSER_STRING, bstrData);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "Failed to set WIA_IPS_PRINTER_ENDORSER_STRING, hr = 0x%08X", hr));
+ }
+ }
+
+ if (bstrData)
+ {
+ SysFreeString(bstrData);
+ }
+
+ if (pwData)
+ {
+ delete[] pwData;
+ }
+
+ WIAEX_TRACE_FUNC_HR;
+
+ return hr;
+
+}
+
+/**************************************************************************\
+*
+* Helper for IWiaMiniDrv::drvAcquireItemData. Loads a test data file (image
+* or metadata) from resources. A test image is expected to be in EXIF format
+* and match the dimensions described by the MIN/MAX_TEST_SCAN_WIDTH/HEIGHT.
+* The function does not attempt to validate the contents of data loaded from
+* resources, it only copies the whole data file to the specified stream.
+*
+* Parameters:
+*
+* ulResourceId - resource identifier
+* pStream - stream destination for the test image
+* pulDataSize - returns the size in bytes of the data loaded from
+* resources and written to the destination stream
+*
+* Return Value:
+*
+* S_OK if successful or an error HRESULT if an error occurrs
+*
+\**************************************************************************/
+
+HRESULT
+CWiaDriver::LoadTestDataResourceToStream(
+ ULONG ulResourceId,
+ _In_ IStream *pStream,
+ _Out_ ULONG *pulDataSize)
+{
+ HRESULT hr = S_OK;
+
+ WIAEX_TRACE_BEGIN;
+
+ if ((!pStream) && (!pulDataSize))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *pulDataSize = 0;
+
+ HRSRC hResourceInfo = FindResource(g_hInst, MAKEINTRESOURCE(ulResourceId), RT_RCDATA);
+ if (hResourceInfo)
+ {
+ ULONG ulWrittenData = 0;
+
+ ULONG ulDataSize = (ULONG)SizeofResource(g_hInst, hResourceInfo);
+ if (ulDataSize > 0)
+ {
+ HGLOBAL hTestData = LoadResource(g_hInst, hResourceInfo);
+ if (hTestData)
+ {
+ PBYTE pTestData = (PBYTE)LockResource(hTestData);
+ if (pTestData)
+ {
+ LARGE_INTEGER lZero = {};
+
+ hr = pStream->Seek(lZero, STREAM_SEEK_SET, NULL);
+ if (S_OK == hr)
+ {
+ hr = pStream->Write(pTestData, ulDataSize, &ulWrittenData);
+ if (S_OK == hr)
+ {
+ if (ulWrittenData != ulDataSize)
+ {
+ hr = E_FAIL;
+ WIAEX_ERROR((g_hInst, "Failed to load data (%u) from resources, expected %u bytes, got %u bytes, hr = 0x%08X",
+ ulResourceId, ulDataSize, ulWrittenData, hr));
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Failed to load data (%u) from resources, hr = 0x%08X", ulResourceId, hr));
+ }
+
+ HRESULT hrTemp = pStream->Seek(lZero, STREAM_SEEK_SET, NULL);
+ if (S_OK != hrTemp)
+ {
+ if (S_OK == hr)
+ {
+ hr = hrTemp;
+ }
+
+ WIAEX_ERROR((g_hInst, "Failed to reset memory stream, hr = 0x%08X", hrTemp));
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Failed to reset memory stream, hr = 0x%08X", hr));
+ }
+
+ }
+ DeleteObject(hTestData);
+ }
+
+ if (S_OK == hr)
+ {
+ *pulDataSize = ulDataSize;
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(::GetLastError());
+ WIAEX_ERROR((g_hInst, "Bad resource (%u), hr = 0x%08X", ulResourceId, hr));
+ }
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(::GetLastError());
+ WIAEX_ERROR((g_hInst, "Cannot find resource (%u), hr = 0x%08X", ulResourceId, hr));
+ }
+ }
+
+ WIAEX_TRACE_FUNC_HR;
+
+ return hr;
+}
+
+/**************************************************************************\
+*
+* Helper for IWiaMiniDrv::drvAcquireItemData. Writes generated or hard-coded
+* sample metadata to the specified download stream. This data is to be
+* transferred from this sample driver to the WIA application client as an
+* imprinter/endorser text/CSV file, or raw barcode/patch code/MICR metadata.
+*
+* Parameters:
+*
+* pWiasContext - item context
+* guidItemCategory - item category (WIA_CATEGORY_ value)
+* guidFormat - trasfer file format (WiaImgFmt_ value)
+* pStream - stream destination for the test image
+* pulDataSize - returns the size in bytes of the data loaded from
+* resources and written to the destination stream
+*
+* Return Value:
+*
+* S_OK if successful or an error HRESULT if an error occurrs
+*
+\**************************************************************************/
+
+HRESULT
+CWiaDriver::LoadTestDataToStream(
+ _In_ BYTE *pWiasContext,
+ GUID guidItemCategory,
+ GUID guidFormat,
+ _In_ IStream *pStream,
+ _Out_ ULONG *pulDataSize)
+{
+ HRESULT hr = S_OK;
+ ULONG ulDataSize = 0;
+
+ WIAEX_TRACE_BEGIN;
+
+ if ((!pWiasContext) || (!pStream) && (!pulDataSize))
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if ((IsEqualGUID(WIA_CATEGORY_IMPRINTER, guidItemCategory) || IsEqualGUID(WIA_CATEGORY_ENDORSER, guidItemCategory)) &&
+ (!IsEqualGUID(WiaImgFmt_BMP, guidFormat)))
+ {
+ if (IsEqualGUID(WiaImgFmt_TXT, guidFormat) || IsEqualGUID(WiaImgFmt_CSV, guidFormat))
+ {
+ BSTR bstrData = NULL;
+
+ //
+ // Read the current WIA_IPS_PRINTER_ENDORSER_STRING character string value
+ // and prepare to write it to the destination stream, for simplicity without
+ // expanding any special formatting sequences. Note the NULL string terminator
+ // is not to be written to the stream (which for this sample it will end up
+ // as a TXT or CSV file):
+ //
+ hr = wiasReadPropStr(pWiasContext, WIA_IPS_PRINTER_ENDORSER_STRING, &bstrData, NULL, TRUE);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->Write(g_bBOM, sizeof(g_bBOM), NULL);
+ if (SUCCEEDED(hr))
+ {
+ ulDataSize = (ULONG)(wcslen((PWCHAR)bstrData) * sizeof(WCHAR));
+
+ hr = pStream->Write((PBYTE)bstrData, ulDataSize, NULL);
+ if (FAILED(hr))
+ {
+ WIAEX_ERROR((g_hInst, "IStream::Write(data) failed, hr = 0x%08X", hr));
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "IStream::Write(prefix) failed, hr = 0x%08X", hr));
+ }
+ }
+ else
+ {
+ WIAEX_ERROR((g_hInst, "Failed to read the current WIA_IPS_PRINTER_ENDORSER_STRING value, hr = 0x%08X", hr));
+ }
+
+ if (bstrData)
+ {
+ SysFreeString(bstrData);
+ }
+ }
+ else
+ {
+ hr = E_INVALIDARG;
+ }
+ }
+ else if (IsEqualGUID(WIA_CATEGORY_BARCODE_READER, guidItemCategory) && IsEqualGUID(WiaImgFmt_RAWBAR, guidFormat))
+ {
+ WIA_BARCODES bc = {};
+ WIA_BARCODE_INFO bi = {};
+
+ //
+ // 3 hard-coded sample barcodes to report. These sample barcodes match the XML sample metadata (Barcodes.xml):
+ //
+
+ WCHAR szBarcodeOne[] = L"036000291452";
+ WCHAR szBarcodeTwo[] = L"3117013206375";
+ WCHAR szBarcodeThree[] = L"This is a Full ASCII Code 39 example";
+
+ //
+ // When computing the data size take into account the following important details:
+ //
+ // WIA_BARCODES includes a WIA_BARCODE_INFO structure element as a placeholder.
+ // WIA_BARCODE_INFO includes a WCHAR pleceholder for the text.
+ // Make sure the size of these fields are not counted twice.
+ //
+ // Do not include the length of the NULL string terminators for the character sequences,
+ // they and not NULL terminated.
+ //
+ // Do not compute the fixed size (excluding the text) of a WIA_BARCODE_INFO structure
+ // doing sizeof(WIA_BARCODE_INFO) - sizeof(WCHAR), this will incorrectly add compiler
+ // padding for the data structure (2 additional bytes by default). Instead compute
+ // the length of the fixed WIA_BARCODE_INFO size as 8 * sizeof(DWORD).
+ //
+ ULONG ulFixedSize = 4 * sizeof(DWORD); //not: sizeof(WIA_BARCODES - sizeof(WIA_BARCODES_INFO)
+ ULONG ulFixedInfoSize = 8 * sizeof(DWORD); //not: sizeof(WIA_BARCODES_INFO) - sizeof(WCHAR)
+ ulDataSize = ulFixedSize + (3 * ulFixedInfoSize) + sizeof(szBarcodeOne) + sizeof(szBarcodeTwo) + sizeof(szBarcodeThree) - (3 * sizeof(WCHAR));
+
+ const char szSignature[] = "WBAR";
+ memcpy(&bc.Tag, szSignature, sizeof(DWORD));
+
+ bc.Version = 0x00010000;
+ bc.Size = ulDataSize;
+ bc.Count = 3;
+
+ hr = pStream->Write(&bc, ulFixedSize, NULL);
+ if (SUCCEEDED(hr))
+ {
+ bi.Size = ulFixedInfoSize + sizeof(szBarcodeOne) - sizeof(WCHAR);
+ bi.Type = 0;
+ bi.Page = 0;
+ bi.Confidence = 5;
+ bi.XOffset = 0;
+ bi.YOffset = 0;
+ bi.Rotation = 90;
+ bi.Length = (DWORD)wcslen(szBarcodeOne);
+
+ hr = pStream->Write(&bi, ulFixedInfoSize, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->Write(szBarcodeOne, sizeof(szBarcodeOne) - sizeof(WCHAR), NULL);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ bi.Size = ulFixedInfoSize + sizeof(szBarcodeTwo) - sizeof(WCHAR);
+ bi.Type = 2;
+ bi.Page = 0;
+ bi.Confidence = 9;
+ bi.XOffset = 2;
+ bi.YOffset = 1000;
+ bi.Rotation = 0;
+ bi.Length = (DWORD)wcslen(szBarcodeTwo);
+
+ hr = pStream->Write(&bi, ulFixedInfoSize, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->Write(szBarcodeTwo, sizeof(szBarcodeTwo) - sizeof(WCHAR), NULL);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ bi.Size = ulFixedInfoSize + sizeof(szBarcodeThree) - sizeof(WCHAR);
+ bi.Type = 7;
+ bi.Page = 0;
+ bi.Confidence = 10;
+ bi.XOffset = 0;
+ bi.YOffset = 2000;
+ bi.Rotation = 0;
+ bi.Length = (DWORD)wcslen(szBarcodeThree);
+
+ hr = pStream->Write(&bi, ulFixedInfoSize, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->Write(szBarcodeThree, sizeof(szBarcodeThree) - sizeof(WCHAR), NULL);
+ }
+ }
+ }
+ else if (IsEqualGUID(WIA_CATEGORY_PATCH_CODE_READER, guidItemCategory) && IsEqualGUID(WiaImgFmt_RAWPAT, guidFormat))
+ {
+ WIA_PATCH_CODES pc = {};
+ WIA_PATCH_CODE_INFO pi = {};
+
+ //
+ // 2 hard-coded sample patch codes to report. These sample codes match the XML sample metadata (PatchCod.xml):
+ //
+
+ ULONG ulFixedSize = 4 * sizeof(DWORD);
+ ULONG ulFixedInfoSize = sizeof(DWORD);
+ ulDataSize = ulFixedSize + (2 * ulFixedInfoSize);
+
+ const char szSignature[] = "WPAT";
+ memcpy(&pc.Tag, szSignature, sizeof(DWORD));
+
+ pc.Version = 0x00010000;
+ pc.Size = ulDataSize;
+ pc.Count = 2;
+
+ hr = pStream->Write(&pc, ulFixedSize, NULL);
+ if (SUCCEEDED(hr))
+ {
+ pi.Type = 2;
+
+ hr = pStream->Write(&pi, ulFixedInfoSize, NULL);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pi.Type = 1;
+
+ hr = pStream->Write(&pi, ulFixedInfoSize, NULL);
+ }
+ }
+ else if (IsEqualGUID(WIA_CATEGORY_MICR_READER, guidItemCategory) && IsEqualGUID(WiaImgFmt_RAWMIC, guidFormat))
+ {
+ WIA_MICR micr = {};
+ WIA_MICR_INFO mi = {};
+
+ //
+ // 2 hard-coded sample MICR codes to report. These sample codes match the XML sample metadata (Micr.xml):
+ //
+
+ WCHAR szMicrOne[] = L"1234567890";
+ WCHAR szMicrTwo[] = L"987?543?10";
+
+ //
+ // When computing the data size take into account the following important details:
+ //
+ // WIA_MICR includes a WIA_MICR_INFO structure element as a placeholder.
+ // WIA_MICR_INFO includes a WCHAR pleceholder for the text.
+ // Make sure the size of these fields are not counted twice.
+ //
+ // Do not include the length of the NULL string terminators for the character sequences,
+ // they and not NULL terminated.
+ //
+ // Do not compute the fixed size (excluding the text) of a WIA_MICR_INFO structure
+ // doing sizeof(WIA_MICR_INFO) - sizeof(WCHAR), this may add compiler padding for
+ // the data structure. Instead compute the length of the fixed WIA_MICR_INFO size
+ // as 3 * sizeof(DWORD).
+ //
+ ULONG ulFixedSize = 5 * sizeof(DWORD); //not: sizeof(WIA_MICR - sizeof(WIA_MICR_INFO)
+ ULONG ulFixedInfoSize = 3 * sizeof(DWORD); //not: sizeof(WIA_MICR_INFO) - sizeof(WCHAR)
+ ulDataSize = ulFixedSize + (2 * ulFixedInfoSize) + sizeof(szMicrOne) + sizeof(szMicrTwo) - (2 * sizeof(WCHAR));
+
+ const char szSignature[] = "WMIC";
+ memcpy(&micr.Tag, szSignature, sizeof(DWORD));
+
+ micr.Version = 0x00010000;
+ micr.Size = ulDataSize;
+ micr.Count = 2;
+ micr.Placeholder = L'?';
+
+ hr = pStream->Write(&micr, ulFixedSize, NULL);
+ if (SUCCEEDED(hr))
+ {
+ mi.Size = ulFixedInfoSize + sizeof(szMicrOne) - sizeof(WCHAR);
+ mi.Page = 0;
+ mi.Length = (DWORD)wcslen(szMicrOne);
+
+ hr = pStream->Write(&mi, ulFixedInfoSize, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->Write(szMicrOne, sizeof(szMicrOne) - sizeof(WCHAR), NULL);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ mi.Size = ulFixedInfoSize + sizeof(szMicrTwo) - sizeof(WCHAR);
+ mi.Page = 1;
+ mi.Length = (DWORD)wcslen(szMicrTwo);
+
+ hr = pStream->Write(&mi, ulFixedInfoSize, NULL);
+ if (SUCCEEDED(hr))
+ {
+ hr = pStream->Write(szMicrTwo, sizeof(szMicrTwo) - sizeof(WCHAR), NULL);
+ }
+ }
+ }
+ else
+ {
+ hr = E_INVALIDARG;
+ WIAEX_ERROR((g_hInst, "Invalid item - transfer format combination, hr = 0x%08X", hr));
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *pulDataSize = ulDataSize;
+ }
+
+ WIAEX_TRACE_FUNC_HR;
+
+ return hr;
+}
+
+/**************************************************************************\
+*
+* Helper for TransferFile. Computes the transfer completion relative to the
+* specified size of data written and the estimated target file size.
+*
+* Parameters:
+*
+* pulPercentComplete - percent complete value to be updated
+* ulEstimatedFileSize - estimated file size, in bytes (0 if unknown)
+* ulFileBytesWritten - number of bytes of data written
+* bDirectWiaTransfer - TRUE if this is a full WIA transfer when the
+* percent complete must be incremented from 0%
+* to 99%, FALSE if this is a transfer of a file
+* already read from the scanner when the percent
+* complete is set from ulResumeFrom to 99% -or-
+* if this is a transfer from scanner to the driver
+* when the percent complete is set from 0% to 49%.
+* ulResumeFrom - if bDirectWiaTransfer is FALSE this parameter
+* indicates a start value between 0% and 49% for
+* a transfer from the driver to the WIA app -or-
+* 0 to indicate a transfer from the scanner to driver
+* Return Value:
+*
+* None
+*
+\**************************************************************************/
+
+void
+CWiaDriver::ComputeTransferProgress(
+ _Inout_ ULONG *pulPercentComplete,
+ ULONG ulEstimatedFileSize,
+ ULONG ulFileBytesWritten,
+ BOOL bDirectWIATransfer,
+ ULONG ulResumeFrom)
+{
+ ULONG ulStartPos = 0;
+ double dMax = 100.0;
+ ULONG ulLimit = 99;
+
+ if (!bDirectWIATransfer)
+ {
+ if (!ulResumeFrom)
+ {
+ //
+ // If this is an indirect (translated) transfer and the indicated
+ // start position is 0 we will compute the transfer complete on a
+ // 0% to 50% scale up to a maximum value of 49%:
+ //
+ ulStartPos = 0;
+ dMax = 50.0;
+ ulLimit = 49;
+
+ }
+ else
+ {
+ //
+ // If a non-zero value is indicated to start the progress indicator
+ // from we will resume incrementing the transfer complete from here:
+ //
+ if (ulResumeFrom < 50)
+ {
+ ulStartPos = ulResumeFrom + 1;
+ }
+ else
+ {
+ ulStartPos = 50;
+ }
+
+ dMax = 100.0 - (double)ulStartPos;
+ ulLimit = 99 - ulStartPos;
+ }
+ }
+
+ if (pulPercentComplete)
+ {
+ //
+ // Note that we do not know in advance the exact size of the file to be transferred.
+ // In order to allow the WIA client application to see activity (other than the
+ // amount of data that is being transferred with each write) we will use the estimated
+ // uncompressed data size (if available) or simply increment the percent complete until
+ // 99 or end of file transfer (and then set it to 100%):
+ //
+ if (ulEstimatedFileSize > 0)
+ {
+ *pulPercentComplete = (ULONG)(dMax * (((double)ulFileBytesWritten) / ((double)ulEstimatedFileSize)));
+ if ((*pulPercentComplete) > ulLimit)
+ {
+ *pulPercentComplete = ulLimit;
+ }
+ }
+ else
+ {
+ if ((*pulPercentComplete) < ulLimit)
+ {
+ *pulPercentComplete += 1;
+ }
+ }
+
+ if (!bDirectWIATransfer)
+ {
+ *pulPercentComplete += ulStartPos;
+ }
+ }
+}