Sample Code
windows driver samples/ Windows Image Acquisition (WIA) Driver Samples/ C++/ ProdScan/ ScanJobs.cpp/
/************************************************************************** * * 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), MAKEINTRESOURCE(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; } } }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials