diff options
Diffstat (limited to 'wia/ProdScan')
32 files changed, 18145 insertions, 0 deletions
diff --git a/wia/ProdScan/BasicArray.h b/wia/ProdScan/BasicArray.h new file mode 100644 index 00000000..8894dabc --- /dev/null +++ b/wia/ProdScan/BasicArray.h @@ -0,0 +1,381 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: BasicArray.h +* +* Project: Production Scanner Driver Sample +* +* Description: Contains the class declaration of the CBasicDynamicArray class. +* +***************************************************************************/ + +#ifndef __SIMARRAY_H_INCLUDED +#define __SIMARRAY_H_INCLUDED + +template<class T> +class CBasicDynamicArray +{ +private: + + int m_nSize; + int m_nMaxSize; + int m_nGrowSize; + T *m_pArray; + + enum + { + eGrowSize = 10 // The number of items to add each time the array grows. + }; + +public: + + CBasicDynamicArray(void) + : m_nSize(0), + m_nMaxSize(0), + m_nGrowSize(eGrowSize), + m_pArray(NULL) + {} + + CBasicDynamicArray( + int nInitialSize, + int nGrowSize = 0) + : m_nSize(0), + m_nMaxSize(0), + m_nGrowSize(nGrowSize ? nGrowSize : eGrowSize), + m_pArray(NULL) + { + GrowTo(nInitialSize); + } + + CBasicDynamicArray( + const CBasicDynamicArray<T> &other) + : m_nSize(0), + m_nMaxSize(0), + m_nGrowSize(eGrowSize), + m_pArray(NULL) + { + Append(other); + } + + ~CBasicDynamicArray( + void) + { + Destroy(); + } + + CBasicDynamicArray& + operator=( + const CBasicDynamicArray &other) + { + if (this != &other) + { + Destroy(); + Append(other); + } + return *this; + } + + void + Destroy( + void) + { + if (m_pArray) + { + delete[] m_pArray; + m_pArray = NULL; + } + m_nSize = m_nMaxSize = 0; + } + + void + Append( + const CBasicDynamicArray &other) + { + if (GrowTo(m_nSize + other.Size())) + { + for (int i = 0; i < other.Size(); i++) + { + Append(other[i]); + } + } + } + + int + Append( + const T &element) + { + int nResult = -1; + if (GrowTo(m_nSize + 1)) + { + m_pArray[m_nSize] = element; + nResult = m_nSize; + m_nSize++; + } + return nResult; + } + + int + Insert( + const T &element, + int nIndex) + { + // + // Make sure we can accomodate this new item: + // + if (GrowTo(m_nSize + 1)) + { + // + // Make sure the item is within the range we've allocated: + // + if ((nIndex >= 0) && (nIndex <= m_nSize)) + { + // + // Make room for the new item by moving all items above up by one slot: + // + for (int i = Size(); i > nIndex; i--) + { + m_pArray[i] = m_pArray[i-1]; + } + + // + // Save the new item: + // + m_pArray[nIndex] = element; + + // + // We're now one larger: + // + m_nSize++; + + // + // Return the index of the slot we used: + // + return nIndex; + } + } + + // + // Return an error + // + return -1; + } + + void + Delete( + int nItem) + { + if ((nItem >= 0) && (nItem < m_nSize) && (m_pArray)) + { + T *pTmpArray = new T[m_nMaxSize]; + if (pTmpArray) + { + T *pSrc = NULL, *pTgt = NULL; + pSrc = m_pArray; + pTgt = pTmpArray; + + for (int i = 0; i < m_nSize; i++) + { + if (i != nItem) + { + *pTgt = *pSrc; + pTgt++; + } + pSrc++; + } + delete[] m_pArray; + m_pArray = pTmpArray; + m_nSize--; + } + } + } + + bool + GrowTo( + int nSize) + { + // + // If the array is already large enough, just return true: + // + if (nSize < m_nMaxSize) + { + return true; + } + + // + // Save old size, in case we can't allocate a new array: + // + int nOldMaxSize = m_nMaxSize; + + // + // Find the correct size to grow to: + // + while (m_nMaxSize < nSize) + { + m_nMaxSize += m_nGrowSize; + } + + // + // Allocate the array: + // + T *pTmpArray = new T[m_nMaxSize]; + if (pTmpArray) + { + // + // Copy the old array over: + // + for (int i = 0; i < m_nSize; i++) + { + pTmpArray[i] = m_pArray[i]; + } + + // + // Delete the old array: + // + if (m_pArray) + { + delete[] m_pArray; + } + + // + // Assign the new array to the old one and return true: + // + m_pArray = pTmpArray; + return true; + } + else + { + // + // If we couldn't allocate the new array, restore the maximum size and return false: + // + m_nMaxSize = nOldMaxSize; + return false; + } + } + + // + // Simple swap: + // + void + Swap( + T& a, + T& b) + { + T t = a; + a = b; + b = t; + } + + int + Find( + const T& element) const + { + for (int i = 0; i < m_nSize; i++) + { + if (m_pArray[i] == element) + { + return i; + } + } + return -1; + } + + bool + operator==( + const CBasicDynamicArray &other) + { + if (Size() != other.Size()) + { + return false; + } + + for (int i = 0; i < Size(); i++) + { + if (!(m_pArray[i] == other[i])) + { + return false; + } + } + return true; + } + + bool + Contains( + const T& element) + { + return (Find(element) >= 0); + } + + void + Size( + int nSize) + { + m_nSize = nSize; + } + + void + MaxSize( + int nMaxSize) + { + m_nMaxSize = nMaxSize; + } + + void + GrowSize( + int nGrowSize) + { + m_nGrowSize = nGrowSize; + } + + int + Size( + void) const + { + return m_nSize; + } + + int + MaxSize( + void) const + { + return m_nMaxSize; + } + + int + GrowSize( + void) const + { + return m_nGrowSize; + } + + T* + GetBuffer( + int nSize) + { + return GrowTo(nSize) ? m_pArray : NULL; + } + + const T* + Array( + void) const + { + return m_pArray; + } + + const T& + operator[]( + int nIndex) const + { + return m_pArray[nIndex]; + } + + T& + operator[]( + int nIndex) + { + return m_pArray[nIndex]; + } +}; + +#endif + diff --git a/wia/ProdScan/CapMan.cpp b/wia/ProdScan/CapMan.cpp new file mode 100644 index 00000000..9758ad67 --- /dev/null +++ b/wia/ProdScan/CapMan.cpp @@ -0,0 +1,515 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: CapMan.cpp +* +* Project: Production Scanner Driver Sample +* +* Description: Contains the implementation for the CWIACapabilityManager +* class, a helper class for managing WIA capabilities +* +***************************************************************************/ + +#include "stdafx.h" + +/**************************************************************************\ +* +* CWIACapabilityManager constructor +* +\**************************************************************************/ + +CWIACapabilityManager::CWIACapabilityManager() +{ + m_ulEvents = 0; + m_ulCommands = 0; +} + +/**************************************************************************\ +* +* CWIACapabilityManager destructor +* +\**************************************************************************/ + +CWIACapabilityManager::~CWIACapabilityManager() +{ + Destroy(); +} + +/**************************************************************************\ +* +* Parameters: +* +* hInstance - module instance handle used for driver resources +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT +CWIACapabilityManager::Initialize( + _In_ HINSTANCE hInstance) +{ + HRESULT hr = S_OK; + + if (!hInstance) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + m_hInstance = hInstance; + } + + return hr; +} + +/**************************************************************************\ +* +* Parameters: +* +* None +* +* Return Value: +* +* None +* +\**************************************************************************/ + +void +CWIACapabilityManager::Destroy() +{ + INT nCapabilities = m_CapabilityArray.Size(); + + for (INT i = 0; i < nCapabilities; i++) + { + FreeCapability(&m_CapabilityArray[i], TRUE); + } + m_CapabilityArray.Destroy(); + + m_ulEvents = 0; + m_ulCommands = 0; +} + +/**************************************************************************\ +* +* Parameters: +* +* guidCapability - capability identifier +* uiNameResourceID - capability name +* uiDescriptionResourceID - capability description +* ulFlags - capability flags +* wszIcon - capability icon +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT +CWIACapabilityManager::AddCapability( + const GUID guidCapability, + UINT uiNameResourceID, + UINT uiDescriptionResourceID, + ULONG ulFlags, + _In_ LPCWSTR wszIcon) +{ + HRESULT hr = S_OK; + WIA_DEV_CAP_DRV *pWIADeviceCapability = NULL; + WCHAR wCapabilityString[MAX_PATH] = {}; + + if (!wszIcon) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // Allocate memory for the new capability structure: + // + if (SUCCEEDED(hr)) + { + hr = AllocateCapability(&pWIADeviceCapability); + if (SUCCEEDED(hr) && (!pWIADeviceCapability)) + { + hr = E_POINTER; + WIAEX_ERROR((g_hInst, "AllocateCapability failed to return a valid pointer, hr = 0x%08X", hr)); + } + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Cannot allocate memory for capability, hr = 0x%08X", hr)); + } + } + + // + // Copy the capability flags and GUID identifier: + // + if (SUCCEEDED(hr)) + { + pWIADeviceCapability->ulFlags = ulFlags; + *pWIADeviceCapability->guid = guidCapability; + } + + // + // Load and copy the capability name from resources: + // + if (SUCCEEDED(hr)) + { + if (LoadString(m_hInstance, uiNameResourceID, wCapabilityString, ARRAYSIZE(wCapabilityString))) + { + hr = StringCbCopyW(pWIADeviceCapability->wszName, MAX_CAPABILITY_STRING_SIZE_BYTES, wCapabilityString); + if(FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to copy source string (%ws) to destination string, hr = 0x%08X", wCapabilityString, hr)); + } + } + else + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Failed to load the device capability name string %u from resources, hr = 0x%08X", uiNameResourceID, hr)); + } + } + + // + // Load and copy the capability description from resources: + // + if (SUCCEEDED(hr)) + { + memset(wCapabilityString, 0, sizeof(wCapabilityString)); + if (LoadString(m_hInstance, uiDescriptionResourceID, wCapabilityString, ARRAYSIZE(wCapabilityString))) + { + hr = StringCbCopyW(pWIADeviceCapability->wszDescription, MAX_CAPABILITY_STRING_SIZE_BYTES, wCapabilityString); + if(FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to copy source string (%ws) to destination string, hr = 0x%08X", wCapabilityString, hr)); + } + } + else + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Failed to load the device capability description string %u from DLL resource, hr = 0x%08X", + uiDescriptionResourceID, hr)); + } + } + + // + // Copy the icon location: + // + if (SUCCEEDED(hr)) + { + memset(pWIADeviceCapability->wszIcon, 0, MAX_CAPABILITY_STRING_SIZE_BYTES); + + hr = StringCbCopyW(pWIADeviceCapability->wszIcon, MAX_CAPABILITY_STRING_SIZE_BYTES, wszIcon); + if(FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to copy source string (%ws) to destination string, hr = 0x%08X", wszIcon, hr)); + } + } + + // + // Add the new capability item to the array: + // + if(SUCCEEDED(hr)) + { + if ((WIA_NOTIFICATION_EVENT & pWIADeviceCapability->ulFlags) || (WIA_ACTION_EVENT & pWIADeviceCapability->ulFlags)) + { + // + // Event capabilities are inserted at the beginning of the array: + // + m_CapabilityArray.Insert(*pWIADeviceCapability, 0); + + m_ulEvents += 1; + } + else + { + // + // Command capabilities are appended at the end of the array: + // + m_CapabilityArray.Append(*pWIADeviceCapability); + + m_ulCommands += 1; + } + + if ((m_ulEvents + m_ulCommands) != (ULONG)m_CapabilityArray.Size()) + { + WIAEX_ERROR((g_hInst, "Counted %u capabilities (%u events, %u commands), recorded %u..", + m_ulEvents + m_ulCommands, m_ulEvents, m_ulCommands, m_CapabilityArray.Size())); + } + } + + // + // Clean up: + // + + if (pWIADeviceCapability) + { + CoTaskMemFree(pWIADeviceCapability); + pWIADeviceCapability = NULL; + } + + return hr; +} + +/**************************************************************************\ +* +* Parameters: +* +* ppWIADeviceCapability - pointer to capability object to allocate +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT +CWIACapabilityManager::AllocateCapability( + _Out_ WIA_DEV_CAP_DRV **ppWIADeviceCapability) +{ + HRESULT hr = S_OK; + WIA_DEV_CAP_DRV *pWIADeviceCapability = NULL; + + if (!ppWIADeviceCapability) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *ppWIADeviceCapability = NULL; + +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "pWIADeviceCapability is freed with FreeCapability when the call fails") + pWIADeviceCapability = (WIA_DEV_CAP_DRV*)CoTaskMemAlloc(sizeof(WIA_DEV_CAP_DRV)); + if (!pWIADeviceCapability) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for WIA_DEV_CAP_DRV structure, hr = 0x%08X", hr)); + } + else + { + // + // Successfully created WIA_DEV_CAP_DRV, now initialize to 0. + // + memset(pWIADeviceCapability, 0, sizeof(WIA_DEV_CAP_DRV)); + } + } + + if (SUCCEEDED(hr)) + { +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "Freed with FreeCapability when the call fails") + pWIADeviceCapability->guid = (GUID*)CoTaskMemAlloc(sizeof(GUID)); + if (!pWIADeviceCapability->guid) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for GUID member of WIA_DEV_CAP_DRV structure, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "Freed with FreeCapability when the call fails") + pWIADeviceCapability->wszName = (LPOLESTR)CoTaskMemAlloc(MAX_CAPABILITY_STRING_SIZE_BYTES); + if (!pWIADeviceCapability->wszName) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for LPOLESTR (wszName) member of WIA_DEV_CAP_DRV structure, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "Freed with FreeCapability when the call fails") + pWIADeviceCapability->wszDescription = (LPOLESTR)CoTaskMemAlloc(MAX_CAPABILITY_STRING_SIZE_BYTES); + if (!pWIADeviceCapability->wszDescription) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for LPOLESTR (wszDescription) member of WIA_DEV_CAP_DRV structure, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "Freed with FreeCapability when the call fails") + pWIADeviceCapability->wszIcon = (LPOLESTR)CoTaskMemAlloc(MAX_CAPABILITY_STRING_SIZE_BYTES); + if (!pWIADeviceCapability->wszIcon) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for LPOLESTR (wszIcon) member of WIA_DEV_CAP_DRV structure, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + *pWIADeviceCapability->guid = GUID_NULL; + memset(pWIADeviceCapability->wszName, 0, MAX_CAPABILITY_STRING_SIZE_BYTES); + memset(pWIADeviceCapability->wszDescription, 0, MAX_CAPABILITY_STRING_SIZE_BYTES); + memset(pWIADeviceCapability->wszIcon, 0, MAX_CAPABILITY_STRING_SIZE_BYTES); + + *ppWIADeviceCapability = pWIADeviceCapability; + } + else if (pWIADeviceCapability) + { + FreeCapability(pWIADeviceCapability); + pWIADeviceCapability = NULL; + } + + return hr; +} + +/**************************************************************************\ +* +* Parameters: +* +* pWIADeviceCapability - pointer to capability object to free +* bFreeCapabilityContentOnly - TRUE to free only the capability content +* or FALSE to free the entire object instance +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +void +CWIACapabilityManager::FreeCapability( + _In_ WIA_DEV_CAP_DRV *pWIADeviceCapability, + BOOL bFreeCapabilityContentOnly) +{ + if (pWIADeviceCapability) + { + if (pWIADeviceCapability->guid) + { + CoTaskMemFree(pWIADeviceCapability->guid); + pWIADeviceCapability->guid = NULL; + } + + if (pWIADeviceCapability->wszName) + { + CoTaskMemFree(pWIADeviceCapability->wszName); + pWIADeviceCapability->wszName = NULL; + } + + if (pWIADeviceCapability->wszDescription) + { + CoTaskMemFree(pWIADeviceCapability->wszDescription); + pWIADeviceCapability->wszDescription = NULL; + } + + if (pWIADeviceCapability->wszIcon) + { + CoTaskMemFree(pWIADeviceCapability->wszIcon); + pWIADeviceCapability->wszIcon = NULL; + } + + if (!bFreeCapabilityContentOnly) + { + CoTaskMemFree(pWIADeviceCapability); + } + } + else + { + WIAEX_ERROR((g_hInst, "Invalid parameter, caller attempted to free a NULL WIA_DEV_CAP_DRV structure")); + } +} + +/**************************************************************************\ +* +* Parameters: +* +* None +* +* Return Value: +* +* Pointer to the capability manager's list of capabilities +* +\**************************************************************************/ + +WIA_DEV_CAP_DRV* +CWIACapabilityManager::GetCapabilities() +{ + WIA_DEV_CAP_DRV* pCapabilities = NULL; + + if ((!m_ulEvents) && (!m_ulCommands)) + { + WIAEX_ERROR((g_hInst, "No capabilities to return")); + pCapabilities = NULL; + } + else + { + pCapabilities = &m_CapabilityArray[0]; + } + + return pCapabilities; +} + +/**************************************************************************\ +* +* Parameters: +* +* None +* +* Return Value: +* +* Pointer to the capability manager's list of command capabilities +* +\**************************************************************************/ + +WIA_DEV_CAP_DRV* +CWIACapabilityManager::GetCommands() +{ + WIA_DEV_CAP_DRV* pCommands = NULL; + + if (!m_ulCommands) + { + WIAEX_ERROR((g_hInst, "No commands to return")); + pCommands = NULL; + } + else + { + // + // Command capabilities are stored at the end of the capability list, after the event capabilities: + // + pCommands = &m_CapabilityArray[m_ulEvents]; + } + + return pCommands; +} + +/**************************************************************************\ +* +* Parameters: +* +* None +* +* Return Value: +* +* Pointer to the capability manager's list of event capabilities +* +\**************************************************************************/ + +WIA_DEV_CAP_DRV* +CWIACapabilityManager::GetEvents() +{ + WIA_DEV_CAP_DRV* pEvents = NULL; + + if (!m_ulEvents) + { + WIAEX_ERROR((g_hInst, "No events to return")); + pEvents = NULL; + } + else + { + // + // Event capabilities are stored at the beginning of the capability list: + // + pEvents = &m_CapabilityArray[0]; + } + + return pEvents; +} diff --git a/wia/ProdScan/CapMan.h b/wia/ProdScan/CapMan.h new file mode 100644 index 00000000..5147cfef --- /dev/null +++ b/wia/ProdScan/CapMan.h @@ -0,0 +1,81 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: CapMan.h +* +* Project: Production Scanner Driver Sample +* +* Description: Contains the class declaration for CWIACapabilityManager +* +***************************************************************************/ + +#pragma once + +#define MAX_CAPABILITY_STRING_SIZE_BYTES (sizeof(WCHAR) * MAX_PATH) + +class CWIACapabilityManager +{ +public: + CWIACapabilityManager(); + ~CWIACapabilityManager(); + +public: + HRESULT + Initialize( + _In_ HINSTANCE hInstance); + + void + Destroy(); + + HRESULT + AddCapability( + const GUID guidCapability, + UINT uiNameResourceID, + UINT uiDescriptionResourceID, + ULONG ulFlags, + _In_ LPCWSTR wszIcon); + + HRESULT + AllocateCapability( + _Out_ WIA_DEV_CAP_DRV **ppWIADeviceCapability); + + void + FreeCapability( + _In_ WIA_DEV_CAP_DRV *pWIADeviceCapability, + BOOL bFreeCapabilityContentOnly = FALSE); + + WIA_DEV_CAP_DRV* + GetCapabilities(); + + WIA_DEV_CAP_DRV* + GetCommands(); + + WIA_DEV_CAP_DRV* + GetEvents(); + + ULONG + GetNumCapabilities() + { + return (m_ulEvents + m_ulCommands); + } + + ULONG + GetNumEvents() + { + return m_ulEvents; + } + + ULONG + GetNumCommands() + { + return m_ulCommands; + } + +private: + HINSTANCE m_hInstance; + CBasicDynamicArray<WIA_DEV_CAP_DRV> m_CapabilityArray; + + ULONG m_ulEvents; + ULONG m_ulCommands; +}; diff --git a/wia/ProdScan/Constants.h b/wia/ProdScan/Constants.h new file mode 100644 index 00000000..1ef3ebb7 --- /dev/null +++ b/wia/ProdScan/Constants.h @@ -0,0 +1,230 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: Constants.h +* +* Project: Production Scanner Driver Sample +* +* Description: Contains declaration for macro definitions and constant +* variables used internally by the Production Scanner Driver Sample. +* Character string constants declared here represent names +* that should not be localized (i.e. put into resources) +* +***************************************************************************/ + +#pragma once + +// +// Minimum and maximum scan area dimensions are hard-coded for this sample driver +// to a size equivalent with a US Letter page size (8.5" x 11"). Note the minimum +// sizes are configured to the same sizes as maximums, reason for this being that +// the sample driver does not crop the test image to transfer. However, for property +// negotiation only the minimum sizes can be changed to lower values, just that +// the driver will ignore a smaller crop frame when scanning. A real driver should +// not do this and must always configure the real scan area dimensions, min and max: +// +#define MAX_SCAN_AREA_WIDTH 8500 +#define MAX_SCAN_AREA_HEIGHT 11000 +#define MIN_SCAN_AREA_WIDTH MAX_SCAN_AREA_WIDTH +#define MIN_SCAN_AREA_HEIGHT MAX_SCAN_AREA_HEIGHT + +// +// Minimum and maximum imprinter area dimensions are hard-coded as well for this +// sample driver. These dimensions match the sample test imprinter image size: +// +#define IMPRINTER_MAX_WIDTH 640 +#define IMPRINTER_MAX_HEIGHT 480 +#define IMPRINTER_MIN_WIDTH IMPRINTER_MAX_WIDTH +#define IMPRINTER_MIN_HEIGHT IMPRINTER_MAX_HEIGHT + +// +// This sample driver does not support landscape orientation: +// +#define LANDSCAPE_SUPPOPRTED 0 + +// +// The optical/native and only scan resolution for the sample driver is 300 DPI: +// +#define OPTICAL_RESOLUTION 300 + +// +// When scanning from the Feeder with WIA_IPS_PAGES set to ALL_PAGES or from the Auto +// source (when the the WIA_IPS_PAGES is not accessible) this sample driver has +// a hard-coded limit of the number of pages to 'scan' and transfer: +// +#define MAX_SCAN_PAGES 200 + +// +// The following constants define the number of pages "scanned" when the sample +// driver simulates that it detects a job separator page or a multi-feed error +// when job separators and/or multi-feed detection is enabled: +// +#define JOB_SEPARATOR_AT_PAGE 3 +#define MULTI_FEED_AT_PAGE 5 + +// +// WIA Item Tree item names (hard-coded): +// +#define WIA_DRIVER_ROOT_NAME L"Root" +#define WIA_DRIVER_FLATBED_NAME L"Flatbed" +#define WIA_DRIVER_FEEDER_NAME L"Feeder" +#define WIA_DRIVER_AUTO_NAME L"Auto" +#define WIA_DRIVER_IMPRINTER_NAME L"Imprinter" +#define WIA_DRIVER_ENDORSER_NAME L"Endorser" +#define WIA_DRIVER_BARCODE_READER_NAME L"Barcode Reader" +#define WIA_DRIVER_PATCH_CODE_READER_NAME L"Patch Code Reader" +#define WIA_DRIVER_MICR_READER_NAME L"MICR Reader" + +// +// File name extension constants used for WIA_IPA_FILENAME_EXTENSION: +// +#define FILE_EXT_BMP L"BMP" +#define FILE_EXT_JPG L"JPG" +#define FILE_EXT_RAW L"RAW" +#define FILE_EXT_CSV L"CSV" +#define FILE_EXT_TXT L"TXT" +#define FILE_EXT_XML L"XML" + +// +// Valid characters for the pretended imprinter and endorser units for this sample driver: +// +#define SAMPLE_IMPRINTER_VALID_CHARS L"0123456789abcdefghijklmnoprstuvwxzyABCDEFGHIJKLMNOPRSTUVWXZY$~!@#$*%^(){}[]|+-=<>.?_: ," +#define SAMPLE_ENDORSER_VALID_CHARS L"0123456789ademnloprSstx ," + +// +// Byte Order Mark (BOM) for the imprinter/endorser text when packaged in WiaImgFmt_CSV and +// WiaImgFmt_TXT files. This text must be encoded UTF-16 little-endian byte order, double-byte +// fixed size characters only (no surrogate pairs), and must be prefixed with this BOM: +// +const BYTE g_bBOM[] = +{ + 0xFF, + 0xFE +}; + +// +// Valid barcode types claimed for the barcode reader implemented by this sample driver: +// +const LONG g_lSupportedBarcodeTypes[] = +{ + WIA_BARCODE_UPCA, + WIA_BARCODE_UPCE, + WIA_BARCODE_CODABAR, + WIA_BARCODE_NONINTERLEAVED_2OF5, + WIA_BARCODE_INTERLEAVED_2OF5, + WIA_BARCODE_CODE39, + WIA_BARCODE_CODE39_MOD43, + WIA_BARCODE_CODE39_FULLASCII, + WIA_BARCODE_CODE93, + WIA_BARCODE_CODE128, + WIA_BARCODE_CODE128A, + WIA_BARCODE_CODE128B, + WIA_BARCODE_CODE128C, + WIA_BARCODE_GS1128, + WIA_BARCODE_GS1DATABAR, + WIA_BARCODE_ITF14, + WIA_BARCODE_EAN8, + WIA_BARCODE_EAN13, + WIA_BARCODE_POSTNETA, + WIA_BARCODE_POSTNETB, + WIA_BARCODE_POSTNETC, + WIA_BARCODE_POSTNET_DPBC, + WIA_BARCODE_PLANET, + WIA_BARCODE_INTELLIGENT_MAIL, + WIA_BARCODE_POSTBAR, + WIA_BARCODE_RM4SCC, + WIA_BARCODE_HIGH_CAPACITY_COLOR, + WIA_BARCODE_MAXICODE, + WIA_BARCODE_PDF417 +}; + +// +// Valid patch code types claimed for the patch code reader implemented by this sample driver: +// +const LONG g_lSupportedPatchCodeTypes[] = +{ + WIA_PATCH_CODE_1, + WIA_PATCH_CODE_2, + WIA_PATCH_CODE_3, + WIA_PATCH_CODE_4, + WIA_PATCH_CODE_6, + WIA_PATCH_CODE_T +}; + +// +// Constant table of standard WIA document sizes, in Portrait orientation. +// Dimensions are defined in 1/1000ths of an inch. The table is included +// for use in a modified driver based on this sample, the sample driver +// in its current form supporting from this list only WIA_PAGE_LETTER: +// + +typedef struct _WIA_PAGE_SIZE_COMBINATION +{ + LONG m_lPageSize; + LONG m_lPageWidth; + LONG m_lPageHeight; +} WIA_PAGE_SIZE_COMBINATION, *PWIA_WIA_PAGE_SIZE_COMBINATION; + +const WIA_PAGE_SIZE_COMBINATION g_DefinedPageSizeCombinations[] = +{ + { WIA_PAGE_A4, 8267, 11692 }, + { WIA_PAGE_LETTER, 8500, 11000 }, + { WIA_PAGE_USLEGAL, 8500, 14000 }, + { WIA_PAGE_USLEDGER, 11000, 17000 }, + { WIA_PAGE_USSTATEMENT, 5500, 8500 }, + { WIA_PAGE_BUSINESSCARD, 3543, 2165 }, + { WIA_PAGE_ISO_A0, 33110, 46811 }, + { WIA_PAGE_ISO_A1, 23385, 33110 }, + { WIA_PAGE_ISO_A2, 16535, 23385 }, + { WIA_PAGE_ISO_A3, 11692, 16535 }, + { WIA_PAGE_ISO_A5, 5826, 8267 }, + { WIA_PAGE_ISO_A6, 4133, 5826 }, + { WIA_PAGE_ISO_A7, 2913, 4133 }, + { WIA_PAGE_ISO_A8, 2047, 2913 }, + { WIA_PAGE_ISO_A9, 1456, 2047 }, + { WIA_PAGE_ISO_A10, 1023, 1456 }, + { WIA_PAGE_ISO_B0, 39370, 55669 }, + { WIA_PAGE_ISO_B1, 27834, 39370 }, + { WIA_PAGE_ISO_B2, 19685, 27834 }, + { WIA_PAGE_ISO_B3, 13897, 19685 }, + { WIA_PAGE_ISO_B4, 9842, 13897 }, + { WIA_PAGE_ISO_B5, 6929, 9842 }, + { WIA_PAGE_ISO_B6, 4921, 6929 }, + { WIA_PAGE_ISO_B7, 3464, 4921 }, + { WIA_PAGE_ISO_B8, 2440, 3464 }, + { WIA_PAGE_ISO_B9, 1732, 2440 }, + { WIA_PAGE_ISO_B10, 1220, 1732 }, + { WIA_PAGE_ISO_C0, 36102, 51062 }, + { WIA_PAGE_ISO_C1, 25511, 36102 }, + { WIA_PAGE_ISO_C2, 18031, 25511 }, + { WIA_PAGE_ISO_C3, 12755, 18031 }, + { WIA_PAGE_ISO_C4, 9015, 12755 }, + { WIA_PAGE_ISO_C5, 6377, 9015 }, + { WIA_PAGE_ISO_C6, 4488, 6377 }, + { WIA_PAGE_ISO_C7, 3188, 4488 }, + { WIA_PAGE_ISO_C8, 2244, 3188 }, + { WIA_PAGE_ISO_C9, 1574, 2244 }, + { WIA_PAGE_ISO_C10, 1102, 1574 }, + { WIA_PAGE_JIS_B0, 40551, 57322 }, + { WIA_PAGE_JIS_B1, 28661, 40551 }, + { WIA_PAGE_JIS_B2, 20275, 28661 }, + { WIA_PAGE_JIS_B3, 14330, 20275 }, + { WIA_PAGE_JIS_B4, 10118, 14330 }, + { WIA_PAGE_JIS_B5, 7165, 10118 }, + { WIA_PAGE_JIS_B6, 5039, 7165 }, + { WIA_PAGE_JIS_B7, 3582, 5039 }, + { WIA_PAGE_JIS_B8, 2519, 3582 }, + { WIA_PAGE_JIS_B9, 1771, 2519 }, + { WIA_PAGE_JIS_B10, 1259, 1771 }, + { WIA_PAGE_JIS_2A, 46811, 66220 }, + { WIA_PAGE_JIS_4A, 66220, 93622 }, + { WIA_PAGE_DIN_2B, 55669, 78740 }, + { WIA_PAGE_DIN_4B, 78740, 111338 } +}; + +// +// Maximum number of colors for the color dropout feature implemented by this sample driver: +// +const LONG g_lMaxDropColors = 3; +const LONG g_lDefaultDropColors[] = { 0, 0, 0 }; diff --git a/wia/ProdScan/Events.cpp b/wia/ProdScan/Events.cpp new file mode 100644 index 00000000..0960239b --- /dev/null +++ b/wia/ProdScan/Events.cpp @@ -0,0 +1,180 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* Title: Events.cpp +* +* Description: This file contains the IStiUSD methods used for WIA/STI +* events and helper CWiaDriver methods which are event related. +* +***************************************************************************/ + +#include "stdafx.h" + +/**************************************************************************\ +* +* Implements IStiUSD::SetNotificationHandle. Specifies an event handle +* that the driver should use to inform the caller of device events. +* Typically called by the WIA service (the Still Image Event Monitor). +* Also called by the driver itself to control the WIA event mechanism. +* +* The WIA service will pass in a valid handle (created using CreateEvent()), +* indicating that it wants the WIA driver to signal this handle when an +* event occurs in the hardware. +* +* NULL can be passed to this SetNotificationHandle() method. NULL indicates +* that the WIA driver is to STOP all device activity, and exit any event +* wait operations. +* +* Parameters: +* +* hEvent - HANDLE to an event created by the WIA service using CreateEvent() +* This parameter can be NULL, indicating that all previous event +* waiting should be stopped. +* +* Return Value: +* +* If the operation succeeds, the method should return S_OK. +* Otherwise, it should return one of the STIERR-prefixed error +* codes defined in stierr.h. +* +\**************************************************************************/ +HRESULT CWiaDriver::SetNotificationHandle( + _In_opt_ HANDLE hEvent) +{ + if ((hEvent) && (INVALID_HANDLE_VALUE != hEvent)) + { + // + // Enable STI/WIA scan events for this driver: + // + + // + // This event is created and owned by the caller, which may be + // either this driver itself or the WIA service. The owner must + // close the event handle that it owns when calling this method + // with a different parameter. If we attempt to close the event + // handle here we cause an exception on checked builds. In the + // extreme case that the event is not closed by its owner it + // must be closed by the system when the process (in this case + // the WIA service process) terminates. + // + // if (m_hWiaEvent) + // { + // CloseHandle(m_hWiaEvent); + // m_hWiaEvent = NULL; + // } + // + m_hWiaEvent = hEvent; + + // + // Refresh also the backup copy of the event handle: + // + m_hWiaEventStoredCopy = m_hWiaEvent; + } + else + { + // + // Disable STI/WIA scan events for this driver - reset + // the event handle but keep its backup copy, if any + // + m_hWiaEvent = NULL; + + } + + return S_OK; +} + +/**************************************************************************\ +* +* Implements IStiUSD::GetNotificationData. Returns a description of the most +* recent event that occurred on the still image device. If no events have +* occurred since the last time the method was called, the method should return +* STIERR_NOEVENTS. If an event occurred, the driver should return the GUID +* associated with it (STINOTIFY::guidNotificationCode). +* +* GetNotificationData is called both for polled events and interrupt events +* (this driver supports only interrupt events): +* +* 1. [POLLING EVENTS] IStiUSD::GetStatus() reported that there was an event +* pending, by setting the STI_EVENTHANDLING_PENDING flag in the STI_DEVICE_STATUS +* structure. Polling events mechanism is not supported by this driver. +* +* 2. [INTERRUPT EVENTS] The hEvent handle passed in by IStiUSD::SetNotificationHandle() +* was signaled by the hardware, or by calling SetEvent() directly. This is the +* WIA event mechanism this driver supports. +* +* The driver is responsible for filling out the STINOTIFY structure members: +* +* dwSize - size of the STINOTIFY structure. +* +* guidNotificationCode - GUID that represents the event that is to be responded to. +* This should be set to GUID_NULL if no event is to be sent. This will tell the +* WIA service that no event really happened. +* +* abNotificationData - OPTIONAL - vendor specific information. This data is limited +* to 64 bytes of data ONLY. Not used by this driver. +* +* Parameters: +* +* hEvent - caller-supplied handle to a Win32 event, created by calling CreateEvent +* +* Return Value: +* +* If the operation succeeds, the method should return S_OK if there is an event +* signaled or STIERR_NOEVENTS if there are no events. If an error occurrs +* it should return one of the other STIERR-prefixed error codes defined in stierr.h. +* +\**************************************************************************/ +HRESULT CWiaDriver::GetNotificationData( + _Out_ LPSTINOTIFY lpNotify) +{ + HRESULT hr = S_OK; + + if (!lpNotify) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X",hr)); + } + + // + // This sample driver does not signal WIA scan events so we'll return here STIERR_NOEVENTS: + // + if (SUCCEEDED(hr)) + { + hr = STIERR_NOEVENTS; + } + + // + // A real driver would check if the Hardware device signaled a device event + // and if so, fill in the lpNotify response data as following: + // + // if (SUCCEEDED(hr)) + // { + // memset(lpNotify, 0, sizeof(STINOTIFY)); + // lpNotify->dwSize = sizeof(STINOTIFY); + // lpNotify->guidNotificationCode = guidEvent; + // } + // + // where guidEvent would be one of the following: + // + // WIA_EVENT_SCAN_IMAGE + // WIA_EVENT_DEVICE_NOT_READY + // WIA_EVENT_DEVICE_READY + // WIA_EVENT_FLATBED_LID_OPEN + // WIA_EVENT_FLATBED_LID_CLOSED + // WIA_EVENT_FEEDER_LOADED + // WIA_EVENT_FEEDER_EMPTIED + // WIA_EVENT_COVER_OPEN + // WIA_EVENT_COVER_CLOSED + // + // or any other WIA_NOTIFICATION_EVENT and/or WIA_ACTION_EVENT reported to + // IWiaMiniDrv::drvGetCapabilities (see CWiaDriver::drvGetCapabilities). + // + + if (FAILED(hr) && (STIERR_NOEVENTS != hr)) + { + m_hrLastEdviceError = hr; + } + + return hr; +} diff --git a/wia/ProdScan/FileConv.cpp b/wia/ProdScan/FileConv.cpp new file mode 100644 index 00000000..5e3091d2 --- /dev/null +++ b/wia/ProdScan/FileConv.cpp @@ -0,0 +1,730 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* Title: FileConv.cpp +* +* Description: This file contains implementation of utility functions for +* image file format conversions used by the sample driver. +* +***************************************************************************/ + +#include "stdafx.h" + +// +// The GDI+ codec name used for the DIB file format: +// +#define GDIPLUS_BMP_ENCODER L"image/bmp" + +/**************************************************************************\ +* +* Inline function to convert a GDI+ result code to a COM HRESULT, +* similar to the HRESULT_FOM_WIN32 macro. +* +* Parameters: +* +* status - GDI+ return code value +* +* Return Value: +* +* HRESULT describing the GDI+ status, E_FAIL if conversion is possible +* +\**************************************************************************/ + +inline HRESULT +GDISTATUS_TO_HRESULT( + Gdiplus::Status status) +{ + HRESULT hr = E_FAIL; + DWORD dwErr = NO_ERROR; + + switch (status) + { + case Gdiplus::Ok: + hr = S_OK; + break; + + case Gdiplus::InvalidParameter: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::InvalidParameter")); + hr = E_INVALIDARG; + break; + + case Gdiplus::OutOfMemory: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::OutOfMemory")); + hr = E_OUTOFMEMORY; + break; + + case Gdiplus::InsufficientBuffer: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::InsufficientBuffer")); + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + break; + + case Gdiplus::Aborted: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::Aborted")); + hr = E_ABORT; + break; + + case Gdiplus::ObjectBusy: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::ObjectBusy")); + hr = E_PENDING; + break; + + case Gdiplus::FileNotFound: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::FileNotFound")); + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + break; + + case Gdiplus::AccessDenied: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::AccessDenied")); + hr = E_ACCESSDENIED; + break; + + case Gdiplus::UnknownImageFormat: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::UnknownImageFormat")); + hr = HRESULT_FROM_WIN32(ERROR_INVALID_PIXEL_FORMAT); + break; + + case Gdiplus::NotImplemented: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::NotImplemented")); + hr = E_NOTIMPL; + break; + + case Gdiplus::Win32Error: + dwErr = GetLastError(); + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::Win32Error, last error: 0x%08X", dwErr)); + hr = HRESULT_FROM_WIN32(dwErr); + break; + + case Gdiplus::ValueOverflow: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::ValueOverflow")); + hr = E_FAIL; + break; + + case Gdiplus::FontFamilyNotFound: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::FontFamilyNotFound")); + hr = E_FAIL; + break; + + case Gdiplus::FontStyleNotFound: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::FontFamilyNotFound")); + hr = E_FAIL; + break; + + case Gdiplus::NotTrueTypeFont: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::NotTrueTypeFont")); + hr = E_FAIL; + break; + + case Gdiplus::UnsupportedGdiplusVersion: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::UnsupportedGdiplusVersion")); + hr = E_FAIL; + break; + + case Gdiplus::GdiplusNotInitialized: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::GdiplusNotInitialized")); + hr = E_FAIL; + break; + + case Gdiplus::WrongState: + WIAEX_ERROR((g_hInst, "GDI+: Gdiplus::WrongState")); + hr = E_FAIL; + break; + + default: + WIAEX_ERROR((g_hInst, "GDI+: unknown Gdiplus status code (%u)", (ULONG)status)); + hr = E_FAIL; + } + + return hr; +} + +/**************************************************************************\ +* +* Executes GdiplusStartup to initialize the GDI+ engine. Must be called +* before calling ConvertImageToDIB. A matching ShutdownGDIPlus call +* must be always made to stop the GDI+ engine started by this function. +* +* Parameters: +* +* ppToken - token returned by GDI+, must be used when calling ShutdownGDIPlus +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT +InitializeGDIPlus( + _Out_ ULONG_PTR *ppToken) +{ + HRESULT hr = S_OK; + GdiplusStartupInput gdiplusStartupInput; + + if (!ppToken) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // Start the GDI+ engine: + // + if (SUCCEEDED(hr)) + { + hr = GDISTATUS_TO_HRESULT(GdiplusStartup(ppToken, &gdiplusStartupInput, NULL)); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Gdiplus::GdiplusStartup failed, hr = 0x%08X", hr)); + } + } + + return hr; +} + +/**************************************************************************\ +* +* Executes GdiplusShutdown to shutdown the GDI+ engine. +* +* Parameters: +* +* pToken - token obtained from a previous InitializeGDIPlus call +* +* Return Value: +* +* E_INVALIDARG if called with a NULL pToken parameter or S_OK +* (GDI+ does not return a result code for GdiplusShutdown) +* +\**************************************************************************/ + +HRESULT +ShutdownGDIPlus( + _In_ ULONG_PTR pToken) +{ + HRESULT hr = S_OK; + + if (!pToken) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // Shutdown the GDI+ engine: + // + if (SUCCEEDED(hr)) + { + GdiplusShutdown(pToken); + } + + return hr; +} + +/**************************************************************************\ +* +* Internal helper for ConvertImageToDIB. Enumerates available GDI+ image +* format encoders and returns the CLSID of the specified encoder, if available. +* +* Parameters: +* +* wszFormat - GDI+ format name, e.g. "image/bmp" for DIB +* pClisid - returns the CLSID of the GDI+ encoder +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT +_GetEncoderCLSID( + _In_ LPCWSTR wszFormat, + _Out_ CLSID *pClsid) +{ + HRESULT hr = S_OK; + UINT nNumEncoders = 0; + UINT cbEncoderSize = 0; + ImageCodecInfo *pImageCodecInfo = NULL; + + if ((!wszFormat) || (!pClsid)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + hr = GDISTATUS_TO_HRESULT(GetImageEncodersSize(&nNumEncoders, &cbEncoderSize)); + if (SUCCEEDED(hr) && (!cbEncoderSize)) + { + hr = E_FAIL; + } + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Gdiplus::GetImageEncodersSize(%ws) failed, hr = 0x%08X", wszFormat, hr)); + } + } + + if (SUCCEEDED(hr)) + { + pImageCodecInfo = (ImageCodecInfo*)new BYTE[cbEncoderSize]; + if (!pImageCodecInfo) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for the encoder info, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = GDISTATUS_TO_HRESULT(GetImageEncoders(nNumEncoders, cbEncoderSize, pImageCodecInfo)); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Gdiplus::GetImageEncoders(%u encoders) failed, hr = 0x%08X", nNumEncoders, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = E_FAIL; + + for (UINT j = 0; j < nNumEncoders; ++j) + { + _Analysis_assume_(cbEncoderSize >= nNumEncoders * sizeof(ImageCodecInfo)); + _Analysis_assume_(wcslen(wszFormat) < (cbEncoderSize / sizeof(WCHAR))); + + if (!wcscmp(pImageCodecInfo[j].MimeType, wszFormat)) + { + *pClsid = pImageCodecInfo[j].Clsid; + hr = S_OK; + break; + } + } + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "No %ws encoder found in %u available GDI+ encoders, hr = 0x%08X", wszFormat, nNumEncoders, hr)); + } + } + + if (pImageCodecInfo) + { + delete[] pImageCodecInfo; + } + + return hr; +} + +/**************************************************************************\ +* +* Converts a GDI+ compatible image to a Windows DIB. +* +* Parameters: +* +* pInputImage - GDI+ Image object containing the image to be converted +* ppOutputStream - returns a new global memory IStream containing the +* converted DIB image file (must be released by caller) +* plImageWidth - (optional) returns the width of the image, in pixels +* plImageHeight - (optional) returns the height of the image, in pixels +* plOutputImageBPL - (optional) returns the estimated number of bytes +* per line for the converted DIB image +* +* Remarks: +* +* The caller is responsible to release the returned IStream object to +* free the memory after successful execution of this function. +* +* The caller must execute InitializeGDIPlus before calling this function. +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT +ConvertImageToDIB( + _In_ Image *pInputImage, + _Outptr_ IStream **ppOutputStream, + _Out_opt_ LONG *plImageWidth, + _Out_opt_ LONG *plImageHeight, + _Out_opt_ LONG *plOutputImageBPL) +{ + HRESULT hr = S_OK; + CLSID clsidBmpEncoder = {}; + LONG lImageWidth = 0; + LONG lImageHeight = 0; + LONG lOutputBPL = 0; + LONG lBitDepth = 0; + + WIAEX_TRACE_BEGIN; + + if ((!pInputImage) || (!ppOutputStream)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // Read the dimensions of the input image: + // + if (SUCCEEDED(hr)) + { + *ppOutputStream = NULL; + + lImageWidth = pInputImage->GetWidth(); + lImageHeight = pInputImage->GetHeight(); + lBitDepth = GetPixelFormatSize(pInputImage->GetPixelFormat()); + + if ((lImageWidth > 0) && (lImageHeight > 0) && (lBitDepth > 0)) + { + lOutputBPL = BytesPerLine(lImageWidth, lBitDepth); + + WIAS_TRACE((g_hInst, "Input image is %u x %u pixels, %u bpp, %u BPL (estimated) on output", + lImageWidth, lImageHeight, lBitDepth, lOutputBPL)); + } + else + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Incorrect image dimensions reported by GDI+ (%d x %d pixels, %d bpp), hr = 0x%08X", + lImageWidth, lImageHeight, lBitDepth, hr)); + } + } + + // + // Retrieve the GDI+ encoder necessary to convert the input image to a DIB: + // + if (SUCCEEDED(hr)) + { + hr = _GetEncoderCLSID(GDIPLUS_BMP_ENCODER, &clsidBmpEncoder); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "The GDI+ %ws encoder appears to be missing or improperly installed, hr = 0x%08X", + GDIPLUS_BMP_ENCODER, hr)); + } + } + + // + // Create the output stream in global memory: + // + if (SUCCEEDED(hr)) + { + hr = CreateStreamOnHGlobal(NULL, TRUE, ppOutputStream); + if (SUCCEEDED(hr) && (!(*ppOutputStream))) + { + hr = E_FAIL; + } + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CreateStreamOnHGlobal failed, hr = 0x%08X", hr)); + } + } + + // + // Convert and save the image stored in the Image object to the output stream as a DIB: + // + if (SUCCEEDED(hr)) + { + hr = GDISTATUS_TO_HRESULT(pInputImage->Save(*ppOutputStream, &clsidBmpEncoder)); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Image::Save(%ws) failed, hr = 0x%08X", GDIPLUS_BMP_ENCODER, hr)); + } + } + + if (FAILED(hr)) + { + if (ppOutputStream && *ppOutputStream) + { + (*ppOutputStream)->Release(); + *ppOutputStream = NULL; + } + } + + if (SUCCEEDED(hr)) + { + if (plImageWidth) + { + *plImageWidth = lImageWidth; + } + if (plImageHeight) + { + *plImageHeight = lImageHeight; + } + if (plOutputImageBPL) + { + *plOutputImageBPL = lOutputBPL; + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Converts a 8-bpp grayscale or 24-bpp RGB color DIB to a WIA Raw image. +* +* Parameters: +* +* ppInputStream - input stream containing the DIB to be converted +* ppOutputStream - returns a new global memory IStream containing the +* converted Raw image file (must be released by caller) +* +* Remarks: +* +* The current form of this function for simplicty supports only 8-bpp +* Grayscale and 24-bpp RGB color images. Palettes are not supported. +* +* The caller is responsible to release the returned IStream object to +* free the memory after successful execution of this function. +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT +ConvertDibToRaw( + _In_ IStream *pInputStream, + _Out_ IStream **ppOutputStream) +{ + HRESULT hr = S_OK; + LARGE_INTEGER liOffset = {}; + WIA_RAW_HEADER wiaRawHeader = {}; + BYTE bGrayPalette[256] = {}; + BITMAPINFOHEADER bih = {}; + ULONG ulDataSize = 0; + ULONG ulDataWritten = 0; + ULONG ulPaletteSize = 0; + + WIAEX_TRACE_BEGIN; + + if ((!pInputStream) || (!ppOutputStream)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08.8X", hr)); + } + else + { + *ppOutputStream = NULL; + } + + liOffset.LowPart = sizeof(BITMAPFILEHEADER); + hr = pInputStream->Seek(liOffset, STREAM_SEEK_SET, NULL); + if (S_OK != hr) + { + WIAEX_ERROR((g_hInst, "IStream::Seek(%u, STREAM_SEEK_SET, NULL) failed, hr = 0x%08X", liOffset.LowPart, hr)); + } + + if (S_OK == hr) + { + // + // Extract the DIB header from the input stream: + // + hr = pInputStream->Read((void *)&bih, sizeof(bih), &ulDataSize); + if ((S_OK == hr) && (ulDataSize != sizeof(bih))) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Expected to read %u bytes for the DIB header, got %u bytes, hr = 0x%08X", + sizeof(bih), ulDataSize, hr)); + } + + // + // Validate the input DIB and fill in the output Raw header: + // + if (S_OK == hr) + { + if ((bih.biBitCount != 24) && (bih.biBitCount != 8)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Test image must be either 8-bpp Grayscale or 24-bpp RGB Color, hr = 0x%08X", + sizeof(bih), ulDataSize, hr)); + } + else + { + const char szRawSignature[] = "WRAW"; + memcpy(&wiaRawHeader.Tag, szRawSignature, sizeof(DWORD)); + + wiaRawHeader.Version = 0x00010000; + wiaRawHeader.HeaderSize = sizeof(wiaRawHeader); + + wiaRawHeader.XExtent = bih.biWidth; + wiaRawHeader.YExtent = bih.biHeight; + wiaRawHeader.LineOrder = (bih.biHeight < 0) ? WIA_LINE_ORDER_TOP_TO_BOTTOM : WIA_LINE_ORDER_BOTTOM_TO_TOP; + wiaRawHeader.BitsPerPixel = bih.biBitCount; + wiaRawHeader.BytesPerLine = BytesPerLine(bih.biWidth, bih.biBitCount); + wiaRawHeader.ChannelsPerPixel = (8 == bih.biBitCount) ? 1 : 3; + wiaRawHeader.DataType = (8 == bih.biBitCount) ? WIA_DATA_GRAYSCALE : WIA_DATA_RAW_BGR; + wiaRawHeader.BitsPerChannel[0] = 8; + wiaRawHeader.BitsPerChannel[1] = (8 == bih.biBitCount) ? 0 : 8; + wiaRawHeader.BitsPerChannel[2] = (8 == bih.biBitCount) ? 0 : 8; + wiaRawHeader.Compression = WIA_COMPRESSION_NONE; + wiaRawHeader.PhotometricInterp = WIA_PHOTO_WHITE_1; + + // + // The scan resolution is fixed for this sample driver: + // + wiaRawHeader.XRes = OPTICAL_RESOLUTION; + wiaRawHeader.YRes = OPTICAL_RESOLUTION; + + ulDataSize = wiaRawHeader.BytesPerLine * wiaRawHeader.YExtent; + ulPaletteSize = (8 == bih.biBitCount) ? (256 * sizeof(BYTE)) : 0; + + wiaRawHeader.RawDataSize = ulDataSize; + wiaRawHeader.RawDataOffset = ulPaletteSize; + wiaRawHeader.PaletteOffset = 0; + wiaRawHeader.PaletteSize = ulPaletteSize; + + // + // For 8-bpp Grayscale data prepare a standard grayscale palette with entries sorted + // in increasing or decreasing order depending on the photometric interpretation: + // + if (8 == bih.biBitCount) + { + for (UINT i = 0; i < 256; i++) + { + bGrayPalette[i] = (WIA_PHOTO_WHITE_1 == wiaRawHeader.PhotometricInterp) ? (BYTE)i : (255 - (BYTE)i); + } + } + + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.Version = 0x%08X", wiaRawHeader.Version)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.HeaderSize = %u bytes", wiaRawHeader.HeaderSize)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.XExtent = %u pixels", wiaRawHeader.XExtent)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.YExtent = %u pixels", wiaRawHeader.YExtent)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.LineOrder = %u", wiaRawHeader.LineOrder)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.BitsPerPixel = %u bpp", wiaRawHeader.BitsPerPixel)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.BytesPerLine = %u BPL", wiaRawHeader.BytesPerLine)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.ChannelsPerPixel = %u", wiaRawHeader.ChannelsPerPixel)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.DataType = %u", wiaRawHeader.DataType)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.BitsPerChannel[0] = %u bps", wiaRawHeader.BitsPerChannel[0])); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.BitsPerChannel[1] = %u bps", wiaRawHeader.BitsPerChannel[1])); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.BitsPerChannel[2] = %u bps", wiaRawHeader.BitsPerChannel[2])); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.Compression = %u", wiaRawHeader.Compression)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.PhotometricInterp = %u", wiaRawHeader.PhotometricInterp)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.XRes = %u DPI", wiaRawHeader.XRes)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.YRes = %u DPI", wiaRawHeader.YRes)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.RawDataOffset = %u bytes", wiaRawHeader.RawDataOffset)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.RawDataSize = %u bytes", wiaRawHeader.RawDataSize)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.PaletteOffset = %u bytes", wiaRawHeader.PaletteOffset)); + WIAS_TRACE((g_hInst, "WIA_RAW_HEADER.PaletteSize = %u bytes", wiaRawHeader.PaletteSize)); + } + } + else + { + WIAEX_ERROR((g_hInst, "Failed to read the DIB header for the test image, hr = 0x%08X", hr)); + } + + // + // If there is a grayscale palette, jump the input stream pointer past it: + // + if ((S_OK == hr) && (8 == bih.biBitCount)) + { + liOffset.LowPart = 256 * sizeof(RGBQUAD); + hr = pInputStream->Seek(liOffset, STREAM_SEEK_CUR, NULL); + if (S_OK != hr) + { + WIAEX_ERROR((g_hInst, "IStream::Seek(%u, STREAM_SEEK_SET, NULL) failed, hr = 0x%08X", liOffset.LowPart, hr)); + } + } + + // + // Create the output stream in memory: + // + if (S_OK == hr) + { + hr = CreateStreamOnHGlobal(NULL, TRUE, ppOutputStream); + if (SUCCEEDED(hr) && (!(*ppOutputStream))) + { + hr = E_FAIL; + } + if (S_OK != hr) + { + WIAEX_ERROR((g_hInst, "CreateStreamOnHGlobal failed, hr = 0x%08X", hr)); + } + } + + // + // Write the Raw header to the output stream: + // + if (S_OK == hr) + { + hr = (*ppOutputStream)->Write(&wiaRawHeader, sizeof(wiaRawHeader), &ulDataWritten); + if (SUCCEEDED(hr) && (sizeof(wiaRawHeader) != ulDataWritten)) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Expected to write %u bytes for the raw header, wrote %u bytes, hr = 0x%08X", + sizeof(wiaRawHeader), ulDataWritten, hr)); + } + if (S_OK != hr) + { + WIAEX_ERROR((g_hInst, "IStream::Write(%u bytes) failed, hr = 0x%08X", sizeof(wiaRawHeader), hr)); + } + } + + // + // Write the palette (if one) to the output stream: + // + if ((S_OK == hr) && (8 == bih.biBitCount)) + { + hr = (*ppOutputStream)->Write(&bGrayPalette[0], ulPaletteSize, &ulDataWritten); + if (SUCCEEDED(hr) && (ulPaletteSize != ulDataWritten)) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Expected to write %u bytes for the raw palette, wrote %u bytes, hr = 0x%08X", + ulPaletteSize, ulDataWritten, hr)); + } + if (S_OK != hr) + { + WIAEX_ERROR((g_hInst, "IStream::Write(%u bytes) failed, hr = 0x%08X", ulPaletteSize, hr)); + } + } + + // + // Write the Raw image data to the output stream: + // + if (S_OK == hr) + { + ULARGE_INTEGER uliDataSize = {}, uliRead = {}, uliWritten = {}; + + uliDataSize.LowPart = ulDataSize; + + hr = pInputStream->CopyTo(*ppOutputStream, uliDataSize, &uliRead, &uliWritten); + if ((S_OK == hr) && ((ulDataSize != uliRead.LowPart) || (ulDataSize != uliWritten.LowPart))) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Expected to stream copy %u bytes for the raw image data, copied %u bytes, hr = 0x%08X", + ulDataSize, uliWritten.LowPart, hr)); + } + if (S_OK != hr) + { + WIAEX_ERROR((g_hInst, "IStream::CopyTo(%u bytes) failed, hr = 0x%08X", ulDataSize, hr)); + } + } + + // + // Reset the output stream seek pointer at the beginning of the stream: + // + if (S_OK == hr) + { + liOffset.LowPart = 0; + hr = (*ppOutputStream)->Seek(liOffset, STREAM_SEEK_SET, NULL); + if (S_OK != hr) + { + WIAEX_ERROR((g_hInst, "IStream::Seek(0, STREAM_SEEK_SET, NULL) failed, hr = 0x%08X", hr)); + } + } + } + + if ((S_OK != hr) && ppOutputStream && *ppOutputStream) + { + (*ppOutputStream)->Release(); + *ppOutputStream = NULL; + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} diff --git a/wia/ProdScan/FileConv.h b/wia/ProdScan/FileConv.h new file mode 100644 index 00000000..13e0de97 --- /dev/null +++ b/wia/ProdScan/FileConv.h @@ -0,0 +1,37 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: FileConv.h +* +* Project: Production Scanner Driver Sample +* +* Description: This file contains definitions for the utility functions +* used by the sample driver for image file format conversions. +* +***************************************************************************/ + +#pragma once + +HRESULT +InitializeGDIPlus( + _Out_ ULONG_PTR *ppToken); + +HRESULT +ShutdownGDIPlus( + _In_ ULONG_PTR pToken); + +HRESULT +ConvertImageToDIB( + _In_ Image *pInputImage, + _Outptr_ IStream **ppOutputStream, + _Out_opt_ LONG *plImageWidth = NULL, + _Out_opt_ LONG *plImageHeight = NULL, + _Out_opt_ LONG *plOutputImageBPL = NULL); + +HRESULT +ConvertDibToRaw( + _In_ IStream *pInputStream, + _Out_ IStream **ppOutputStream); + + diff --git a/wia/ProdScan/InitProp.cpp b/wia/ProdScan/InitProp.cpp new file mode 100644 index 00000000..0254112d --- /dev/null +++ b/wia/ProdScan/InitProp.cpp @@ -0,0 +1,3134 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Name: InitProp.cpp +* +* Description: This file contains code for WIA property initialization +* for the Production Scanner Driver Sample +* +***************************************************************************/ + +#include "stdafx.h" + +/**************************************************************************\ +* +* Initializes the Root item properties +* +* Parameters: +* +* pWiasContext - pointer to the item context +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeRootItemProperties( + _In_ BYTE* pWiasContext) +{ + HRESULT hr = S_OK; + + WIAEX_TRACE_BEGIN; + + // + // Validate input: + // + if (!pWiasContext) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // Initialize Root item properties: + // + if (SUCCEEDED(hr)) + { + CWIAPropertyManager PropertyManager; + + // + // WIA_IPA_ITEM_CATEGORY: + // + GUID guidItemCategory = WIA_CATEGORY_ROOT; + hr = PropertyManager.AddProperty(WIA_IPA_ITEM_CATEGORY, WIA_IPA_ITEM_CATEGORY_STR, RN, guidItemCategory); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_ITEM_CATEGORY, hr = 0x%08X", hr)); + } + + // + // WIA_IPA_ACCESS_RIGHTS + // + if (SUCCEEDED(hr)) + { + LONG lAccessRights = WIA_ITEM_READ; + + hr = PropertyManager.AddProperty(WIA_IPA_ACCESS_RIGHTS, WIA_IPA_ACCESS_RIGHTS_STR, RF, lAccessRights, lAccessRights); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_ACCESS_RIGHTS, hr = 0x%08X", hr)); + } + } + + // + // WIA_DPS_DOCUMENT_HANDLING_CAPABILITIES and default WIA_DPS_DOCUMENT_HANDLING_STATUS: + // + + LONG lDocumentHandlingCapabilities = AUTO_SOURCE | FLAT | FEED | DUP | IMPRINTER | ENDORSER | + BARCODE_READER | PATCH_CODE_READER | MICR_READER; + + LONG lDocumentHandlingStatus = FLAT_READY | FEED_READY | DUP_READY | IMPRINTER_READY | ENDORSER_READY | + BARCODE_READER_READY | PATCH_CODE_READER_READY | MICR_READER_READY; + + LONG lValidDocumentHandlingStatus = FLAT_COVER_UP | FLAT_READY | FEED_READY | DUP_READY | IMPRINTER_READY | + ENDORSER_READY | BARCODE_READER_READY | PATCH_CODE_READER_READY | MICR_READER_READY | + PAPER_JAM | PATH_COVER_UP | MULTIPLE_FEED | DEVICE_ATTENTION | LAMP_ERR; + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_DPS_DOCUMENT_HANDLING_CAPABILITIES, + WIA_DPS_DOCUMENT_HANDLING_CAPABILITIES_STR , RN, lDocumentHandlingCapabilities); + + if(FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_DPS_DOCUMENT_HANDLING_CAPABILITIES, hr = 0x%08X", hr)); + } + } + + // + // WIA_DPS_DOCUMENT_HANDLING_STATUS: + // + if (SUCCEEDED(hr)) + { + // + // Initialize with default ready flag values: + // + hr = PropertyManager.AddProperty(WIA_DPS_DOCUMENT_HANDLING_STATUS, + WIA_DPS_DOCUMENT_HANDLING_STATUS_STR, RN, lDocumentHandlingStatus, lValidDocumentHandlingStatus); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_DPS_DOCUMENT_HANDLING_STATUS, hr = 0x%08X", hr)); + } + } + + // + // WIA_DPA_CONNECT_STATUS + // + // The sample device is always available: + // + if (SUCCEEDED(hr)) + { + LONG lDeviceConnected = 1; + hr = PropertyManager.AddProperty(WIA_DPA_CONNECT_STATUS, WIA_DPA_CONNECT_STATUS_STR, RN, lDeviceConnected); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_DPA_CONNECT_STATUS, hr = 0x%08X", hr)); + } + } + + // + // WIA_DPS_SCAN_AVAILABLE_ITEM: + // + if (SUCCEEDED(hr)) + { + // + // If the global (per driver instance) m_bstrScanAvailableItem is not yet initialized + // initialize it now to an empty string. Note that because we use here m_bstrScanAvailableItem + // to initialize the property we do not need to execute UpdateScanAvailableItemProperty: + // + if (!m_bstrScanAvailableItem) + { + hr = UpdateScanAvailableItemName(NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the scan available item name, hr = 0x%08X", hr)); + } + } + else + { + WIAS_TRACE((g_hInst, "Scan available from %ws", m_bstrScanAvailableItem)); + } + + if (SUCCEEDED(hr)) + { + #pragma prefast(suppress:__WARNING_INVALID_PARAM_VALUE_1, "m_bstrScanAvailableItem is allocated to an empty string by the UpdateScanAvailableItemName call above" + hr = PropertyManager.AddProperty(WIA_DPS_SCAN_AVAILABLE_ITEM, WIA_DPS_SCAN_AVAILABLE_ITEM_STR, RN, m_bstrScanAvailableItem); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_DPS_SCAN_AVAILABLE_ITEM, hr = 0x%08X", hr)); + } + } + } + + // + // Set the properties: + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.SetItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties failed to set WIA root item properties, hr = 0x%08X", hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_ITEM_CATEGORY, hr = 0x%08X", hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Wrapper method to initializes the properties for the child items this +* sample driver creates. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* nDocumentHandlingSelect - a WIA_DPS_DOCUMENT_HANDLING_SELECT value (such +* as FLAT or FEED (defined in wiadef.h) identifying +* the item being initialized +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeChildItemProperties( + _In_ BYTE* pWiasContext, + UINT nDocumentHandlingSelect) + +{ + HRESULT hr = S_OK; + CWIAPropertyManager PropertyManager; + + WIAEX_TRACE_BEGIN; + + // + // No need to validate parameters of trace failures here, the called functions + // do the validation and output full error traces: + // + hr = InitializeCommonChildProperties(pWiasContext, nDocumentHandlingSelect); + if (SUCCEEDED(hr)) + { + if ((FLAT == nDocumentHandlingSelect) || (FEEDER == nDocumentHandlingSelect)) + { + hr = InitializeFlatbedFeederProperties(pWiasContext, nDocumentHandlingSelect); + if (SUCCEEDED(hr) && (FEEDER == nDocumentHandlingSelect)) + { + hr = InitializeFeederSpecificProperties(pWiasContext); + } + } + else if ((IMPRINTER == nDocumentHandlingSelect) || (ENDORSER == nDocumentHandlingSelect)) + { + hr = InitializeImprinterEndorserProperties(pWiasContext, nDocumentHandlingSelect); + } + else if (BARCODE_READER == nDocumentHandlingSelect) + { + hr = InitializeBarcodeReaderProperties(pWiasContext); + } + else if (PATCH_CODE_READER == nDocumentHandlingSelect) + { + hr = InitializePatchCodeReaderProperties(pWiasContext); + } + else if (MICR_READER == nDocumentHandlingSelect) + { + hr = InitializeMicrReaderProperties(pWiasContext); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Initializes common child item properties (properties common to all +* data source items this driver creates: Flatbed, Feeder, Auto, Imprinter, etc. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* nDocumentHandlingSelect - a WIA_DPS_DOCUMENT_HANDLING_SELECT value (such +* as FLAT or FEED (defined in wiadef.h) identifying +* the item being initialized +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeCommonChildProperties( + _In_ BYTE* pWiasContext, + UINT nDocumentHandlingSelect) + +{ + HRESULT hr = S_OK; + + CWIAPropertyManager PropertyManager; + + if (!pWiasContext) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::InitializeCommonChildProperties, invalid item context parameter, hr = 0x%08X", hr)); + } + + WIAEX_TRACE_BEGIN; + + // + // WIA_IPA_ITEM_CATEGORY + // + if (SUCCEEDED(hr)) + { + GUID guidItemCategory = WIA_CATEGORY_FLATBED; + + switch (nDocumentHandlingSelect) + { + case FLAT: + guidItemCategory = WIA_CATEGORY_FLATBED; + break; + + case FEED: + guidItemCategory = WIA_CATEGORY_FEEDER; + break; + + case AUTO_SOURCE: + guidItemCategory = WIA_CATEGORY_AUTO; + break; + + case IMPRINTER: + guidItemCategory = WIA_CATEGORY_IMPRINTER; + break; + + case ENDORSER: + guidItemCategory = WIA_CATEGORY_ENDORSER; + break; + + case BARCODE_READER: + guidItemCategory = WIA_CATEGORY_BARCODE_READER; + break; + + case PATCH_CODE_READER: + guidItemCategory = WIA_CATEGORY_PATCH_CODE_READER; + break; + + case MICR_READER: + guidItemCategory = WIA_CATEGORY_MICR_READER; + break; + + default: + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::InitializeCommonChildProperties, invalid item (%u) parameter, hr = 0x%08X", + nDocumentHandlingSelect, hr)); + } + + hr = PropertyManager.AddProperty(WIA_IPA_ITEM_CATEGORY, WIA_IPA_ITEM_CATEGORY_STR, RN, guidItemCategory); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_ITEM_CATEGORY for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_ACCESS_RIGHTS + // + if (SUCCEEDED(hr)) + { + LONG lAccessRights = WIA_ITEM_READ; + + hr = PropertyManager.AddProperty(WIA_IPA_ACCESS_RIGHTS, WIA_IPA_ACCESS_RIGHTS_STR, RF, lAccessRights, lAccessRights); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_ACCESS_RIGHTS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_FORMAT + // + // For image transfers, this sample driver supports the DIB (mandatory default), EXIF and Raw image file formats. + // + // The sample imprinter and endorser items support the CSV (mandatory default) and TXT for text transfers and + // DIB (mandatory default) for graphics transfers. Default data transfer mode is text (required). + // + // The sample barcode, patch code and MICR reader items support the required XML and Raw metadata transfers. + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<GUID> guidFormatArray; + + if ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect) || + (AUTO_SOURCE == nDocumentHandlingSelect)) + { + guidFormatArray.Append(WiaImgFmt_BMP); + guidFormatArray.Append(WiaImgFmt_EXIF); + guidFormatArray.Append(WiaImgFmt_RAW); + } + else if ((IMPRINTER == nDocumentHandlingSelect) || (ENDORSER == nDocumentHandlingSelect)) + { + guidFormatArray.Append(WiaImgFmt_CSV); + guidFormatArray.Append(WiaImgFmt_TXT); + guidFormatArray.Append(WiaImgFmt_BMP); + } + else if (BARCODE_READER == nDocumentHandlingSelect) + { + guidFormatArray.Append(WiaImgFmt_XMLBAR); + guidFormatArray.Append(WiaImgFmt_RAWBAR); + } + else if (PATCH_CODE_READER == nDocumentHandlingSelect) + { + guidFormatArray.Append(WiaImgFmt_XMLPAT); + guidFormatArray.Append(WiaImgFmt_RAWPAT); + } + else if (MICR_READER == nDocumentHandlingSelect) + { + guidFormatArray.Append(WiaImgFmt_XMLMIC); + guidFormatArray.Append(WiaImgFmt_RAWMIC); + } + + hr = PropertyManager.AddProperty(WIA_IPA_FORMAT, WIA_IPA_FORMAT_STR, RWL, guidFormatArray[0], guidFormatArray[0], &guidFormatArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_FORMAT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_TYMED + // + // This sample driver supports only TYMED_FILE (single page files) for image as well as text and metadata data transfers + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lTymedArray; + lTymedArray.Append(TYMED_FILE); + + hr = PropertyManager.AddProperty(WIA_IPA_TYMED, WIA_IPA_TYMED_STR, RWL, lTymedArray[0], lTymedArray[0], &lTymedArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_TYMED for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_PREFERRED_FORMAT + // + // For image transfers, this sample driver reports EXIF as the preferred transfer file format. + // + // For printer/endorser transfers, this driver reports CSV as the preferred transfer file format. + // + // For barcode, patch code and MICR metadata transfers, this driver reports XML as the preferred transfer file format. + // + if (SUCCEEDED(hr)) + { + GUID guidPreferredFormat = {}; + + if ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect) || + (AUTO_SOURCE == nDocumentHandlingSelect)) + { + guidPreferredFormat = WiaImgFmt_EXIF; + } + else if ((IMPRINTER == nDocumentHandlingSelect) || (ENDORSER == nDocumentHandlingSelect)) + { + guidPreferredFormat = WiaImgFmt_CSV; + } + else if (BARCODE_READER == nDocumentHandlingSelect) + { + guidPreferredFormat = WiaImgFmt_XMLBAR; + } + else if (PATCH_CODE_READER == nDocumentHandlingSelect) + { + guidPreferredFormat = WiaImgFmt_XMLPAT; + } + else if (MICR_READER == nDocumentHandlingSelect) + { + guidPreferredFormat = WiaImgFmt_XMLMIC; + } + + hr = PropertyManager.AddProperty(WIA_IPA_PREFERRED_FORMAT, WIA_IPA_PREFERRED_FORMAT_STR, RN, guidPreferredFormat); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_PREFERRED_FORMAT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_FILENAME_EXTENSION + // + if (SUCCEEDED(hr)) + { + BSTR bstrFileExtension = NULL; + + // + // Note that WIA_IPA_FILENAME_EXTENSION must match the WIA_IPA_FORMAT current value, not WIA_IPA_PREFERRED_FORMAT: + // + if ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect) || + (AUTO_SOURCE == nDocumentHandlingSelect)) + { + bstrFileExtension = SysAllocString(FILE_EXT_BMP); + } + else if ((IMPRINTER == nDocumentHandlingSelect) || (ENDORSER == nDocumentHandlingSelect)) + { + bstrFileExtension = SysAllocString(FILE_EXT_CSV); + } + else if ((BARCODE_READER == nDocumentHandlingSelect) || (PATCH_CODE_READER == nDocumentHandlingSelect) || + (MICR_READER == nDocumentHandlingSelect)) + { + bstrFileExtension = SysAllocString(FILE_EXT_XML); + } + + if (bstrFileExtension) + { + hr = PropertyManager.AddProperty(WIA_IPA_FILENAME_EXTENSION, WIA_IPA_FILENAME_EXTENSION_STR, RN, bstrFileExtension); + + SysFreeString(bstrFileExtension); + bstrFileExtension = NULL; + } + else + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Could not allocate the file name extension property value, hr = 0x%08X", hr)); + } + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_FILENAME_EXTENSION for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_COMPRESSION + // + // For image transfers, this sample driver supports no compression (mandatory default, for DIB and Raw transfers) + // and JPEG (EEXIF transfers). The sample driver also pretends to support auto-compression (WIA_COMPRESSION_AUTO) + // but in auto-compression mode JPEG compression is always selected. + // + // For all other metadata transfers, this sample driver supports no compression (mandatory default). + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lCompressionArray; + + lCompressionArray.Append(WIA_COMPRESSION_NONE); + if ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect) || (AUTO_SOURCE == nDocumentHandlingSelect)) + { + lCompressionArray.Append(WIA_COMPRESSION_JPEG); + lCompressionArray.Append(WIA_COMPRESSION_AUTO); + } + + hr = PropertyManager.AddProperty(WIA_IPA_COMPRESSION, WIA_IPA_COMPRESSION_STR, RWL, lCompressionArray[0], lCompressionArray[0], &lCompressionArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_COMPRESSION for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // Apply the property changes to the current session's Application Item Tree: + // + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.SetItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties failed to set WIA item properties for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Initializes the properties common to the Flatbed and Feeder items. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* nDocumentHandlingSelect - FLAT or FEED (defined in wiadef.h) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeFlatbedFeederProperties( + _In_ BYTE* pWiasContext, + UINT nDocumentHandlingSelect) + +{ + HRESULT hr = S_OK; + CWIAPropertyManager PropertyManager; + + if ((!pWiasContext) || ((FLAT != nDocumentHandlingSelect) && (FEED != nDocumentHandlingSelect))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::InitializeFlatbedFeederProperties, invalid parameter, hr = 0x%08X", hr)); + } + + WIAEX_TRACE_BEGIN; + + // + // WIA_IPA_ITEM_SIZE + // + if (SUCCEEDED(hr)) + { + LONG lItemSize = 0; + + hr = PropertyManager.AddProperty(WIA_IPA_ITEM_SIZE, WIA_IPA_ITEM_SIZE_STR, RN, lItemSize); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_ITEM_SIZE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_PLANAR: + // + LONG lPlanar = WIA_PACKED_PIXEL; + + hr = PropertyManager.AddProperty(WIA_IPA_PLANAR, WIA_IPA_PLANAR_STR, RN, lPlanar); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_PLANAR for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + + // + // WIA_IPA_DATATYPE + // + // This sample driver supports 24-bpp RGB color and 8-bpp Grayscale for the image transfers, as well as the auto color mode. + // 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 document that is scanned: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lDataTypeArray; + lDataTypeArray.Append(WIA_DATA_GRAYSCALE); + lDataTypeArray.Append(WIA_DATA_COLOR); + lDataTypeArray.Append(WIA_DATA_AUTO); + + hr = PropertyManager.AddProperty(WIA_IPA_DATATYPE, WIA_IPA_DATATYPE_STR, RWL, lDataTypeArray[0], lDataTypeArray[0], &lDataTypeArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_DATATYPE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_DEPTH + // + // This sample driver supports 24-bpp RGB color and 8-bpp Grayscale, as well as the auto value (WIA_DEPTH_AUTO or 0): + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lDepthArray; + lDepthArray.Append(8); + lDepthArray.Append(24); + lDepthArray.Append(WIA_DEPTH_AUTO); + + hr = PropertyManager.AddProperty(WIA_IPA_DEPTH , WIA_IPA_DEPTH_STR, RWLC, lDepthArray[0], lDepthArray[0], &lDepthArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_DEPTH for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_CHANNELS_PER_PIXEL + // + // This sample driver supports 1 and 3 channels (samples) per pixel + // + if (SUCCEEDED(hr)) + { + LONG lChannelsPerPixel = 1; //default value that matches the default WIA_DATA_GRAYSCALE + + hr = PropertyManager.AddProperty(WIA_IPA_CHANNELS_PER_PIXEL, WIA_IPA_CHANNELS_PER_PIXEL_STR, RN, lChannelsPerPixel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_CHANNELS_PER_PIXEL for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_BITS_PER_CHANNEL + // + // This sample driver supports only 8 bits per channel (sample) + // + if (SUCCEEDED(hr)) + { + LONG lBitsPerChannel = 8; + + hr = PropertyManager.AddProperty(WIA_IPA_BITS_PER_CHANNEL, WIA_IPA_BITS_PER_CHANNEL_STR, RN, lBitsPerChannel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_BITS_PER_CHANNEL for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_RAW_BITS_PER_CHANNEL + // + if (SUCCEEDED(hr)) + { + BYTE bRawBitsPerChannel[] = { 8 }; //to match the default WIA_DATA_GRAYSCALE + + hr = PropertyManager.AddProperty(WIA_IPA_RAW_BITS_PER_CHANNEL, WIA_IPA_RAW_BITS_PER_CHANNEL_STR, RN, &bRawBitsPerChannel[0], 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_RAW_BITS_PER_CHANNEL for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + + // + // WIA_IPS_CUR_INTENT + // + if (SUCCEEDED(hr)) + { + LONG lCurrentIntent = WIA_INTENT_NONE; + LONG lValidIntents = WIA_INTENT_IMAGE_TYPE_COLOR | WIA_INTENT_IMAGE_TYPE_GRAYSCALE | WIA_INTENT_MAXIMIZE_QUALITY | WIA_INTENT_MINIMIZE_SIZE; + + hr = PropertyManager.AddProperty(WIA_IPS_CUR_INTENT, WIA_IPS_CUR_INTENT_STR, RWF, lCurrentIntent, lValidIntents); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_CUR_INTENT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_OPTICAL_XRES and WIA_IPS_OPTICAL_YRES + // + // This sample driver reports OPTICAL_RESOLUTION DPI as optical resolution on both scan directions + // + if (SUCCEEDED(hr)) + { + LONG lOpticalResolution = OPTICAL_RESOLUTION; + + hr = PropertyManager.AddProperty(WIA_IPS_OPTICAL_XRES, WIA_IPS_OPTICAL_XRES_STR, RN, lOpticalResolution); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_OPTICAL_XRES for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_OPTICAL_YRES, WIA_IPS_OPTICAL_YRES_STR, RN, lOpticalResolution); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_OPTICAL_YRES for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + } + + // + // WIA_IPS_XRES and WIA_IPS_YRES + // + // This sample driver supports OPTICAL_RESOLUTION DPI as the only scan resolution for both scan directions + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lResolutionArray; + lResolutionArray.Append(OPTICAL_RESOLUTION); + + // + // Add WIA_IPS_XRES and WIA_IPS_YRES as WIA_PROP_LIST: + // + hr = PropertyManager.AddProperty(WIA_IPS_XRES, WIA_IPS_XRES_STR, RWLC, lResolutionArray[0], lResolutionArray[0], &lResolutionArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_XRES for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_YRES, WIA_IPS_YRES_STR, RWLC, lResolutionArray[0], lResolutionArray[0], &lResolutionArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_YRES for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + } + + // + // WIA_IPS_XSCALING and WIA_IPS_YSCALING + // + // This sample driover supports only 100% scaling (which means no actual scaling) + // + if (SUCCEEDED(hr)) + { + LONG lScaling = 100; + + hr = PropertyManager.AddProperty(WIA_IPS_XSCALING, WIA_IPS_XSCALING_STR, RWRC, lScaling, lScaling, lScaling, lScaling, 0); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_XSCALING for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_YSCALING, WIA_IPS_YSCALING_STR, RWRC, lScaling, lScaling, lScaling, lScaling, 0); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_YSCALING for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + } + + // + // WIA_IPS_MIN_HORIZONTAL_SIZE, WIA_IPS_MAX_HORIZONTAL_SIZE, WIA_IPS_MIN_VERTICAL_SIZE and WIA_IPS_MAX_VERTICAL_SIZE + // + // This sample driver supports for both flatbed and feeder the following: + // + // Minimum scan region is MIN_SCAN_AREA_WIDTH x MIN_SCAN_AREA_HEIGHT + // Maximum scan region is MAX_SCAN_AREA_WIDTH x MAX_SCAN_AREA_HEIGHT + // + if (SUCCEEDED(hr)) + { + LONG lMaximumWidth = MAX_SCAN_AREA_WIDTH; + + hr = PropertyManager.AddProperty(WIA_IPS_MAX_HORIZONTAL_SIZE, WIA_IPS_MAX_HORIZONTAL_SIZE_STR, RN, lMaximumWidth); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MAX_HORIZONTAL_SIZE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + LONG lMaximumHeight = MAX_SCAN_AREA_HEIGHT; + + hr = PropertyManager.AddProperty(WIA_IPS_MAX_VERTICAL_SIZE, WIA_IPS_MAX_VERTICAL_SIZE_STR, RN, lMaximumHeight); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MAX_VERTICAL_SIZE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + LONG lMinimumWidth = MIN_SCAN_AREA_WIDTH; + + hr = PropertyManager.AddProperty(WIA_IPS_MIN_HORIZONTAL_SIZE, WIA_IPS_MIN_HORIZONTAL_SIZE_STR, RN, lMinimumWidth); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MIN_HORIZONTAL_SIZE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + LONG lMinimumHeight = MIN_SCAN_AREA_HEIGHT; + + hr = PropertyManager.AddProperty(WIA_IPS_MIN_VERTICAL_SIZE, WIA_IPS_MIN_VERTICAL_SIZE_STR, RN, lMinimumHeight); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MIN_VERTICAL_SIZE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_XPOS, WIA_IPS_YPOS, WIA_IPS_XEXTENT and WIA_IPS_YEXTENT + // + // In general, for the flatbed item, valid values are to be initialized from + // (considering current WIA_IPS_X/YRES and WIA_IPS_X/YSCALING): + // + // WIA_IPS_MIN_HORIZONTAL_SIZE, + // WIA_IPS_MIN_VERTICAL_SIZE, + // WIA_IPS_MAX_HORIZONTAL_SIZE, + // WIA_IPS_MAX_VERTICAL_SIZE + // + // For the feeder item valid values are to be initialized from + // the size of the currently selected document size, considering + // orientation and current WIA_IPS_X/YRES/SCALING: + // + // WIA_IPS_PAGE_SIZE + // WIA_IPS_PAGE_WIDTH/HEIGHT (if WIA_IPS_PAGE_SIZE is set to CUSTOM, default) + // WIA_IPS_ORIENTATION + // + // The default scan region should cover the entire available scan area. + // + + LONG lMinXExtent = 1; + LONG lMinYExtent = 1; + LONG lMaxXExtent = 2; + LONG lMaxYExtent = 2; + + LONG lXResolution = OPTICAL_RESOLUTION; + LONG lYResolution = OPTICAL_RESOLUTION; + + if (SUCCEEDED(hr) && (AUTO_SOURCE != nDocumentHandlingSelect)) + { + // + // Convert back from 1/1000" values x pixels-per-inch: + // + lMinXExtent = (MIN_SCAN_AREA_WIDTH * lXResolution) / 1000; + if (!lMinXExtent) + { + lMinXExtent = 1; + } + lMinYExtent = (MIN_SCAN_AREA_HEIGHT * lYResolution) / 1000; + if (!lMinYExtent) + { + lMinYExtent = 1; + } + lMaxXExtent = (MAX_SCAN_AREA_WIDTH * lXResolution) / 1000; + lMaxYExtent = (MAX_SCAN_AREA_HEIGHT * lYResolution) / 1000; + + // + // IMPORTANT: do not round up! + // + // lMaxXExtent = (LONG)((((float)MAX_SCAN_AREA_WIDTH * (float)lXResolution) / 1000.0f) + 0.5f); + // lMaxYExtent = (LONG)((((float)MAX_SCAN_AREA_HEIGHT * (float)lYResolution) / 1000.0f) + 0.5f); + // + + if ((lMaxXExtent < 1) || (lMaxYExtent < 1) || (lMinXExtent > lMaxXExtent) || (lMinYExtent > lMaxYExtent)) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Invalid resolution and-or minimum and-or maximum scan area size values, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_XPOS, WIA_IPS_XPOS_STR, RWRC, 0, 0, 0, lMaxXExtent - lMinXExtent, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_XPOS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_YPOS, WIA_IPS_YPOS_STR, RWRC, 0, 0, 0, lMaxYExtent - lMinYExtent, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_YPOS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_XEXTENT, WIA_IPS_XEXTENT_STR, RWRC, lMaxXExtent, lMaxXExtent, lMinXExtent, lMaxXExtent, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_XEXTENT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_YEXTENT, WIA_IPS_YEXTENT_STR, RWRC, lMaxYExtent, lMaxYExtent, lMinYExtent, lMaxYExtent, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_YEXTENT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + } + + // + // WIA_IPS_BRIGHTNESS and WIA_IPS_CONTRAST + // + // This sample driver simulates brightness and contrast adjustment between + // a standard range from -1000 to 1000, with a default value of 0. + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_BRIGHTNESS, WIA_IPS_BRIGHTNESS_STR, RWRC, 0, 0, -1000, 1000, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_BRIGHTNESS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_CONTRAST, WIA_IPS_CONTRAST_STR, RWRC, 0, 0, -1000, 1000, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_CONTRAST for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_ROTATION + // + // This sample driver supports only 0 degrees rotation (no actual rotation) + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lRotationArray; + lRotationArray.Append(0); + + hr = PropertyManager.AddProperty(WIA_IPS_ROTATION, WIA_IPS_ROTATION_STR, RWLC, lRotationArray[0], lRotationArray[0], &lRotationArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ROTATION for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_THRESHOLD + // + // This sample driver supports only the default value of 128 + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_THRESHOLD, WIA_IPS_THRESHOLD_STR, RWRC, 128, 128, 128, 128, 0); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_THRESHOLD for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PREVIEW + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lScanModeArray; + lScanModeArray.Append(WIA_FINAL_SCAN); + lScanModeArray.Append(WIA_PREVIEW_SCAN); + + hr = PropertyManager.AddProperty(WIA_IPS_PREVIEW, WIA_IPS_PREVIEW_STR, RWL, lScanModeArray[0], lScanModeArray[0], &lScanModeArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PREVIEW for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_SHOW_PREVIEW_CONTROL + // + if (SUCCEEDED(hr)) + { + // + // There is the option to disable the preview control for Feeder but this sample driver is not using it: + // + // lShowPreviewControl = (FLAT == nDocumentHandlingSelect) ? WIA_SHOW_PREVIEW_CONTROL : WIA_DONT_SHOW_PREVIEW_CONTROL; + // + + LONG lShowPreviewControl = WIA_SHOW_PREVIEW_CONTROL; + + hr = PropertyManager.AddProperty(WIA_IPS_SHOW_PREVIEW_CONTROL, WIA_IPS_SHOW_PREVIEW_CONTROL_STR, RN, lShowPreviewControl); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_SHOW_PREVIEW_CONTROL for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_SUPPORTS_CHILD_ITEM_CREATION + // + if (SUCCEEDED(hr)) + { + BOOL lSupportsChildItem = FALSE; + + hr = PropertyManager.AddProperty(WIA_IPS_SUPPORTS_CHILD_ITEM_CREATION, WIA_IPS_SUPPORTS_CHILD_ITEM_CREATION_STR, RN, lSupportsChildItem); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_SUPPORTS_CHILD_ITEM_CREATION for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PHOTOMETRIC_INTERP + // + // This sample driver supports only the default value of WIA_PHOTO_WHITE_1 + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lPhotoInterpArray; + lPhotoInterpArray.Append(WIA_PHOTO_WHITE_1); + + hr = PropertyManager.AddProperty(WIA_IPS_PHOTOMETRIC_INTERP, WIA_IPS_PHOTOMETRIC_INTERP_STR, RWL, lPhotoInterpArray[0], lPhotoInterpArray[0], &lPhotoInterpArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PHOTOMETRIC_INTERP for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // Even though deprecated the following "image information" properties are still required + // for compatibility with existing legacy XP applications, including Scanner and Camera Wizard, + // applications using the default WIA UI (including Paint) and the TWAIN applications using + // the WIA driver though the TWAIN - WIA compatibility layer: + // + // WIA_IPA_PIXELS_PER_LINE - the image width, in pixels, for the final image + // WIA_IPA_NUMBER_OF_LINES - the image length, in pixels, for the final image + // WIA_IPA_BYTES_PER_LINE - line width in bytes matching WIA_IPA_PIXELS_PER_LINE and WIA_IPA_DEPTH + // + // All these values must match the exact dimensions of the final image to be transferred + // to the application, a mismatch could cause unpredictable behaviour, including Divide by Zero + // and Access Violation errors in the application attempting to receive data that doesn't exist. + // + + if (SUCCEEDED(hr)) + { + LONG lPixelsPerLine = lMaxXExtent; + + hr = PropertyManager.AddProperty(WIA_IPA_PIXELS_PER_LINE, WIA_IPA_PIXELS_PER_LINE_STR, RN, lPixelsPerLine); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_PIXELS_PER_LINE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + LONG lNumberOfLines = lMaxYExtent; + + hr = PropertyManager.AddProperty(WIA_IPA_NUMBER_OF_LINES, WIA_IPA_NUMBER_OF_LINES_STR, RN, lNumberOfLines); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_NUMBER_OF_LINES for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + // + // DIB (the default image file transfer format) lines must be DWORD aligned, meaning + // that each line must be multiple by 4 bytes in length, padded if necessary at the end: + // + LONG lBytesPerLine = 2552; + + hr = PropertyManager.AddProperty(WIA_IPA_BYTES_PER_LINE, WIA_IPA_BYTES_PER_LINE_STR, RN, lBytesPerLine); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_BYTES_PER_LINE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_BUFFER_SIZE must be supported in order to be able to increase (if needed) the default 64KB value set + // by the WIA Compatibility Layer in the WIA Service when the driver is used with a legacy WIA 1.0 application: + // + + if (SUCCEEDED(hr)) + { + LONG lBufferSize = DEFAULT_BUFFER_SIZE; + + hr = PropertyManager.AddProperty(WIA_IPA_BUFFER_SIZE, WIA_IPA_BUFFER_SIZE_STR, RN, lBufferSize); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_BUFFER_SIZE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_AUTO_CROP + // + // The sample driver implements WIA_AUTO_CROP_SINGLE but does not support actual image cropping: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lAutoCropArray; + lAutoCropArray.Append(WIA_AUTO_CROP_DISABLED); + lAutoCropArray.Append(WIA_AUTO_CROP_SINGLE); + + hr = PropertyManager.AddProperty(WIA_IPS_AUTO_CROP, WIA_IPS_AUTO_CROP_STR, RWL, lAutoCropArray[0], lAutoCropArray[0], &lAutoCropArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_AUTO_CROP for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_OVER_SCAN + // + // The sample driver pretends to support overscanning on all directions, however the overscan settings are unfunctional: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lOverScanArray; + lOverScanArray.Append(WIA_OVER_SCAN_DISABLED); + lOverScanArray.Append(WIA_OVER_SCAN_TOP_BOTTOM); + lOverScanArray.Append(WIA_OVER_SCAN_LEFT_RIGHT); + lOverScanArray.Append(WIA_OVER_SCAN_ALL); + + hr = PropertyManager.AddProperty(WIA_IPS_OVER_SCAN, WIA_IPS_OVER_SCAN_STR, RWL, lOverScanArray[0], lOverScanArray[0], &lOverScanArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_OVER_SCAN for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_OVER_SCAN_LEFT, WIA_IPS_OVER_SCAN_RIGHT, WIA_IPS_OVER_SCAN_TOP and WIA_IPS_OVER_SCAN_BOTTOM + // + // The sample driver pretends to support overscanning from 0 to 1" on all document sides, in 0.001" increments: + // + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_OVER_SCAN_LEFT, WIA_IPS_OVER_SCAN_LEFT_STR, RWRC, 0, 0, 0, 1000, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_OVER_SCAN_LEFT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_OVER_SCAN_RIGHT, WIA_IPS_OVER_SCAN_RIGHT_STR, RWRC, 0, 0, 0, 1000, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_OVER_SCAN_RIGHT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_OVER_SCAN_TOP, WIA_IPS_OVER_SCAN_TOP_STR, RWRC, 0, 0, 0, 1000, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_OVER_SCAN_TOP for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_OVER_SCAN_BOTTOM, WIA_IPS_OVER_SCAN_BOTTOM_STR, RWRC, 0, 0, 0, 1000, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_OVER_SCAN_BOTTOM for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_COLOR_DROP + // + // The sample driver implements the color-drop properties, however it does not + // execute any actual color filtering (drop) on the test image: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lColorDropArray; + lColorDropArray.Append(WIA_COLOR_DROP_DISABLED); + lColorDropArray.Append(WIA_COLOR_DROP_RED); + lColorDropArray.Append(WIA_COLOR_DROP_GREEN); + lColorDropArray.Append(WIA_COLOR_DROP_BLUE); + lColorDropArray.Append(WIA_COLOR_DROP_RGB); + + hr = PropertyManager.AddProperty(WIA_IPS_COLOR_DROP, WIA_IPS_COLOR_DROP_STR, RWL, lColorDropArray[0], lColorDropArray[0], &lColorDropArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_COLOR_DROP for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_COLOR_DROP_MULTI: + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_COLOR_DROP_MULTI, WIA_IPS_COLOR_DROP_MULTI_STR, RN, g_lMaxDropColors); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_COLOR_DROP_MULTI for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_COLOR_DROP_RED, WIA_IPS_COLOR_DROP_GREEN and WIA_IPS_COLOR_DROP_BLUE: + // + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_COLOR_DROP_RED, WIA_IPS_COLOR_DROP_RED_STR, RW, g_lMaxDropColors, (LONG *)&g_lDefaultDropColors[0]); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_COLOR_DROP_RED for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_COLOR_DROP_GREEN, WIA_IPS_COLOR_DROP_GREEN_STR, RW, g_lMaxDropColors, (LONG *)&g_lDefaultDropColors[0]); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_COLOR_DROP_GREEN for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_COLOR_DROP_BLUE, WIA_IPS_COLOR_DROP_BLUE_STR, RW, g_lMaxDropColors, (LONG *)&g_lDefaultDropColors[0]); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_COLOR_DROP_BLUE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // Apply the property changes to the current session's Application Item Tree: + // + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.SetItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties failed to set WIA item properties for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Initializes the properties specific to the Feeder item. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeFeederSpecificProperties( + _In_ BYTE* pWiasContext) + +{ + HRESULT hr = S_OK; + CWIAPropertyManager PropertyManager; + + if (!pWiasContext) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::InitializeFeederSpecificProperties, invalid parameter, hr = 0x%08X", hr)); + } + + WIAEX_TRACE_BEGIN; + + // + // WIA_IPS_DOCUMENT_HANDLING_SELECT + // + LONG lDocumentHandlingSelect = FRONT_ONLY; + LONG lDocumentHandlingSelectValidValues = FRONT_ONLY | DUPLEX; + + hr = PropertyManager.AddProperty(WIA_IPS_DOCUMENT_HANDLING_SELECT, WIA_IPS_DOCUMENT_HANDLING_SELECT_STR, RWF, lDocumentHandlingSelect, lDocumentHandlingSelectValidValues); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_DOCUMENT_HANDLING_SELECT, hr = 0x%08X", hr)); + } + + // + // WIA_IPS_SHEET_FEEDER_REGISTRATION + // + if (SUCCEEDED(hr)) + { + LONG lFeederRegistration = LEFT_JUSTIFIED; + + hr = PropertyManager.AddProperty(WIA_IPS_SHEET_FEEDER_REGISTRATION, WIA_IPS_SHEET_FEEDER_REGISTRATION_STR, RN, lFeederRegistration); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_SHEET_FEEDER_REGISTRATION, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_PAGES + // + // Important: the default value for this property should be ALL_PAGES (0). + // However legacy XP applications need a WIA_IPS_PAGES > 0 in order to work. + // For this reason the default WIA_IPS_PAGES is changed to 1. Clients who need + // to transfer all available images must set WIA_IPS_PAGES to ALL_PAGES. + // + if (SUCCEEDED(hr)) + { + LONG lMaxPages = 0x7FFFFFFF; //maximum pozitive value for a signed 32-bit integer + LONG lMinPages = 0; //ALL_PAGES + LONG lDefaultPages = 1; + + hr = PropertyManager.AddProperty(WIA_IPS_PAGES, WIA_IPS_PAGES_STR, + RWR, lDefaultPages, lDefaultPages, lMinPages, lMaxPages, 1); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PAGES, hr = 0x%08X", hr)); + } + } + + LONG lPageWidth = MAX_SCAN_AREA_WIDTH; + LONG lPageHeight = MAX_SCAN_AREA_HEIGHT; + + // + // WIA_IPS_PAGE_SIZE + // + // This sample driver supports Letter, custom and auto-detect document sizes + // + if (SUCCEEDED(hr)) + { + LONG lDefaultPageSize = WIA_PAGE_CUSTOM; + + hr = PropertyManager.AddProperty(WIA_IPS_PAGE_SIZE, WIA_IPS_PAGE_SIZE_STR, RWL, lDefaultPageSize, lDefaultPageSize, &m_lPortraitSizesArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PAGE_SIZE, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_ORIENTATION + // + // This sample driver supports only Portrait orientation + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lOrientationArray; + lOrientationArray.Append(PORTRAIT); + + hr = PropertyManager.AddProperty(WIA_IPS_ORIENTATION, WIA_IPS_ORIENTATION_STR, RWL, lOrientationArray[0], lOrientationArray[0], &lOrientationArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ORIENTATION, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_PAGE_WIDTH + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_PAGE_WIDTH, WIA_IPS_PAGE_WIDTH_STR, RN, lPageWidth); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ORIENTATION, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_PAGE_HEIGHT + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_PAGE_HEIGHT, WIA_IPS_PAGE_HEIGHT_STR, RN, lPageHeight); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ORIENTATION, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_JOB_SEPARATORS + // + // When job separators are enabled, the sample driver simulates a job separator page every JOB_SEPARATOR_AT_PAGE pages scanned: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lJobSeparatorsArray; + lJobSeparatorsArray.Append(WIA_SEPARATOR_DISABLED); + lJobSeparatorsArray.Append(WIA_SEPARATOR_DETECT_SCAN_CONTINUE); + lJobSeparatorsArray.Append(WIA_SEPARATOR_DETECT_SCAN_STOP); + lJobSeparatorsArray.Append(WIA_SEPARATOR_DETECT_NOSCAN_CONTINUE); + lJobSeparatorsArray.Append(WIA_SEPARATOR_DETECT_NOSCAN_STOP); + + hr = PropertyManager.AddProperty(WIA_IPS_JOB_SEPARATORS, WIA_IPS_JOB_SEPARATORS_STR, RWL, lJobSeparatorsArray[0], lJobSeparatorsArray[0], &lJobSeparatorsArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_JOB_SEPARATORS, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_LONG_DOCUMENT + // + // The sample driver implemenmts the property but does not implement the actual functionality: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lLongDocArray; + lLongDocArray.Append(WIA_LONG_DOCUMENT_DISABLED); + lLongDocArray.Append(WIA_LONG_DOCUMENT_ENABLED); + lLongDocArray.Append(WIA_LONG_DOCUMENT_SPLIT); + + hr = PropertyManager.AddProperty(WIA_IPS_LONG_DOCUMENT, WIA_IPS_LONG_DOCUMENT_STR, RWL, lLongDocArray[0], lLongDocArray[0], &lLongDocArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_LONG_DOCUMENT, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_BLANK_PAGES + // + // The sample driver implements the property but does not implement actual blank page detection functionality: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lBlankPagesArray; + lBlankPagesArray.Append(WIA_BLANK_PAGE_DETECTION_DISABLED); + lBlankPagesArray.Append(WIA_BLANK_PAGE_DISCARD); + lBlankPagesArray.Append(WIA_BLANK_PAGE_JOB_SEPARATOR); + + hr = PropertyManager.AddProperty(WIA_IPS_BLANK_PAGES, WIA_IPS_BLANK_PAGES_STR, RWL, lBlankPagesArray[0], lBlankPagesArray[0], &lBlankPagesArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_BLANK_PAGES, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_BLANK_PAGES_SENSITIVITY + // + // This sample driver reports a range of supported values (which are not functional) + // between 0 and 10 inclusive, with 5 being the default sensitivity: + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_BLANK_PAGES_SENSITIVITY, WIA_IPS_BLANK_PAGES_SENSITIVITY_STR, RWR, 5, 5, 1, 10, 1); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_BLANK_PAGES_SENSITIVITY, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_MULTI_FEED + // + // When multi-feed detection is enabled, the sample driver simulates a multi-feed condition every MULTI_FEED_AT_PAGE pages scanned: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lMultiFeedArray; + lMultiFeedArray.Append(WIA_MULTI_FEED_DETECT_DISABLED); + lMultiFeedArray.Append(WIA_MULTI_FEED_DETECT_STOP_ERROR); + lMultiFeedArray.Append(WIA_MULTI_FEED_DETECT_STOP_SUCCESS); + lMultiFeedArray.Append(WIA_MULTI_FEED_DETECT_CONTINUE); + + hr = PropertyManager.AddProperty(WIA_IPS_MULTI_FEED, WIA_IPS_MULTI_FEED_STR, RWL, lMultiFeedArray[0], lMultiFeedArray[0], &lMultiFeedArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MULTI_FEED, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_MULTI_FEED_SENSITIVITY + // + // This sample driver reports a range of supported values (which are not functional) + // between 0 and 10 inclusive, with 5 being the default sensitivity: + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_MULTI_FEED_SENSITIVITY, WIA_IPS_MULTI_FEED_SENSITIVITY_STR, RWR, 5, 5, 1, 10, 1); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MULTI_FEED_SENSITIVITY, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_ALARM + // + // This sample driver does pretend to support one kind of audible alarm (beep) to signal + // when a multi-feed conditions is detected, not functional: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lAlarmArray; + lAlarmArray.Append(WIA_ALARM_NONE); + lAlarmArray.Append(WIA_ALARM_BEEP1); + + hr = PropertyManager.AddProperty(WIA_IPS_ALARM, WIA_IPS_ALARM_STR, RWL, lAlarmArray[0], lAlarmArray[0], &lAlarmArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ALARM, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_SCAN_AHEAD: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lScanAheadArray; + lScanAheadArray.Append(WIA_SCAN_AHEAD_DISABLED); + lScanAheadArray.Append(WIA_SCAN_AHEAD_ENABLED); + + hr = PropertyManager.AddProperty(WIA_IPS_SCAN_AHEAD, WIA_IPS_SCAN_AHEAD_STR, RWL, lScanAheadArray[0], lScanAheadArray[0], &lScanAheadArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_SCAN_AHEAD, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_SCAN_AHEAD_CAPACITY: + // + if (SUCCEEDED(hr)) + { + ULONG ulScanAheadCapacity = 0; //undefined + + hr = PropertyManager.AddPropertyUL(WIA_IPS_SCAN_AHEAD_CAPACITY, WIA_IPS_SCAN_AHEAD_CAPACITY_STR, RN, ulScanAheadCapacity); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_SCAN_AHEAD_CAPACITY, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_FEEDER_CONTROL + // + // The sample driver pretents to support manual feeder motor control + // (the WIA_COMMAND_START_FEEDER and the WIA_COMMAND_STOP_FEEDER commands): + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lFeederControlArray; + lFeederControlArray.Append(WIA_FEEDER_CONTROL_AUTO); + lFeederControlArray.Append(WIA_FEEDER_CONTROL_MANUAL); + + hr = PropertyManager.AddProperty(WIA_IPS_FEEDER_CONTROL, WIA_IPS_FEEDER_CONTROL_STR, RWL, lFeederControlArray[0], lFeederControlArray[0], &lFeederControlArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_SCAN_AHEAD, hr = 0x%08X", hr)); + } + } + + // + // Apply the property changes to the current session's Application Item Tree: + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.SetItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties failed to set WIA item properties for feeder, hr = 0x%08X", hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Initializes the properties specific to the Imprinter and Endorser items. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* nDocumentHandlingSelect - IMPRINTER or ENDORSER (defined in wiadef.h) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeImprinterEndorserProperties( + _In_ BYTE* pWiasContext, + UINT nDocumentHandlingSelect) +{ + HRESULT hr = S_OK; + CWIAPropertyManager PropertyManager; + + if ((!pWiasContext) || ((IMPRINTER != nDocumentHandlingSelect) && (ENDORSER != nDocumentHandlingSelect))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::InitializeImprinterEndorserProperties, invalid parameter, hr = 0x%08X", hr)); + } + + WIAEX_TRACE_BEGIN; + + // + // WIA_IPS_PRINTER_ENDORSER + // + // This sample driver pretends to have an imprinter on the front side of the feeder and an endorser on the back side + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lPrinterEndorserArray; + lPrinterEndorserArray.Append(WIA_PRINTER_ENDORSER_DISABLED); + lPrinterEndorserArray.Append(WIA_PRINTER_ENDORSER_AUTO); + lPrinterEndorserArray.Append((IMPRINTER == nDocumentHandlingSelect) ? WIA_PRINTER_ENDORSER_FEEDER_FRONT : WIA_PRINTER_ENDORSER_FEEDER_BACK); + + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER, WIA_IPS_PRINTER_ENDORSER_STR, RWL, lPrinterEndorserArray[0], lPrinterEndorserArray[0], &lPrinterEndorserArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_ORDER + // + // The sample imprinter operates after scan, the sample endorser operates before scan + // + if (SUCCEEDED(hr)) + { + LONG lPrinterEndorserOrder = (IMPRINTER == nDocumentHandlingSelect) ? WIA_PRINTER_ENDORSER_AFTER_SCAN : WIA_PRINTER_ENDORSER_BEFORE_SCAN; + + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_ORDER, WIA_IPS_PRINTER_ENDORSER_ORDER_STR, RN, lPrinterEndorserOrder); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_ORDER for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_COUNTER + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_COUNTER , WIA_IPS_PRINTER_ENDORSER_COUNTER_STR, RWRC, 0, 0, 0, 0xFFFFFFFF, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_COUNTER for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + + } + + // + // WIA_IPS_PRINTER_ENDORSER_STEP + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_STEP, WIA_IPS_PRINTER_ENDORSER_STEP_STR, RWRC, 1, 1, 1, 0xFFFFFFFF, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_STEP for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + + } + + // + // WIA_IPS_PRINTER_ENDORSER_XOFFSET and WIA_IPS_PRINTER_ENDORSER_YOFFSET + // + // This sample driver pretends to support from 0" to 3" inclusive imprinter and endorser offsets, in 0.001" step increments + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_XOFFSET, WIA_IPS_PRINTER_ENDORSER_XOFFSET_STR, RWRC, 0, 0, 0, 3000, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_XOFFSET for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + else + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_YOFFSET, WIA_IPS_PRINTER_ENDORSER_YOFFSET_STR, RWRC, 0, 0, 0, 3000, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_YOFFSET for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + } + + // + // WIA_IPS_ROTATION + // + // This sample driver pretends to support all standard 90' rotation values for its imprinter and endorser units + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lRotationArray; + lRotationArray.Append(PORTRAIT); + lRotationArray.Append(LANDSCAPE); + lRotationArray.Append(ROT180); + lRotationArray.Append(ROT270); + + hr = PropertyManager.AddProperty(WIA_IPS_ROTATION, WIA_IPS_ROTATION_STR, RWL, lRotationArray[0], lRotationArray[0], &lRotationArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ROTATION for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_NUM_LINES + // + // This sample driver pretends that supports only one line of text for the imprinter and for the endorser + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_NUM_LINES, WIA_IPS_PRINTER_ENDORSER_NUM_LINES_STR, RN, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_NUM_LINES for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_STRING + // + if (SUCCEEDED(hr)) + { + BSTR bstrPrinterEndorser = SysAllocString((IMPRINTER == nDocumentHandlingSelect) ? L"Sample imprinter text" : L"Sample endorser text"); + if (bstrPrinterEndorser) + { + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_STRING, WIA_IPS_PRINTER_ENDORSER_STRING_STR, RW, bstrPrinterEndorser); + + SysFreeString(bstrPrinterEndorser); + } + else + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Could not allocate memory for the the WIA_IPS_PRINTER_ENDORSER_STRING property value, hr = 0x%08X", hr)); + } + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_STRING for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_VALID_CHARACTERS + // + if (SUCCEEDED(hr)) + { + BSTR bstrValidChars = SysAllocString((IMPRINTER == nDocumentHandlingSelect) ? SAMPLE_IMPRINTER_VALID_CHARS : SAMPLE_ENDORSER_VALID_CHARS); + if (bstrValidChars) + { + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_VALID_CHARACTERS, WIA_IPS_PRINTER_ENDORSER_VALID_CHARACTERS_STR, RN, bstrValidChars); + + SysFreeString(bstrValidChars); + bstrValidChars = NULL; + } + else + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Could not allocate memory for the the IA_IPS_PRINTER_ENDORSER_VALID_CHARACTERS property value, hr = 0x%08X", hr)); + } + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_VALID_CHARACTERS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_VALID_FORMAT_SPECIFIERS + // + // This sample driver implements this optional property only for the imprinter + // where it implements a sub-set of all the possible standard values. + // + if (SUCCEEDED(hr) && (IMPRINTER == nDocumentHandlingSelect)) + { + LONG lFormatSpecs[] = { + WIA_PRINT_DATE, + WIA_PRINT_YEAR, + WIA_PRINT_MONTH, + WIA_PRINT_DAY, + WIA_PRINT_WEEK_DAY, + WIA_PRINT_TIME_24H, + WIA_PRINT_HOUR_24H, + WIA_PRINT_MINUTE, + WIA_PRINT_SECOND, + WIA_PRINT_PAGE_COUNT}; + ULONG ulFormatSpecs = ARRAYSIZE(lFormatSpecs); + + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_VALID_FORMAT_SPECIFIERS, WIA_IPS_PRINTER_ENDORSER_VALID_FORMAT_SPECIFIERS_STR, RN, ulFormatSpecs, lFormatSpecs); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_VALID_FORMAT_SPECIFIERS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_PADDING + // + // This sample driver does pretend to support all imprinter/endorser padding values but does not really apply padding: + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lPaddingArray; + lPaddingArray.Append(WIA_PRINT_PADDING_NONE); + lPaddingArray.Append(WIA_PRINT_PADDING_ZERO); + lPaddingArray.Append(WIA_PRINT_PADDING_BLANK); + + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_PADDING, WIA_IPS_PRINTER_ENDORSER_PADDING_STR, RWL, lPaddingArray[0], lPaddingArray[0], &lPaddingArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_PADDING for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_FONT_TYPE + // + // This sample driver does support all font type values but does not apply font type changes. + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lFontTypeArray; + lFontTypeArray.Append(WIA_PRINT_FONT_NORMAL); + lFontTypeArray.Append(WIA_PRINT_FONT_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_EXTRA_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_ITALIC_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_ITALIC_EXTRA_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_ITALIC); + lFontTypeArray.Append(WIA_PRINT_FONT_SMALL); + lFontTypeArray.Append(WIA_PRINT_FONT_SMALL_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_SMALL_EXTRA_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_SMALL_ITALIC_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_SMALL_ITALIC_EXTRA_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_SMALL_ITALIC); + lFontTypeArray.Append(WIA_PRINT_FONT_LARGE); + lFontTypeArray.Append(WIA_PRINT_FONT_LARGE_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_LARGE_EXTRA_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_LARGE_ITALIC_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_LARGE_ITALIC_EXTRA_BOLD); + lFontTypeArray.Append(WIA_PRINT_FONT_LARGE_ITALIC); + + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_FONT_TYPE, WIA_IPS_PRINTER_ENDORSER_FONT_TYPE_STR, RWL, lFontTypeArray[0], lFontTypeArray[0], &lFontTypeArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_FONT_TYPE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_CHARACTER_ROTATION + // + // This sample driver pretends to support all possible values, but does not actually apply any character rotation. + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lCharRotatationArray; + lCharRotatationArray.Append(PORTRAIT); + lCharRotatationArray.Append(LANDSCAPE); + lCharRotatationArray.Append(ROT180); + lCharRotatationArray.Append(ROT270); + + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_CHARACTER_ROTATION, WIA_IPS_PRINTER_ENDORSER_CHARACTER_ROTATION_STR, + RWL, lCharRotatationArray[0], lCharRotatationArray[0], &lCharRotatationArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_CHARACTER_ROTATION for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + + // + // WIA_IPS_PRINTER_ENDORSER_MAX_CHARACTERS + // + // This sample driver pretends to support a maximum number of characters of 0xFFFFFFFF (unrealistic) for the imprinter and endorser. + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_MAX_CHARACTERS, WIA_IPS_PRINTER_ENDORSER_MAX_CHARACTERS_STR, RN, 0xFFFFFFFF); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_MAX_CHARACTERS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_INK + // + // This sample driver hard-codes a value of 50% (half capacity remaining) for the imprinter and endorser ink. + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_INK, WIA_IPS_PRINTER_ENDORSER_INK_STR, RN, 50); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_INK for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_TEXT_UPLOAD + // + // This sample driver reports to support imprinter/endorser text upload to show how an upload + // transfer is to be executed, however the uploaded data is not retained/applied. + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_TEXT_UPLOAD, WIA_IPS_PRINTER_ENDORSER_TEXT_UPLOAD_STR, RN, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_TEXT_UPLOAD for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_TEXT_DOWNLOAD + // + // This sample driver reports to support imprinter/endorser text download to show how a download + // transfer is to be executed, however the downloaded data is always the same/fixed, and is not + // modified to match WIA_IPS_PRINTER_ENDORSER_STRING. + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_TEXT_DOWNLOAD, WIA_IPS_PRINTER_ENDORSER_TEXT_DOWNLOAD_STR, RN, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_TEXT_DOWNLOAD for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_GRAPHICS + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_GRAPHICS, WIA_IPS_PRINTER_ENDORSER_GRAPHICS_STR, RN, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_GRAPHICS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_GRAPHICS_POSITION + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lPositionArray; + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_DEVICE_DEFAULT); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_LEFT); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_RIGHT); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_TOP); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_BOTTOM); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_TOP_LEFT); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_TOP_RIGHT); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_BOTTOM_LEFT); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_BOTTOM_RIGHT); + lPositionArray.Append(WIA_PRINTER_ENDORSER_GRAPHICS_BACKGROUND); + + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_GRAPHICS_POSITION, WIA_IPS_PRINTER_ENDORSER_GRAPHICS_POSITION_STR, RWL, lPositionArray[0], lPositionArray[0], &lPositionArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ROTATION for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_WIDTH + // WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_HEIGHT + // WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MAX_WIDTH + // WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MAX_HEIGHT + // + // This sample driver supports a fixed/predefined graphics size that match the sample imprinter/endorser test image + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_WIDTH, WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_WIDTH_STR, RN, IMPRINTER_MIN_WIDTH); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_WIDTH for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MAX_WIDTH, WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MAX_WIDTH_STR, RN, IMPRINTER_MAX_WIDTH); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_WIDTH for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_HEIGHT, WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_HEIGHT_STR, RN, IMPRINTER_MIN_HEIGHT); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MIN_HEIGHT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MAX_HEIGHT, WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MAX_HEIGHT_STR, RN, IMPRINTER_MAX_HEIGHT); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_GRAPHICS_MAX_HEIGHT for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_MAX_GRAPHICS + // + // This sample driver pretends to support a maximum number of graphics of 1 for its imprinter and endorser. + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_PRINTER_ENDORSER_MAX_GRAPHICS, WIA_IPS_PRINTER_ENDORSER_MAX_GRAPHICS_STR, RN, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_MAX_GRAPHICS for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_GRAPHICS_UPLOAD + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_GRAPHICS_UPLOAD, WIA_IPS_PRINTER_ENDORSER_GRAPHICS_UPLOAD_STR, RN, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_GRAPHICS_UPLOAD for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPS_PRINTER_ENDORSER_GRAPHICS_DOWNLOAD + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddProperty(WIA_IPS_PRINTER_ENDORSER_GRAPHICS_DOWNLOAD, WIA_IPS_PRINTER_ENDORSER_GRAPHICS_DOWNLOAD_STR, RN, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PRINTER_ENDORSER_GRAPHICS_DOWNLOAD for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_DATATYPE + // + // The sample driver supports 1-bpp BW graphics data for the imprinter and the endorser. + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lDataTypeArray; + lDataTypeArray.Append(WIA_DATA_DITHER); + + hr = PropertyManager.AddProperty(WIA_IPA_DATATYPE, WIA_IPA_DATATYPE_STR, RWL, lDataTypeArray[0], lDataTypeArray[0], &lDataTypeArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_DATATYPE for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_DEPTH + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lDepthArray; + lDepthArray.Append(1); + + hr = PropertyManager.AddProperty(WIA_IPA_DEPTH , WIA_IPA_DEPTH_STR, RWLC, lDepthArray[0], lDepthArray[0], &lDepthArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_DEPTH for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_CHANNELS_PER_PIXEL + // + if (SUCCEEDED(hr)) + { + LONG lChannelsPerPixel = 1; + + hr = PropertyManager.AddProperty(WIA_IPA_CHANNELS_PER_PIXEL, WIA_IPA_CHANNELS_PER_PIXEL_STR, RN, lChannelsPerPixel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_CHANNELS_PER_PIXEL for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // WIA_IPA_BITS_PER_CHANNEL + // + if (SUCCEEDED(hr)) + { + LONG lBitsPerChannel = 1; + + hr = PropertyManager.AddProperty(WIA_IPA_BITS_PER_CHANNEL, WIA_IPA_BITS_PER_CHANNEL_STR, RN, lBitsPerChannel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPA_BITS_PER_CHANNEL for item %u, hr = 0x%08X", nDocumentHandlingSelect, hr)); + } + } + + // + // Apply the property changes to the current session's Application Item Tree: + // + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.SetItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties failed to set WIA item properties for item %u, hr = 0x%08X", + nDocumentHandlingSelect, hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Initializes the properties specific to the Barcode Reader item. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeBarcodeReaderProperties( + _In_ BYTE* pWiasContext) +{ + HRESULT hr = S_OK; + CWIAPropertyManager PropertyManager; + + if (!pWiasContext) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::InitializeBarcodeReaderProperties, invalid parameter, hr = 0x%08X", hr)); + } + + WIAEX_TRACE_BEGIN; + + // + // WIA_IPS_BARCODE_READER + // + // This sample driver pretends to support a barcode reader device installed on the front feeder side + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lBarcodeReaderArray; + lBarcodeReaderArray.Append(WIA_BARCODE_READER_DISABLED); + lBarcodeReaderArray.Append(WIA_BARCODE_READER_AUTO); + lBarcodeReaderArray.Append(WIA_BARCODE_READER_FEEDER_FRONT); + + hr = PropertyManager.AddProperty(WIA_IPS_BARCODE_READER, WIA_IPS_BARCODE_READER_STR, RWL, lBarcodeReaderArray[0], lBarcodeReaderArray[0], &lBarcodeReaderArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_BARCODE_READER, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_MAXIMUM_BARCODES_PER_PAGE + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_MAXIMUM_BARCODES_PER_PAGE, WIA_IPS_MAXIMUM_BARCODES_PER_PAGE_STR, RWR, 0, 0, 0, 10, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MAXIMUM_BARCODES_PER_PAGE, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_BARCODE_SEARCH_DIRECTION + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lBarcodeSearchArray; + lBarcodeSearchArray.Append(WIA_BARCODE_AUTO_SEARCH); + lBarcodeSearchArray.Append(WIA_BARCODE_HORIZONTAL_SEARCH); + lBarcodeSearchArray.Append(WIA_BARCODE_VERTICAL_SEARCH); + lBarcodeSearchArray.Append(WIA_BARCODE_HORIZONTAL_VERTICAL_SEARCH); + lBarcodeSearchArray.Append(WIA_BARCODE_VERTICAL_HORIZONTAL_SEARCH); + + hr = PropertyManager.AddProperty(WIA_IPS_BARCODE_SEARCH_DIRECTION, WIA_IPS_BARCODE_SEARCH_DIRECTION_STR, RWL, lBarcodeSearchArray[0], lBarcodeSearchArray[0], &lBarcodeSearchArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_BARCODE_SEARCH_DIRECTION, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_MAXIMUM_BARCODE_SEARCH_RETRIES + // + // This sample driver pretends to support no retries (range containing only the value 0) + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_MAXIMUM_BARCODE_SEARCH_RETRIES, WIA_IPS_MAXIMUM_BARCODE_SEARCH_RETRIES_STR, RWR, 0, 0, 0, 0, 0); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MAXIMUM_BARCODE_SEARCH_RETRIES, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_BARCODE_SEARCH_TIMEOUT + // + if (SUCCEEDED(hr)) + { + hr = PropertyManager.AddPropertyUL(WIA_IPS_BARCODE_SEARCH_TIMEOUT, WIA_IPS_BARCODE_SEARCH_TIMEOUT_STR, RWR, 0, 0, 0, 100, 10); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_BARCODE_SEARCH_TIMEOUT, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_SUPPORTED_BARCODE_TYPES + // + // This sample driver pretends to support a multitude of barcode types. This driver + // ignores this setting when delivering its hard-coded sample barcodes but uses + // WIA_IPS_SUPPORTED_BARCODE_TYPES to validate WIA_IPS_ENABLED_BARCODE_TYPES + // + if (SUCCEEDED(hr)) + { + ULONG ulBarcodeTypes = ARRAYSIZE(g_lSupportedBarcodeTypes); + + hr = PropertyManager.AddProperty(WIA_IPS_SUPPORTED_BARCODE_TYPES, WIA_IPS_SUPPORTED_BARCODE_TYPES_STR, RN, ulBarcodeTypes, (LONG *)&g_lSupportedBarcodeTypes[0]); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_SUPPORTED_BARCODE_TYPES, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_ENABLED_BARCODE_TYPES + // + // By default when barcode detection is enabled there are 3 barcodes enabled, which + // happen to match the sample, hard-coded, barcode metadata for this driver + // + if (SUCCEEDED(hr)) + { + LONG lDefaultEnabledBarcodeTypes[] = { WIA_BARCODE_UPCA, WIA_BARCODE_CODABAR, WIA_BARCODE_CODE39_FULLASCII }; + ULONG ulBarcodeTypes = ARRAYSIZE(lDefaultEnabledBarcodeTypes); + + hr = PropertyManager.AddProperty(WIA_IPS_ENABLED_BARCODE_TYPES, WIA_IPS_ENABLED_BARCODE_TYPES_STR, RW, ulBarcodeTypes, &lDefaultEnabledBarcodeTypes[0]); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ENABLED_BARCODE_TYPES, hr = 0x%08X", hr)); + } + } + + // + // Apply the property changes to the current session's Application Item Tree: + // + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.SetItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties failed to set WIA item properties for the barcode reader item, hr = 0x%08X", hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Initializes the properties specific to the Patch Code Reader item. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializePatchCodeReaderProperties( + _In_ BYTE* pWiasContext) +{ + HRESULT hr = S_OK; + CWIAPropertyManager PropertyManager; + + if (!pWiasContext) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::InitializePatchCodeReaderProperties, invalid parameter, hr = 0x%08X", hr)); + } + + WIAEX_TRACE_BEGIN; + + // + // WIA_IPS_PATCH_CODE_READER + // + // This sample driver pretends to support a patch code reader device installed + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lPatchCodeReaderArray; + lPatchCodeReaderArray.Append(WIA_PATCH_CODE_READER_DISABLED); + lPatchCodeReaderArray.Append(WIA_PATCH_CODE_READER_AUTO); + + hr = PropertyManager.AddProperty(WIA_IPS_PATCH_CODE_READER, WIA_IPS_PATCH_CODE_READER_STR, RWL, lPatchCodeReaderArray[0], lPatchCodeReaderArray[0], &lPatchCodeReaderArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_PATCH_CODE_READER, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_SUPPORTED_PATCH_CODE_TYPES + // + // This sample driver pretends to support a multitude of barcode types. This driver + // ignores this setting when delivering its hard-coded sample barcodes but uses + // WIA_IPS_SUPPORTED_PATCH_CODE_TYPES to validate WIA_IPS_ENABLED_PATCH_CODE_TYPES + // + if (SUCCEEDED(hr)) + { + ULONG ulPatchCodeTypes = ARRAYSIZE(g_lSupportedPatchCodeTypes); + + hr = PropertyManager.AddProperty(WIA_IPS_SUPPORTED_PATCH_CODE_TYPES, WIA_IPS_SUPPORTED_PATCH_CODE_TYPES_STR, RN, ulPatchCodeTypes, (LONG *)&g_lSupportedPatchCodeTypes[0]); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_SUPPORTED_PATCH_CODE_TYPES, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_ENABLED_PATCH_CODE_TYPES + // + // By default when barcode detection is enabled there are 2 patch codes enabled, which + // happen to match the sample, hard-coded, patch code metadata for this driver + // + if (SUCCEEDED(hr)) + { + LONG lDefaultEnabledPatchCodeTypes[] = { WIA_PATCH_CODE_2, WIA_PATCH_CODE_3 }; + ULONG ulPatchCodeTypes = ARRAYSIZE(lDefaultEnabledPatchCodeTypes); + + hr = PropertyManager.AddProperty(WIA_IPS_ENABLED_PATCH_CODE_TYPES, WIA_IPS_ENABLED_PATCH_CODE_TYPES_STR, RW, ulPatchCodeTypes, &lDefaultEnabledPatchCodeTypes[0]); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ENABLED_PATCH_CODE_TYPES, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPS_ALARM + // + // This sample driver does pretend to support one kind of audible alarm (beep) to signal + // when a path code is successfully detected + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lAlarmArray; + lAlarmArray.Append(WIA_ALARM_NONE); + lAlarmArray.Append(WIA_ALARM_BEEP1); + + hr = PropertyManager.AddProperty(WIA_IPS_ALARM, WIA_IPS_ALARM_STR, RWL, lAlarmArray[0], lAlarmArray[0], &lAlarmArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_ALARM, hr = 0x%08X", hr)); + } + } + + // + // Apply the property changes to the current session's Application Item Tree: + // + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.SetItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties failed to set WIA item properties for the patch code reader item, hr = 0x%08X", hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Initializes the properties specific to the MICR Reader item. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeMicrReaderProperties( + _In_ BYTE* pWiasContext) +{ + HRESULT hr = S_OK; + CWIAPropertyManager PropertyManager; + + if (!pWiasContext) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::InitializeMicrReaderProperties, invalid parameter, hr = 0x%08X", hr)); + } + + WIAEX_TRACE_BEGIN; + + // + // WIA_IPS_MICR_READER + // + // This sample driver pretends to support a MICR reader device installed on the front feeder side + // + if (SUCCEEDED(hr)) + { + CBasicDynamicArray<LONG> lMICRReaderArray; + lMICRReaderArray.Append(WIA_MICR_READER_DISABLED); + lMICRReaderArray.Append(WIA_MICR_READER_AUTO); + lMICRReaderArray.Append(WIA_MICR_READER_FEEDER_FRONT); + + hr = PropertyManager.AddProperty(WIA_IPS_MICR_READER, WIA_IPS_MICR_READER_STR, RWL, lMICRReaderArray[0], lMICRReaderArray[0], &lMICRReaderArray); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_IPS_MICR_READER, hr = 0x%08X", hr)); + } + } + + // + // Apply the property changes to the current session's Application Item Tree: + // + + if (SUCCEEDED(hr)) + { + hr = PropertyManager.SetItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties failed to set WIA item properties for the MICR reader item, hr = 0x%08X", hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Initializes WIA_FORMAT_INFO arrays needed for IWiaMiniDrv::drvGetWiaFormatInfo +* +* Parameters: +* +* None +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeFormatInfoArrays() +{ + HRESULT hr = S_OK; + WIA_FORMAT_INFO tFormatInfo = {}; + + // + // This sample driver supports the same WIA_FORMAT_INFO array for Root, Flatbed, Feeder and Auto items: + // + // { WiaImgFmt_BMP, TYMED_FILE } + // { WiaImgFmt_EXIF, TYMED_FILE } + // { WiaImgFmt_RAW, TYMED_FILE } + // + + m_tFormatInfo.Destroy(); + + tFormatInfo.guidFormatID = WiaImgFmt_BMP; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfo.Append(tFormatInfo); + + tFormatInfo.guidFormatID = WiaImgFmt_EXIF; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfo.Append(tFormatInfo); + + tFormatInfo.guidFormatID = WiaImgFmt_RAW; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfo.Append(tFormatInfo); + + // + // The Imprinter and Endorser items support: + // + // { WiaImgFmt_CSV, TYMED_FILE } + // { WiaImgFmt_TXT, TYMED_FILE } + // { WiaImgFmt_BMP, TYMED_FILE } + // + + m_tFormatInfoImprinterEndorser.Destroy(); + + tFormatInfo.guidFormatID = WiaImgFmt_CSV; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoImprinterEndorser.Append(tFormatInfo); + + tFormatInfo.guidFormatID = WiaImgFmt_TXT; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoImprinterEndorser.Append(tFormatInfo); + + tFormatInfo.guidFormatID = WiaImgFmt_BMP; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoImprinterEndorser.Append(tFormatInfo); + + // + // The Barcode Reader item supports: + // + // { WiaImgFmt_XMLBAR, TYMED_FILE } + // { WiaImgFmt_RAWBAR, TYMED_FILE } + // + + m_tFormatInfoBarcodeReader.Destroy(); + + tFormatInfo.guidFormatID = WiaImgFmt_XMLBAR; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoBarcodeReader.Append(tFormatInfo); + + tFormatInfo.guidFormatID = WiaImgFmt_RAWBAR; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoBarcodeReader.Append(tFormatInfo); + + // + // The Patch Code Reader item supports: + // + // { WiaImgFmt_XMLPAT, TYMED_FILE } + // { WiaImgFmt_RAWPAT, TYMED_FILE } + // + + m_tFormatInfoPatchCodeReader.Destroy(); + + tFormatInfo.guidFormatID = WiaImgFmt_XMLPAT; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoPatchCodeReader.Append(tFormatInfo); + + tFormatInfo.guidFormatID = WiaImgFmt_RAWPAT; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoPatchCodeReader.Append(tFormatInfo); + + // + // The MICR Reader item supports: + // + // { WiaImgFmt_XMLMIC, TYMED_FILE } + // { WiaImgFmt_RAWMIC, TYMED_FILE } + // + + m_tFormatInfoMicrReader.Destroy(); + + tFormatInfo.guidFormatID = WiaImgFmt_XMLMIC; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoMicrReader.Append(tFormatInfo); + + tFormatInfo.guidFormatID = WiaImgFmt_RAWMIC; + tFormatInfo.lTymed = TYMED_FILE; + m_tFormatInfoMicrReader.Append(tFormatInfo); + + return hr; +} + +/**************************************************************************\ +* +* Updates the following image information properties in auto-detect color mode. +* Because this sample driver does not support cropping (scan region changes) +* and supports only a single/fixed scan resolution this function is not needed +* to be executed in other situations: +* +* WIA_IPA_PIXELS_PER_LINE +* WIA_IPA_NUMBER_OF_LINES +* WIA_IPA_BYTES_PER_LINE +* WIA_IPA_CHANNELS_PER_PIXEL +* WIA_IPA_RAW_CHANNELS_PER_PIXEL +* +* Note that these properties are not implemented/available on the Auto item. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lDataType - data type; if set to WIA_DATA_AUTO the +* function reads the current WIA_IPA_DATATYPE. +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::UpdateImageInfoProperties( + _In_ BYTE *pWiasContext, + LONG lDataType) +{ + HRESULT hr = S_OK; + GUID guidItemCategory = WIA_CATEGORY_ROOT; + LONG lDepth = 8; + LONG lChannelsPerPixel = 1; + LONG lCompression = WIA_COMPRESSION_NONE; + GUID guidFormat = WiaImgFmt_UNDEFINED; + LONG lXExtent = 0; + LONG lYExtent = 0; + LONG lPixelsPerLine = 0; + LONG lNumberOfLines = 0; + LONG lBytesPerLine = 0; + + WIAEX_TRACE_BEGIN; + + if (!pWiasContext) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::UpdateImageInfoProperties, invalid parameter, hr = 0x%08X", hr)); + } + + // + // Read WIA_IPA_ITEM_CATEGORY to identify the current item: + // + if (SUCCEEDED(hr)) + { + hr = wiasReadPropGuid(pWiasContext, WIA_IPA_ITEM_CATEGORY, &guidItemCategory, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_ITEM_CATEGORY, hr = 0x%08X", hr)); + } + } + + // + // Cannot validate image information properties on the Root or Auto items: + // + if (SUCCEEDED(hr) && ((IsEqualGUID(WIA_CATEGORY_ROOT, guidItemCategory)) || + (IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory)))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Functionality not supported on this item, hr = 0x%08X", hr)); + } + + // + // If a data type is not specified, read WIA_IPA_DATATYPE: + // + if (SUCCEEDED(hr)) + { + if (WIA_DATA_AUTO == lDataType) + { + LONG lActualDataType = WIA_DATA_GRAYSCALE; + + hr = wiasReadPropLong(pWiasContext, WIA_IPA_DATATYPE, &lActualDataType, NULL, TRUE); + if (SUCCEEDED(hr)) + { + if (WIA_DATA_AUTO == lActualDataType) + { + WIAEX_ERROR((g_hInst, "Unspecified data type! Considering 8-bpp grayscale (default) and trying to continue")); + lDepth = 8; + } + else + { + lDepth = (WIA_DATA_COLOR == lActualDataType) ? 24 : 8; + } + } + else + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_DATA_TYPE, hr = 0x%08X", hr)); + } + } + else + { + lDepth = (WIA_DATA_COLOR == lDataType) ? 24 : 8; + } + } + + // + // Check the current WIA_IPA_COMPRESSION: + // + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPA_COMPRESSION, &lCompression, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_COMPRESSION, hr = 0x%08X", hr)); + } + } + + // + // Check the current WIA_IPA_FORMAT: + // + if (SUCCEEDED(hr)) + { + hr = wiasReadPropGuid(pWiasContext, WIA_IPA_FORMAT, &guidFormat, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_FORMAT, hr = 0x%08X", hr)); + } + } + + // + // Check the current WIA_IPS_XEXTENT and WIA_IPS_YTEXTENT: + // + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_XEXTENT, &lXExtent, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_XEXTENT, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_YEXTENT, &lYExtent, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_YEXTENT, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + // + // The number of bytes per line include the padding necessary to make each uncompressed + // line (DIB or Raw) DWORD aligned. When data is compressed the number of bytes per line + // calculated here reflects the original uncompressed image: + // + lPixelsPerLine = lXExtent; + lNumberOfLines = lYExtent; + lBytesPerLine = BytesPerLine(lPixelsPerLine, lDepth); + + WIAS_TRACE((g_hInst, "Image information: %u PPL, %u lines, %u BPL (compression: %u)", + lPixelsPerLine, lNumberOfLines, lBytesPerLine, lCompression)); + } + + // + // Update the following properties, supported for backwards compatibility (with WIA 1.0 + // and TWAIN) on the Flatbed and Feeder item but not on the new Auto item: + // + // WIA_IPA_PIXELS_PER_LINE - the image width, in pixels, for the final image + // WIA_IPA_NUMBER_OF_LINES - the image length, in pixels, for the final image + // WIA_IPA_BYTES_PER_LINE - line width in bytes that must match WIA_IPA_PIXELS_PER_LINE and WIA_IPA_DEPTH + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPA_PIXELS_PER_LINE, lPixelsPerLine); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_PIXELS_PER_LINE, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPA_NUMBER_OF_LINES, lNumberOfLines); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_NUMBER_OF_LINES, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPA_BYTES_PER_LINE, lBytesPerLine); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_BYTES_PER_LINE, hr = 0x%08X", hr)); + } + } + + // + // Update WIA_IPA_CHANNELS_PER_PIXEL and WIA_IPA_RAW_BITS_PER_CHANNEL top match the bit depth. + // Note that this saple driver does not need to update WIA_IPA_BITS_PER_CHANNEL, and that + // WIA_IPA_DEPTH and WIA_IPA_DATA_TYPE are updated during validation (see ValidateFormatProperties): + // + + if (SUCCEEDED(hr)) + { + lChannelsPerPixel = (24 == lDepth) ? 3 : 1; + hr = wiasWritePropLong(pWiasContext, WIA_IPA_CHANNELS_PER_PIXEL, lChannelsPerPixel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_CHANNELS_PER_PIXEL, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + BYTE bRawBitsPerChannel[3] = {}; + + for (int i = 0; i < lChannelsPerPixel; i++) + { + bRawBitsPerChannel[i] = 8; + } + + hr = wiasWritePropBin(pWiasContext, WIA_IPA_RAW_BITS_PER_CHANNEL, lChannelsPerPixel, bRawBitsPerChannel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_RAW_BITS_PER_CHANNEL, hr = 0x%08X", hr)); + } + } + + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Updates the globally stored (per driver instance) scan available item +* name indicating that this item is marked for data transfer due to a +* device initiated scan operation, and/or the scanner is not in a scan +* available state. +* +* Parameters: +* +* wszInputSource - a WIA item name as the name of the input source, +* NULL to reset the value to an empty string. +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::UpdateScanAvailableItemName( + _In_opt_ LPCWSTR wszInputSource) +{ + HRESULT hr = S_OK; + + // + // Clear the current name: + // + if (m_bstrScanAvailableItem) + { + SysFreeString(m_bstrScanAvailableItem); + m_bstrScanAvailableItem = NULL; + } + + // + // Identify the input source and prepare the value containing the apropriate item name: + // + if (wszInputSource) + { + if (!wcscmp(WIA_DRIVER_FEEDER_NAME, wszInputSource)) + { + WIAS_TRACE((g_hInst, "Scan available from feeder (%ws)", wszInputSource)); + m_bstrScanAvailableItem = SysAllocString(WIA_DRIVER_FEEDER_NAME); + } + else if (!wcscmp(WIA_DRIVER_FLATBED_NAME, wszInputSource)) + { + WIAS_TRACE((g_hInst, "Scan available from flatbed (%ws)", wszInputSource)); + m_bstrScanAvailableItem = SysAllocString(WIA_DRIVER_FLATBED_NAME); + } + else + { + WIAS_TRACE((g_hInst, "Scan available from unknown input source (%ws) - information not recorded", + wszInputSource)); + m_bstrScanAvailableItem = SysAllocString(L""); + } + } + else + { + // + // A trace message is avoided here on purpose - the following message, if enabled, + // would be visible both when initializing a new AIT Root with no scan available + // input source recorded -and- when resetting the scan available soure information + // from within IWiaMiniDrv::drvAcquireItemData before executing a new scan job. + // + m_bstrScanAvailableItem = SysAllocString(L""); + } + + if (!m_bstrScanAvailableItem) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to update the scan available item name, out of memory, hr = 0x%08X", hr)); + } + + return hr; +} + +/**************************************************************************\ +* +* Updates the WIA_DPS_SCAN_AVAILABLE_ITEM property at real-time when an +* application attempts to read a property from its AIT Root item. We cannot +* make this update immediately following a notification that a device initiated +* scan is available because the event comes globally, outside of any application +* session context (possibly at a time when no application session exists). +* +* WARNING: must not be called on other items than Root (WiaItemTypeRoot). +* +* Parameters: +* +* pWiasContext - pointer to the item context +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::UpdateScanAvailableItemProperty( + _In_ BYTE *pWiasContext) +{ + HRESULT hr = S_OK; + + if (!pWiasContext) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // If no value is initialized yet, initialize it now to an + // empty string meaning "no scan available source recorded": + // + if (SUCCEEDED(hr) && (!m_bstrScanAvailableItem)) + { + m_bstrScanAvailableItem = SysAllocString(L""); + if (!m_bstrScanAvailableItem) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to initialize an empty string in lieu of a scan available item name, hr = 0x%08X", hr)); + } + } + + // + // Update the WIA_DPS_SCAN_AVAILABLE_ITEM property: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropStr(pWiasContext, WIA_DPS_SCAN_AVAILABLE_ITEM, m_bstrScanAvailableItem); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "wiasWritePropStr(WIA_DPS_SCAN_AVAILABLE_ITEM, '%ws') failed, hr = 0x%08X", m_bstrScanAvailableItem, hr)); + } + } + + return hr; +} + +/**************************************************************************\ +* +* Retrieves standard WIA page sizes that fit in the specified scan area +* dimensions considering the specified orientation. +* +* Parameters: +* +* lMaxWidth - maximum width of the total scan area, in 1/1000" +* lMaxHeight - maximum height of the total scan area, in 1/1000" +* lMinWidth - minimum width of the total scan area, in 1/1000" +* lMinHeight - minimum height of the total scan area, in 1/1000" +* bPortrait - TRUE for portrait, FALSE for landscape orientation +* arrayPageSizes - reference for array where to return page sizes +* +* Return Value: +* +* The number of page sizes found +* +\**************************************************************************/ + +LONG CWiaDriver::GetValidPageSizes( + LONG lMaxWidth, + LONG lMaxHeight, + LONG lMinWidth, + LONG lMinHeight, + BOOL bPortrait, + CBasicDynamicArray<LONG>& arrayPageSizes) +{ + LONG lNumPageSizesFound = 0; + LONG lNumKnownPageSizes = sizeof(g_DefinedPageSizeCombinations) / sizeof(g_DefinedPageSizeCombinations[0]); + + // + // Do not erase page sizes already added to the array, + // append new values that are not yet in the array: + // + // arrayPageSizes.Destroy(); + // + + for (LONG i = 0; i < lNumKnownPageSizes; i++) + { + if (bPortrait) + { + if ((g_DefinedPageSizeCombinations[i].m_lPageWidth <= lMaxWidth) && + (g_DefinedPageSizeCombinations[i].m_lPageHeight <= lMaxHeight) && + (g_DefinedPageSizeCombinations[i].m_lPageWidth >= lMinWidth) && + (g_DefinedPageSizeCombinations[i].m_lPageHeight >= lMinHeight)) + { + if (-1 == arrayPageSizes.Find(g_DefinedPageSizeCombinations[i].m_lPageSize)) + { + arrayPageSizes.Append(g_DefinedPageSizeCombinations[i].m_lPageSize); + } + } + } + else + { + if ((g_DefinedPageSizeCombinations[i].m_lPageWidth <= lMaxHeight) && + (g_DefinedPageSizeCombinations[i].m_lPageHeight <= lMaxWidth) && + (g_DefinedPageSizeCombinations[i].m_lPageWidth >= lMinHeight) && + (g_DefinedPageSizeCombinations[i].m_lPageHeight >= lMinWidth)) + { + if (-1 == arrayPageSizes.Find(g_DefinedPageSizeCombinations[i].m_lPageSize)) + { + arrayPageSizes.Append(g_DefinedPageSizeCombinations[i].m_lPageSize); + } + } + } + } + + lNumPageSizesFound = arrayPageSizes.Size(); + + return lNumPageSizesFound; +} + +/**************************************************************************\ +* +* Returns the width and height for the specified standard page size, +* in 1/1000", according with the indicated orientation +* +* Parameters: +* +* lPageSize - the page size to return the dimensions for +* bPortrait - TRUE for portait orientation, FALSE for landscape +* lPageWidth - reference for variable to receive the page width +* lPageHeight - reference for variable to receive the page height +* Return Value: +* +* S_OK if successful, E_INVALIDARG if no such standard page size is found +* +\**************************************************************************/ + +HRESULT CWiaDriver::GetPageDimensions( + LONG lPageSize, + BOOL bPortrait, + LONG& lPageWidth, + LONG& lPageHeight) +{ + HRESULT hr = E_INVALIDARG; + LONG lNumKnownPageSizes = sizeof(g_DefinedPageSizeCombinations) / sizeof(g_DefinedPageSizeCombinations[0]); + + for (LONG i = 0; i < lNumKnownPageSizes; i++) + { + if (g_DefinedPageSizeCombinations[i].m_lPageSize == lPageSize) + { + if (bPortrait) + { + lPageWidth = g_DefinedPageSizeCombinations[i].m_lPageWidth; + lPageHeight = g_DefinedPageSizeCombinations[i].m_lPageHeight; + } + else + { + lPageWidth = g_DefinedPageSizeCombinations[i].m_lPageHeight; + lPageHeight = g_DefinedPageSizeCombinations[i].m_lPageWidth; + } + + hr = S_OK; + break; + } + } + + return hr; +} diff --git a/wia/ProdScan/MiniDrv.cpp b/wia/ProdScan/MiniDrv.cpp new file mode 100644 index 00000000..3a00291b --- /dev/null +++ b/wia/ProdScan/MiniDrv.cpp @@ -0,0 +1,2619 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* Title: MiniDrv.cpp +* +* Description: This file contains the IWiaMiniDrv interface implementation +* for the Production Scanner Driver Sample, plus C++ constructor and +* destructor code for the main driver's objects, CWiaDriver. +* +***************************************************************************/ + +#include "stdafx.h" + +HINSTANCE g_hInst = NULL; + +/**************************************************************************\ +* +* CWiaDriver constructor +* +\**************************************************************************/ + +CWiaDriver::CWiaDriver( + _In_opt_ LPUNKNOWN punkOuter) : + m_cRef(1), + m_punkOuter(NULL), + m_pIDrvItemRoot(NULL), + m_lClientsConnected(0), + m_bstrDeviceID(NULL), + m_bstrRootFullItemName(NULL), + m_pIStiDevice(NULL), + m_hDeviceKey(NULL), + m_bFeederStarted(FALSE) +{ + // + // See if we are aggregated. If we are (almost always the case) + // save the pointer to the controlling IUnknown, so subsequent + // calls will be delegated. If not, set the same pointer to "this": + // + if (punkOuter) + { + m_punkOuter = punkOuter; + } + else + { + // + // This cast is needed in order to point to right virtual table: + // + m_punkOuter = reinterpret_cast<IUnknown*>(static_cast<INonDelegatingUnknown*>(this)); + } + + memset(m_wszDevicePath, 0, sizeof(m_wszDevicePath)); + + + // + // Warning: do not initialize the entire contents of m_config and m_status to 0, + // this will erase the function pointer tables for the CBasicDynamicArray members! + // + + m_hrLastEdviceError = STI_ERROR_NO_ERROR; + + m_hWiaEvent = NULL; + m_hWiaEventStoredCopy = NULL; + m_bstrScanAvailableItem = NULL; + + // + // IStiUSD::Initialize not executed yet: + // + m_bInitialized = FALSE; + + // + // Initialize the critical section for DestroyDriverItemTree: + // + InitializeCriticalSection(&m_csDestroyDriverItemTree); + + WIAS_TRACE((g_hInst, "Driver object (%p, process: %u) created", this, GetCurrentProcessId())); +} + +/**************************************************************************\ +* +* CWiaDriver destructor +* +\**************************************************************************/ + +CWiaDriver::~CWiaDriver() +{ + DWORD dwProcessId = GetCurrentProcessId(); + DWORD dwThreadId = GetCurrentThreadId(); + + WIAS_TRACE((g_hInst, "Destroying driver object (%p, process: %u, thread: %u)..", + this, dwProcessId, dwThreadId)); + + // + // Free the memory allocated for the global device ID and root item name: + // + if (m_bstrDeviceID) + { + SysFreeString(m_bstrDeviceID); + m_bstrDeviceID = NULL; + } + + if (m_bstrRootFullItemName) + { + SysFreeString(m_bstrRootFullItemName); + m_bstrRootFullItemName = NULL; + } + + // + // Free WIA_FORMAT_INFO arrays: + // + m_tFormatInfo.Destroy(); + m_tFormatInfoImprinterEndorser.Destroy(); + m_tFormatInfoBarcodeReader.Destroy(); + m_tFormatInfoPatchCodeReader.Destroy(); + m_tFormatInfoMicrReader.Destroy(); + + // + // Free cached driver capability array: + // + m_tCapabilityManager.Destroy(); + + // + // Unlink and release the cached IWiaDrvItem root item interface: + // + DestroyDriverItemTree(); + + // + // The driver item tree is destroyed, the critical section can be deleted. + // Make sure there is no concurrent thread releasing the Root item from + // within IWiaMiniDrv::drvUnInitializeWia and delete the critical section: + // + EnterCriticalSection(&m_csDestroyDriverItemTree); + LeaveCriticalSection(&m_csDestroyDriverItemTree); + DeleteCriticalSection(&m_csDestroyDriverItemTree); + + if (m_bstrScanAvailableItem) + { + SysFreeString(m_bstrScanAvailableItem); + m_bstrScanAvailableItem = NULL; + } + + // + // The WIA service may release the driver object during a scanner status update + // operation, for example following an unexpected device disconnect event. + // When this happens we must wait for the critical section to be released before + // deleting it, otherwise the thread owning the CS may remain in an undefined state: + // + + m_bInitialized = FALSE; + + WIAS_TRACE((g_hInst, "Driver object (%p, process: %u, thread: %u) destroyed", this, dwProcessId, dwThreadId)); +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvInitializeWia. Initializes the mini-driver in +* the context of a new WIA application session and creates if needed the +* unique Driver Item Tree describing the item architecture and item names +* that the WIA service will use to create duplicate Application Item Trees +* for each new WIA application session opened with the driver. When the WIA +* service executes this method the driver must receive a character string +* containing the device�s unique identifier along with the IStiDevice COM +* interface pointer describing the current device. The driver must create +* the driver item tree if it hasn�t been built yet. Finally, the driver +* must return back to the WIA service the pointer to the Root item in the +* Driver Item Tree. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* bstrDeviceID - string containing the device's unique identifier +* bstrRootFullItemName - string containing the full name of the root item +* pStiDevice - points to an IStiDevice interface +* pIUnknownOuter - (optional) to receive an IUnknown interface address +* ppIDrvItemRoot - receives the address of the IWiaDrvItem interface +* for the root item +* ppIUnknownInner - unsupported and always set to NULL (all this driver's WIA +* functionality is covered through its IWiaMiniDrv interface) +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvInitializeWia( + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _In_ BSTR bstrDeviceID, + _In_ BSTR bstrRootFullItemName, + _In_ IUnknown* pStiDevice, + _In_ IUnknown* pIUnknownOuter, + _Out_ IWiaDrvItem** ppIDrvItemRoot, + _Out_ IUnknown** ppIUnknownInner, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(pIUnknownOuter); + UNREFERENCED_PARAMETER(lFlags); + + HRESULT hr = S_OK; + + // + // (1/2) Uncomment the code below to enable a basic safety guard against premature + // drvInitializeWia call made by WIA Service before IStiUSD::Initialize completes: + // + // If IWiaMiniDrv::drvInitializeWia is called before IStiUSD::Initialize + // is complete wait up to 1 minute (10 msec x 6000 times) and retry: + // + // const LONG lMaxWaitCycles = 6000; + // const LONG lWaitInterval = 10; + // LONG lWaitCycles = 0; + // + + WIAEX_TRACE_BEGIN; + + if ((!pWiasContext) || (!plDevErrVal)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *plDevErrVal = 0; + *ppIDrvItemRoot = NULL; + *ppIUnknownInner = NULL; + + if (!m_bstrDeviceID) + { + m_bstrDeviceID = SysAllocString(bstrDeviceID); + if (!m_bstrDeviceID) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate BSTR DeviceID string, hr = 0x%08X", hr)); + } + } + } + + if (SUCCEEDED(hr)) + { + if (!m_pIStiDevice) + { + m_pIStiDevice = reinterpret_cast<IStiDevice*>(pStiDevice); + } + + if (!m_bstrRootFullItemName) + { + m_bstrRootFullItemName = SysAllocString(bstrRootFullItemName); + if (!m_bstrRootFullItemName) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate BSTR Root full item name string, hr = 0x%08X", hr)); + } + } + } + + if (SUCCEEDED(hr)) + { + if (!m_pIDrvItemRoot) + { + // + // (2/2) Uncomment the code below to enable a basic safety guard against premature + // drvInitializeWia call made by WIA Service before IStiUSD::Initialize completes: + // + // The WIA service may call IWiaMiniDrv::drvInitializeWia before the + // IStiUSD::Initialize call is completed. Temporarily block creating the Driver Item + // Tree until IStiUSD::Initialize is complete: + // + // if (!m_bInitialized) + // { + // WIAS_TRACE((g_hInst, "Driver not intialized yet, wait..")); + // + // // + // // Wait up to 10 msec x 6000 = 1 minute for IStiUSD::Initialize to complete: + // // + // while ((!m_bInitialized) && ((++lWaitCycles) <= lMaxWaitCycles)) + // { + // Sleep(lWaitInterval); + // } + // + // if (m_bInitialized) + // { + // WIAS_TRACE((g_hInst, "Driver intialized now")); + // } + // else + // { + // WIAEX_ERROR((g_hInst, "Maxmimum timeout reached, driver still not initialized")); + // } + // } + // + + // + // Create the Driver Item Tree matching the current scanner configuration: + // + hr = BuildDriverItemTree(); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the Driver Item Tree, hr = 0x%08X", hr)); + } + } + else + { + // + // The Driver Item Tree already exists. The root item of this item tree + // should be returned to the WIA service: + // + hr = S_OK; + } + } + + // + // Increment the client connection count only when the driver has + // successfully created all the necessary Driver Item Tree items: + // + if (SUCCEEDED(hr)) + { + *ppIDrvItemRoot = m_pIDrvItemRoot; + InterlockedIncrement(&m_lClientsConnected); + WIAS_TRACE((g_hInst,"drvInitializeWia, %d client(s) are currently connected to this driver", m_lClientsConnected)); + } + + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvInitializeWia 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvInitItemProperties. The WIA service builds the +* Application Item Tree and then asks the driver to initialize each item +* executing IWiaMiniDvr::drvInitItemProperties on each before to hand the +* entire tree over to the application (when completing the IWiaDevMgr:: +* CreateDevice or IWiaDevMgr2::CreateDevice call the application makes +* to open a new WIA session). When this method is called the driver is given +* the context of the Application Tree Item to be initialized. The driver must +* populate the item with WIA properties, fully initialized with their names, +* access flags, valid and current values. +* +* This driver supports to create and initialize one of each of the following items: +* +* Root (WIA_CATEGORY_ROOT) +* Flatbed (WIA_CATEGORY_FLATBED, no children) +* Feeder (WIA_CATEGORY_FEEDER, no children) +* Auto (WIA_CATEGORY_AUTO) +* Imprinter (WIA_CATEGORY_IMPRINTER) +* Endorser (WIA_CATEGORY_ENDORSER) +* Barcode Reader (WIA_CATEGORY_BARCODE_READER) +* Patch Code Reader (WIA_CATEGORY_PATCH_CODE_READER) +* MICR Reader (WIA_CATEGORY_MICR_READER) +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvInitItemProperties( + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(lFlags); + + HRESULT hr = S_OK; + LONG lItemFlags = 0; + + WIAEX_TRACE_BEGIN; + + if ((!pWiasContext) || (!plDevErrVal)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *plDevErrVal = 0; + + // + // Read WIA_IPA_ITEM_FLAGS to identify the item to be initialized: + // + hr = wiasReadPropLong(pWiasContext, WIA_IPA_ITEM_FLAGS, &lItemFlags, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to read WIA_IPA_ITEM_FLAGS property, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + if (lItemFlags & WiaItemTypeRoot) + { + // + // This is the Root item, initialize the Root item properties as well as the + // Root mini-driver item context containing the scan destination names: + // + + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for Root..")); + + hr = InitializeRootItemProperties(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize root item properties, hr = 0x%08X", hr)); + } + } + else if ((lItemFlags & WiaItemTypeProgrammableDataSource) && + (lItemFlags & WiaItemTypeTransfer) && + (lItemFlags & WiaItemTypeFile)) + { + // + // This is a child programmable data source item - detect which one from the item name: + // + + IWiaDrvItem *pIWiaDrvItem = NULL; + BSTR bstrItemName = NULL; + + hr = wiasGetDrvItem(pWiasContext, &pIWiaDrvItem); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to retrieve the current driver item to initialize, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + hr = pIWiaDrvItem->GetItemName(&bstrItemName); + if (FAILED (hr)) + { + WIAEX_ERROR((g_hInst, "Failed to get the item name, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + if (!wcscmp(WIA_DRIVER_FLATBED_NAME, bstrItemName)) + { + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for Flatbed..")); + + hr = InitializeChildItemProperties(pWiasContext, FLAT); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the flatbed item's property set, hr = 0x%08X", hr)); + } + } + else if (!wcscmp(WIA_DRIVER_FEEDER_NAME, bstrItemName)) + { + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for Feeder..")); + + hr = InitializeChildItemProperties(pWiasContext, FEED); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the feeder item's property set, hr = 0x%08X", hr)); + } + } + else if (!wcscmp(WIA_DRIVER_AUTO_NAME, bstrItemName)) + { + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for Auto..")); + + hr = InitializeChildItemProperties(pWiasContext, AUTO_SOURCE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the automatic input source item's property set, hr = 0x%08X", hr)); + } + } + else if (!wcscmp(WIA_DRIVER_IMPRINTER_NAME, bstrItemName)) + { + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for Imprinter..")); + + hr = InitializeChildItemProperties(pWiasContext, IMPRINTER); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the imprinter item's property set, hr = 0x%08X", hr)); + } + } + else if (!wcscmp(WIA_DRIVER_ENDORSER_NAME, bstrItemName)) + { + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for Endorser..")); + + hr = InitializeChildItemProperties(pWiasContext, ENDORSER); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the endorser item's property set, hr = 0x%08X", hr)); + } + } + else if (!wcscmp(WIA_DRIVER_BARCODE_READER_NAME, bstrItemName)) + { + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for Barcode Reader..")); + + hr = InitializeChildItemProperties(pWiasContext, BARCODE_READER); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the barcode reader item's property set, hr = 0x%08X", hr)); + } + } + else if (!wcscmp(WIA_DRIVER_PATCH_CODE_READER_NAME, bstrItemName)) + { + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for Patch Code Reader..")); + + hr = InitializeChildItemProperties(pWiasContext, PATCH_CODE_READER); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the patch code reader item's property set, hr = 0x%08X", hr)); + } + } + else if (!wcscmp(WIA_DRIVER_MICR_READER_NAME, bstrItemName)) + { + WIAS_TRACE((g_hInst,"IWiaMiniDrv::drvInitItemProperties called for MICR Reader..")); + + hr = InitializeChildItemProperties(pWiasContext, MICR_READER); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the MICR reader item's property set, hr = 0x%08X", hr)); + } + } + else + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Unsupported item (item name: %ws), hr = 0x%08X", bstrItemName, hr)); + } + + if (bstrItemName) + { + SysFreeString(bstrItemName); + } + } + } + else if (lItemFlags & WiaItemTypeGenerated) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "WiaItemTypeGenerated items are not supported by this driver, hr = 0x%08X", hr)); + } + else + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Unsupported item (item flags: 0x%X), hr = 0x%08X", lItemFlags, hr)); + } + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvInitItemProperties 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvValidateItemProperties. The WIA Service calls +* IWiaMinIDrv::drvValidateItemProperties for properties that an application +* requested to be changed through a IWiaPropertyStorage::WriteMultiple call. +* The driver should validate each individual set request against the set of +* valid property values in the current context and if validation is successful +* it must update all dependent properties. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* nPropSpec - indicates the number of properties in the pPropSpec array +* pPropSpec - list of PROPSPEC elements for the properties to be validated +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvValidateItemProperties( + _Inout_ BYTE *pWiasContext, + LONG lFlags, + ULONG nPropSpec, + _In_reads_(nPropSpec) const PROPSPEC *pPropSpec, + _Out_ LONG *plDevErrVal) +{ + UNREFERENCED_PARAMETER(lFlags); + + HRESULT hr = S_OK; + WIA_PROPERTY_CONTEXT PropertyContext = {}; + PROPID *pPropID = NULL; + BOOL bPropertyContext = FALSE; + LONG lDocumentHandlingSelect = FLAT; + LONG lItemType = 0; + + WIAEX_TRACE_BEGIN; + + if ((!pWiasContext) || (!pPropSpec) || (!plDevErrVal) || (!nPropSpec)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *plDevErrVal = 0; + + hr = wiasGetItemType(pWiasContext, &lItemType); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to get item type, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + if (lItemType & WiaItemTypeRoot) + { + // + // Root item properties for this sample driver do not need any additional validation: + // + hr = S_OK; + } + else + { + GUID guidItemCategory = {}; + + // + // Read WIA_IPA_ITEM_CATEGORY to figure out which child item this is + // (the item names can be also used for this identification purpose, + // and should be used if there is more than one item with the same + // item category): + // + hr = wiasReadPropGuid(pWiasContext, WIA_IPA_ITEM_CATEGORY, &guidItemCategory, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_ITEM_CATEGORY, hr = 0x%08X", hr)); + } + + // + // We need to create an array of property IDs for the properties to be added to the default + // ones existing in the property context to be built with wiasCreatePropContext + // + if (SUCCEEDED(hr)) + { + pPropID = (PROPID*) CoTaskMemAlloc(sizeof(PROPID) * nPropSpec); + if (pPropID) + { + for (ULONG i = 0; i < nPropSpec; i++) + { + pPropID[i] = pPropSpec[i].propid; + } + } + else + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Out of memory, hr = 0x%08X", hr)); + } + } + + // + // Create a propery context for the properties being validated: + // + if (SUCCEEDED(hr)) + { + if (IsEqualGUID(WIA_CATEGORY_FLATBED, guidItemCategory)) + { + lDocumentHandlingSelect = FLAT; + } + else if (IsEqualGUID(WIA_CATEGORY_FEEDER, guidItemCategory)) + { + lDocumentHandlingSelect = FEED; + } + else if (IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory)) + { + lDocumentHandlingSelect = AUTO_SOURCE; + } + else if (IsEqualGUID(WIA_CATEGORY_IMPRINTER, guidItemCategory)) + { + lDocumentHandlingSelect = IMPRINTER; + } + else if (IsEqualGUID(WIA_CATEGORY_ENDORSER, guidItemCategory)) + { + lDocumentHandlingSelect = ENDORSER; + } + else if (IsEqualGUID(WIA_CATEGORY_BARCODE_READER, guidItemCategory)) + { + lDocumentHandlingSelect = BARCODE_READER; + } + else if (IsEqualGUID(WIA_CATEGORY_PATCH_CODE_READER, guidItemCategory)) + { + lDocumentHandlingSelect = PATCH_CODE_READER; + } + else if (IsEqualGUID(WIA_CATEGORY_MICR_READER, guidItemCategory)) + { + lDocumentHandlingSelect = MICR_READER; + } + + hr = wiasCreatePropContext(nPropSpec, (PROPSPEC*)pPropSpec, nPropSpec, pPropID, &PropertyContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create WIA property context to validate %u properties, hr = 0x%08X", nPropSpec, hr)); + } + else + { + bPropertyContext = TRUE; + } + } + + // + // Validate format properties and update as necessary: + // + // WIA_IPA_DATATYPE + // WIA_IPA_DEPTH + // WIA_IPA_CHANNELS_PER_PIXEL + // WIA_IPA_BITS_PER_CHANNEL + // WIA_IPA_FORMAT + // WIA_IPA_FILENAME_EXTENSION + // WIA_IPA_TYMED + // WIA_IPA_COMPRESSION + // + if (SUCCEEDED(hr)) + { + hr = ValidateFormatProperties(pWiasContext, &PropertyContext, lDocumentHandlingSelect); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate format properties, hr = 0x%08X", hr)); + } + } + + if ((FLAT == lDocumentHandlingSelect) || (FEED == lDocumentHandlingSelect)) + { + // + // Validate scan region/document size properties: + // + // WIA_IPS_PAGE_SIZE + // WIA_IPS_ORIENTATION + // WIA_IPS_PAGE_WIDTH + // WIA_IPS_PAGE_HEIGHT + // WIA_IPS_XPOS + // WIA_IPS_YPOS + // WIA_IPS_XEXTENT + // WIA_IPS_YEXTENT + // WIA_IPS_XRES + // WIA_IPS_YRES + // WIA_IPS_XSCALING + // WIA_IPS_YSCALING + // WIA_IPS_LONG_DOCUMENT + // + if (SUCCEEDED(hr)) + { + hr = ValidateRegionProperties(pWiasContext, &PropertyContext, lDocumentHandlingSelect); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate region properties, hr = 0x%08X", hr)); + } + } + + // + // Validate image information properties: + // + // WIA_IPA_PIXELS_PER_LINE + // WIA_IPA_NUMBER_OF_LINES + // WIA_IPA_BYTES_PER_LINE + // + if (SUCCEEDED(hr)) + { + hr = ValidateImageInfoProperties(pWiasContext, &PropertyContext, lDocumentHandlingSelect); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate image information properties, hr = 0x%08X", hr)); + } + } + + // + // Validate color drop properties: + // + // WIA_IPS_COLOR_DROP_RED + // WIA_IPS_COLOR_DROP_GREEN and + // WIA_IPS_COLOR_DROP_BLUE + // + if (SUCCEEDED(hr)) + { + hr = ValidateColorDropProperties(pWiasContext, &PropertyContext, lDocumentHandlingSelect); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate color drop properties, hr = 0x%08X", hr)); + } + } + + // + // Validate other feeder specific properties: + // + // WIA_IPS_DOCUMENT_HANDLING_SELECT + // WIA_IPS_PAGES + // + if (SUCCEEDED(hr) && (FEED == lDocumentHandlingSelect)) + { + hr = ValidateFeedProperties(pWiasContext, &PropertyContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate feeder specific properties, hr = 0x%08X", hr)); + } + } + } + else if ((IMPRINTER == lDocumentHandlingSelect) || (ENDORSER == lDocumentHandlingSelect)) + { + hr = ValidateImprinterEndorserProperties(pWiasContext, &PropertyContext, lDocumentHandlingSelect); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate imprinter/endorser specific properties, hr = 0x%08X", hr)); + } + } + else if (BARCODE_READER == lDocumentHandlingSelect) + { + hr = ValidateBarcodeReaderProperties(pWiasContext, &PropertyContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate barcode reader specific properties, hr = 0x%08X", hr)); + } + } + else if (PATCH_CODE_READER == lDocumentHandlingSelect) + { + hr = ValidatePatchCodeReaderProperties(pWiasContext, &PropertyContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate patch code reader specific properties, hr = 0x%08X", hr)); + } + } + else if (MICR_READER == lDocumentHandlingSelect) + { + hr = ValidateMicrReaderProperties(pWiasContext, &PropertyContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate MICR reader specific properties, hr = 0x%08X", hr)); + } + } + + // + // Validate all changed properties against their (for some of the above properties) updated valid values: + // + if (SUCCEEDED(hr)) + { + hr = wiasValidateItemProperties(pWiasContext, nPropSpec, pPropSpec); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to validate properties using wiasValidateItemProperties, hr = 0x%08X", hr)); + } + } + + // + // Free the property context created, if any: + // + if (bPropertyContext) + { + HRESULT FreePropContextHR = wiasFreePropContext(&PropertyContext); + if (FAILED(FreePropContextHR)) + { + WIAEX_ERROR((g_hInst, "wiasFreePropContext failed, hr = 0x%08X", FreePropContextHR)); + } + } + } + } + + if (pPropID) + { + CoTaskMemFree(pPropID); + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvValidateItemProperties 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvWriteItemProperties. When this method is called +* the driver is given the chance to send to the scanner device the settings +* dictated by the current property values. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* pmdtc - the device transfer context +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvWriteItemProperties( + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _In_ PMINIDRV_TRANSFER_CONTEXT pmdtc, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(lFlags); + + HRESULT hr = S_OK; + LONG lItemType = 0; + + WIAEX_TRACE_BEGIN; + + if ((!pWiasContext) || (!pmdtc) || (!plDevErrVal)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *plDevErrVal = 0; + + hr = wiasGetItemType(pWiasContext, &lItemType); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to get item type, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr) && (lItemType & WiaItemTypeRoot)) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Acquisitions are not supported from the Root item, hr = 0x%08X", hr)); + } + + // + // Apply to the device the scan settings described by the current WIA property configuration. + // Note that this is not the best time to ask the scanner device to validate settings: validation + // should be performed during IWiaMiniDrv/CWiaDriver::drvValidateItemProperties. + // + // ... + // + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvWriteItemProperties 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvReadItemProperties. Reads the device item +* properties. When a client application tries to read a WIA item's properties +* the WIA service will first notify the driver by calling this method. +* The driver should then update any property values that need to be updated +* in real-time from the device every time the application attempts to read +* them (e.g. WIA_DPS_DOCUMENT_HANDLING_STATUS). +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* nPropSpec - number of properties in pPropSpec array +* pPropSpec - list of properties to be read +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvReadItemProperties( + _In_ BYTE* pWiasContext, + LONG lFlags, + ULONG nPropSpec, + _In_ const PROPSPEC* pPropSpec, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(nPropSpec); + UNREFERENCED_PARAMETER(lFlags); + + HRESULT hr = S_OK; + LONG lItemFlags = 0; + + // + // Omitted on pupose, not really usefull without full property information: + // + // WIAEX_TRACE_BEGIN; + // + + if ((!pWiasContext) || (!pPropSpec) || (!plDevErrVal)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *plDevErrVal = 0; + + hr = wiasReadPropLong(pWiasContext, WIA_IPA_ITEM_FLAGS, &lItemFlags, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to read WIA_IPA_ITEM_FLAGS property, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + // + // The following properties require to be updated at run-time: + // + // For the Root item: + // + // WIA_DPS_DOCUMENT_HANDLING_STATUS (*) + // WIA_DPA_CONNECT_STATUS (*) + // WIA_DPS_SCAN_AVAILABLE_ITEM + // + // * - not updated by this sample driver since no HW device connection exists, + // but should be updated by a real scanner driver + // + // For the Flatbed and Feeder items: + // + // None + // + + if (lItemFlags & WiaItemTypeRoot) + { + // + // Update WIA_DPS_SCAN_AVAILABLE_ITEM with the last globally stored + // (per driver instance) item name signaled with an unconsumed scan ready event: + // + hr = UpdateScanAvailableItemProperty(pWiasContext); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update the WIA_DPS_SCAN_AVAILABLE_ITEM property (%ws), hr = 0x%08X", + m_bstrScanAvailableItem ? m_bstrScanAvailableItem : L"<empty string>", hr)); + } + } + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvReadItemProperties 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvLockWiaDevice. The IWiaMiniDrv::drvLockWiaDevice +* method locks the hardware device so that only the current minidriver can +* access it. This sample driver returns S_OK without doing anything special. +* Note that the WIA Service expects this method to succeed for a properly +* installed driver and a working scanner device. See also IStiUSD::LockDevice. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvLockWiaDevice( + _In_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(pWiasContext); + UNREFERENCED_PARAMETER(lFlags); + + *plDevErrVal = 0; + + return S_OK; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvUnLockWiaDevice. The sample driver returns S_OK. +* The WIA Service expects this method to succeed for a properly installed +* driver and a working scanner device). See also IStiUSD::UnlockDevice. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvUnLockWiaDevice( + _In_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(pWiasContext); + UNREFERENCED_PARAMETER(lFlags); + + *plDevErrVal = 0; + + return S_OK; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvAnalyzeItem. This sample driver returns +* E_NOTIMPL as it does not support image item analysis. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* E_NOTIMPL +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvAnalyzeItem( + _In_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(pWiasContext); + UNREFERENCED_PARAMETER(lFlags); + + WIAEX_ERROR((g_hInst, "IWiaMiniDrv::drvAnalyzeItem, this method is not implemented or supported for this driver")); + + m_hrLastEdviceError = STIERR_UNSUPPORTED; + + *plDevErrVal = 0; + + return E_NOTIMPL; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvGetDeviceErrorStr. This driver returns +* E_NOTIMPL because no localized text descriptions of the generic errors +* signaled by IWiaMiniDrv calls are available. +* +* Parameters: +* +* lFlags - reserved (set to 0) +* lDevErrVal - the device error value to be mapped to a string +* ppszDevErrStr - receives the address of a string describing the error +* plDevErr - a status code for this method +* +* Return Value: +* +* E_NOTIMPL +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvGetDeviceErrorStr( + LONG lFlags, + LONG lDevErrVal, + _Out_ LPOLESTR* ppszDevErrStr, + _Out_ LONG* plDevErr) +{ + UNREFERENCED_PARAMETER(lFlags); + UNREFERENCED_PARAMETER(lDevErrVal); + + WIAEX_ERROR((g_hInst, "IWiaMiniDrv::drvGetDeviceErrorStr, this method is not implemented or supported for this driver")); + + if (plDevErr) + { + *plDevErr = WIA_ERROR_INVALID_COMMAND; + } + + if (ppszDevErrStr) + { + *ppszDevErrStr = NULL; + } + + m_hrLastEdviceError = STIERR_UNSUPPORTED; + + return E_NOTIMPL; +} + +/**************************************************************************\ +* +* Helper for CWiaDriver::drvUnInitializeWia. Destroys the Driver Item Tree. +* +* Parameters: +* +* None +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::DestroyDriverItemTree() +{ + HRESULT hr = S_OK; + + WIAEX_TRACE_BEGIN; + + // + // By design the WIA service allows an application to release the Root item at the + // same time as the WIA service releases the WIA driver object following a device + // disconnection. If there are no other applications connected to the driver the + // driver would attempt to unlink and release the Root item concurrently from two + // different threads - one thread executing IWiaMiniDrv::drvUnInitializeWia, the + // other thread executing INonDelegating::NonDelegatingRelease: + // + EnterCriticalSection(&m_csDestroyDriverItemTree); + + if (m_pIDrvItemRoot) + { + WIAS_TRACE((g_hInst,"Unlinking WIA item tree")); + + hr = m_pIDrvItemRoot->UnlinkItemTree(WiaItemTypeDisconnected); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to unlink WIA item tree before being released, hr = 0x%08X", hr)); + } + + // + // Proceed releasing the Root item even if the tree could not be unlinked: + // + + WIAS_TRACE((g_hInst, "Releasing IDrvItemRoot interface")); + + __try + { + m_pIDrvItemRoot->Release(); + } +#pragma prefast(suppress:__WARNING_EXCEPTIONEXECUTEHANDLER, "Note that EXCEPTION_EXECUTE_HANDLER may mask exceptions that may be individually handled otherwise") + __except (EXCEPTION_EXECUTE_HANDLER) + { + hr = WIA_ERROR_ITEM_DELETED; + WIAEX_ERROR((g_hInst, "Exception 0x%08X when calling Release on the Root item, item no longer valid, hr = 0x%08X", + GetExceptionCode(), hr)); + } + + m_pIDrvItemRoot = NULL; + + // + // Keep the current scanner configuration data as well as the transfer format + // information array initialized after it, until the scanner device signals + // that the configuration has been changed or the driver is unloaded. + // + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + LeaveCriticalSection(&m_csDestroyDriverItemTree); + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Helper for CWiaDriver::drvInitializeWia. Creates the Driver Item Tree. +* Called during IWiaMiniDrv::drvInitializeWia when no Driver Item Tree exists +* and also during IWiaMiniDrv::drvDeviceCommand for WIA_CMD_SYNCHRONIZE and +* WIA_CMD_BUILD_DEVICE_TREE. Note that the scanner configuration can be read +* for the first time (in this driver session) during IStiUSD::Initialize. +* +* Parameters: +* +* None +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::BuildDriverItemTree() +{ + HRESULT hr = S_OK; + BSTR bstrRootItemName = NULL; + WIA_DRIVER_ITEM_CONTEXT *pWiaDriverItemContext = NULL; + + // + // All child items implemented by this sample driver except the Auto, Barcode, Patch Code and MICR Reader + // items, support all item flags as the Flatbed and Feeder items do minus the WiaItemTypeImage flag. The + // Imprinter and Endorser items support the same item flags as Flatbed and Feeder, including WiaItemTypeImage, + // since these sample Imprinter and Endorser items support graphics data transfers. + // + const LONG lRootItemFlags = WiaItemTypeFolder | WiaItemTypeDevice | WiaItemTypeRoot; + const LONG lCommonChildItemFlags = WiaItemTypeTransfer | WiaItemTypeFile | WiaItemTypeProgrammableDataSource; + + WIAEX_TRACE_BEGIN; + + // + // The method creates the Driver Item Tree only if it doesn't exist: + // + if (!m_pIDrvItemRoot) + { + WIAS_TRACE((g_hInst, "Building Driver Item Tree....")); + + if (!m_bInitialized) + { + hr = E_UNEXPECTED; + WIAEX_ERROR((g_hInst, "Driver not fully initialized, cannot create Driver Item Tree, hr = 0x%08X", hr)); + } + + // + // Reinitialize the WIA_FORMAT_INFO arrays: + // + if (SUCCEEDED(hr)) + { + hr = InitializeFormatInfoArrays(); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_FORMAT_INFO array, hr = 0x%08X", hr)); + } + } + + // + // Create the default WIA root item. Note that we need item context data for the root item as well as children: + // + + if (SUCCEEDED(hr)) + { + bstrRootItemName = SysAllocString(WIA_DRIVER_ROOT_NAME); + if (!bstrRootItemName) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for the root item name, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasCreateDrvItem(lRootItemFlags, bstrRootItemName, m_bstrRootFullItemName, + (IWiaMiniDrv*)this, sizeof(WIA_DRIVER_ITEM_CONTEXT), + (BYTE **)&pWiaDriverItemContext, &m_pIDrvItemRoot); + if (SUCCEEDED(hr) && ((!pWiaDriverItemContext) || (!m_pIDrvItemRoot))) + { + hr = E_POINTER; + } + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the WIA root item (hr = 0x%08X)", hr)); + } + } + + // + // Initialize the item context data for the root item: + // + if (SUCCEEDED(hr)) + { + memset(pWiaDriverItemContext, 0, sizeof(WIA_DRIVER_ITEM_CONTEXT)); + + // + // The Root item is not using this image cache as it does not allow uploads (or download transfers): + // + pWiaDriverItemContext->m_pUploadedImage = NULL; + } + + // + // Create child items that represent programmable data sources: + // + + if (SUCCEEDED(hr)) + { + hr = CreateWIAChildItem(WIA_DRIVER_FLATBED_NAME, (IWiaMiniDrv*)this, m_pIDrvItemRoot, + WiaItemTypeImage | lCommonChildItemFlags, WIA_CATEGORY_FLATBED, NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the Flatbed item, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = CreateWIAChildItem(WIA_DRIVER_FEEDER_NAME, (IWiaMiniDrv*)this, m_pIDrvItemRoot, + WiaItemTypeImage | lCommonChildItemFlags, WIA_CATEGORY_FEEDER, NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the Feeder item, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = CreateWIAChildItem(WIA_DRIVER_AUTO_NAME, (IWiaMiniDrv*)this, m_pIDrvItemRoot, + lCommonChildItemFlags, WIA_CATEGORY_AUTO, NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the Auto item, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = CreateWIAChildItem(WIA_DRIVER_IMPRINTER_NAME, (IWiaMiniDrv*)this, m_pIDrvItemRoot, + WiaItemTypeImage | lCommonChildItemFlags, WIA_CATEGORY_IMPRINTER, NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the Imprinter item, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = CreateWIAChildItem(WIA_DRIVER_ENDORSER_NAME, (IWiaMiniDrv*)this, m_pIDrvItemRoot, + WiaItemTypeImage | lCommonChildItemFlags, WIA_CATEGORY_ENDORSER, NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the Endorser item, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = CreateWIAChildItem(WIA_DRIVER_BARCODE_READER_NAME, (IWiaMiniDrv*)this, m_pIDrvItemRoot, + lCommonChildItemFlags, WIA_CATEGORY_BARCODE_READER, NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the Barcode Reader item, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = CreateWIAChildItem(WIA_DRIVER_PATCH_CODE_READER_NAME, (IWiaMiniDrv*)this, m_pIDrvItemRoot, + lCommonChildItemFlags, WIA_CATEGORY_PATCH_CODE_READER, NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the Path Code Reader item, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = CreateWIAChildItem(WIA_DRIVER_MICR_READER_NAME, (IWiaMiniDrv*)this, m_pIDrvItemRoot, + lCommonChildItemFlags, WIA_CATEGORY_MICR_READER, NULL); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to create the MICR Reader item, hr = 0x%08X", hr)); + } + } + + if (bstrRootItemName) + { + SysFreeString(bstrRootItemName); + } + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvDeviceCommand. The method IWiaMiniDrv:: +* drvDeviceCommand is called by the WIA service to issue a WIA service +* or application generated command to the driver. The WIA service only +* calls the IWiaMiniDrv::drvDeviceCommand method for a command that the +* driver reports to be supported during IWiaMiniDrv::drvGetCapabilities. +* This driver creates or destroys the Driver Item Tree as requested. +* +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* pguidCommand - WIA command GUID +* ppWiaDrvItem - always set to NULL (this driver does not need to create +* an additional item when a WIA command is executed) +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvDeviceCommand( + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _In_ const GUID* pguidCommand, + _Out_ IWiaDrvItem** ppWiaDrvItem, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(lFlags); + + HRESULT hr = S_OK; + + WIAEX_TRACE_BEGIN; + + if ((!pWiasContext) || (!pguidCommand) || (!plDevErrVal)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // The following commands are supported by this driver: + // + // WIA_CMD_SYNCHRONIZE: deletes and recreates the Driver Item Tree + // WIA_CMD_DELETE_DEVICE_TREE: deletes the Driver Item Tree + // WIA_CMD_BUILD_DEVICE_TREE: creates the Driver Item Tree + // WIA_CMD_START_FEEDER: starts the scanner feeder motor, preparing for scan + // WIA_CMD_STOP_FEEDER: stops the scanner feeder motor + // + if (SUCCEEDED(hr)) + { + *plDevErrVal = 0; + *ppWiaDrvItem = NULL; + + if (IsEqualGUID(WIA_CMD_SYNCHRONIZE, *pguidCommand)) + { + WIAS_TRACE((g_hInst, "WIA_CMD_SYNCHRONIZE")); + + // + // Delete the current Driver Item Tree: + // + hr = DestroyDriverItemTree(); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to delete the current DIT for WIA_CMD_SYNCHRONIZE, hr = 0x%08X", hr)); + } + + // + // Re-create the Driver Item Tree according with the current device configuration: + // + if (SUCCEEDED(hr)) + { + hr = BuildDriverItemTree(); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to re-create the DIT for WIA_CMD_SYNCHRONIZE, hr = 0x%08X", hr)); + } + + // + // Queue tree updated event, regardless of whether BuildDriverItemTree succeeded, + // since we can't guarantee that the tree was left in the same condition: + // + QueueWIAEvent(pWiasContext, WIA_EVENT_TREE_UPDATED); + } + } + else if (IsEqualGUID(WIA_CMD_DELETE_DEVICE_TREE, *pguidCommand)) + { + WIAS_TRACE((g_hInst, "WIA_CMD_DELETE_DEVICE_TREE")); + + if (!m_pIDrvItemRoot) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "WIA_CMD_DELETE_DEVICE_TREE called when no DIT exists, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + hr = DestroyDriverItemTree(); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to delete the current DIT for WIA_CMD_DELETE_DEVICE_TREE, hr = 0x%08X", hr)); + } + } + } + else if (IsEqualGUID(WIA_CMD_BUILD_DEVICE_TREE, *pguidCommand)) + { + WIAS_TRACE((g_hInst, "WIA_CMD_BUILD_DEVICE_TREE")); + + if (m_pIDrvItemRoot) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "WIA_CMD_BUILD_DEVICE_TREE called when DIT already exists, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + hr = BuildDriverItemTree(); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to re-create the DIT for WIA_CMD_BUILD_DEVICE_TREE, hr = 0x%08X", hr)); + } + + // + // Queue tree updated event, regardless ofwhether BuildDriverItemTree succeeded, + // since we can't guarantee that the tree was left in the same condition: + // + QueueWIAEvent(pWiasContext, WIA_EVENT_TREE_UPDATED); + } + } + else if (IsEqualGUID(WIA_CMD_START_FEEDER, *pguidCommand) || IsEqualGUID(WIA_CMD_STOP_FEEDER, *pguidCommand)) + { + GUID guidItemCategory = GUID_NULL; + LONG lFeederMotorControl = WIA_FEEDER_CONTROL_AUTO; + + if (IsEqualGUID(WIA_CMD_START_FEEDER, *pguidCommand)) + { + WIAS_TRACE((g_hInst, "WIA_CMD_START_FEEDER")); + } + else + { + WIAS_TRACE((g_hInst, "WIA_CMD_STOP_FEEDER")); + } + + // + // The feeder motor commands are valid only on the Feeder item: + // + hr = wiasReadPropGuid(pWiasContext, WIA_IPA_ITEM_CATEGORY, &guidItemCategory, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to read WIA_IPA_ITEM_CATEGORY, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr) && (!IsEqualGUID(guidItemCategory, WIA_CATEGORY_FEEDER))) + { + hr = WIA_ERROR_INVALID_COMMAND; + WIAEX_ERROR((g_hInst, "WIA_CMD_START/STOP_FEEDER commands are valid only on the Feeder item, hr = 0x%08X", hr)); + } + + // + // WIA_IPS_FEEDER_CONTROL must be set to WIA_FEEDER_CONTROL_MANUAL: + // + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_FEEDER_CONTROL, &lFeederMotorControl, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to read WIA_IPS_FEEDER_CONTROL property, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr) && (WIA_FEEDER_CONTROL_MANUAL != lFeederMotorControl)) + { + hr = WIA_ERROR_INVALID_COMMAND; + WIAEX_ERROR((g_hInst, "WIA_CMD_START/STOP_FEEDER commands are valid only when WIA_IPS_FEEDER_CONTROL is set to WIA_FEEDER_CONTROL_MANUAL, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + if (IsEqualGUID(WIA_CMD_START_FEEDER, *pguidCommand)) + { + hr = StartFeeder(); + } + else + { + hr = StopFeeder(); + } + } + } + else + { + hr = E_NOTIMPL; + WIAEX_ERROR((g_hInst, "The requested WIA command is not implemented or supported by this driver")); + } + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvDeviceCommand 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvGetCapabilities. The WIA service calls +* IWiaMiniDrv::drvGetCapabilities to obtain a list of hardware command +* capabilities and/or STI/WIA device events supported by the driver. +* +* Parameters: +* +* pWiasContext - pointer to the item context (may be NULL) +* lFlags - reserved (set to 0) +* pcelt - receives the number of elements in the array +* pointed to by the ppCapabilities parameter +* ppCapabilities - receives the address of the first element +* of an array of WIA_DEV_CAP_DRV structures that +* contain the GUIDs of events and commands that +* the device supports +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvGetCapabilities( + _In_opt_ BYTE* pWiasContext, + LONG ulFlags, + _Out_ LONG* pcelt, + _Out_ WIA_DEV_CAP_DRV** ppCapabilities, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(pWiasContext); + UNREFERENCED_PARAMETER(ulFlags); + + HRESULT hr = S_OK; + BOOL bAddCapabilities = FALSE; + BOOL bGetCommands = FALSE; + BOOL bGetEvents = FALSE; + + WIAEX_TRACE_BEGIN; + + // + // Note that pWiasContext may be NULL when the driver signals an event + // before the Driver Item Tree is created and WIA service makes this call. + // It is also unused so we won't verify it: + // + if ((!pcelt) || (!ppCapabilities) || (!plDevErrVal)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *plDevErrVal = 0; + *pcelt = 0; + *ppCapabilities = NULL; + + bAddCapabilities = (BOOL)(!m_tCapabilityManager.GetNumCapabilities()); + } + + // + // Add WIA_EVENT_DEVICE_CONNECTED: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_DEVICE_CONNECTED, IDS_EVENT_DEVICE_CONNECTED_NAME, IDS_EVENT_DEVICE_CONNECTED_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_DEVICE_CONNECTED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_DEVICE_CONNECTED to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_DEVICE_DISCONNECTED: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_DEVICE_DISCONNECTED, IDS_EVENT_DEVICE_DISCONNECTED_NAME, + IDS_EVENT_DEVICE_DISCONNECTED_DESCRIPTION, WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_DEVICE_DISCONNECTED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_DEVICE_DISCONNECTED to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_POWER_SUSPEND: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_POWER_SUSPEND, IDS_EVENT_POWER_SUSPEND_NAME, IDS_EVENT_POWER_SUSPEND_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_DEVICE_DISCONNECTED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_POWER_SUSPEND to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_POWER_RESUME: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_POWER_RESUME, IDS_EVENT_POWER_RESUME_NAME, IDS_EVENT_POWER_RESUME_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_DEVICE_CONNECTED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_POWER_RESUME to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_TREE_UPDATED: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_TREE_UPDATED, IDS_EVENT_TREE_UPDATED_NAME, + IDS_EVENT_TREE_UPDATED_DESCRIPTION, WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_TREE_UPDATED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_TREE_UPDATED to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // The sample driver does not signal at run time any of the events initialized below: + // + // WIA_EVENT_SCAN_IMAGE + // WIA_EVENT_DEVICE_NOT_READY + // WIA_EVENT_DEVICE_READY + // WIA_EVENT_FLATBED_LID_OPEN + // WIA_EVENT_FLATBED_LID_CLOSED + // WIA_EVENT_FEEDER_LOADED + // WIA_EVENT_FEEDER_EMPTIED + // WIA_EVENT_COVER_OPEN + // WIA_EVENT_COVER_CLOSED + // + // To signal one of these events, set the m_hWiaEvent and then WIA will issue a IStiUSD::GetNotificationData + // to receive the GUID of the particular event that is signaled: + // + // if (!SetEvent(m_hWiaEvent)) + // { + // dwErr = ::GetLastError(); + // hr = HRESULT_FROM_WIN32(dwErr); + // if (SUCCEEDED(hr)) + // { + // hr = E_FAIL; + // } + // WIAEX_ERROR((g_hInst, "SetEvent(WiaEvent) failed (0x%08X), hr = 0x%08X", dwErr, hr)); + // } + // + // + + // + // Add WIA_EVENT_SCAN_IMAGE: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_SCAN_IMAGE, IDS_EVENT_SCAN_IMAGE_NAME, IDS_EVENT_SCAN_IMAGE_DESCRIPTION, + WIA_NOTIFICATION_EVENT | WIA_ACTION_EVENT, (LPCWSTR)WIA_ICON_SCAN_BUTTON_PRESS); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_SCAN_IMAGE to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_DEVICE_NOT_READY: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_DEVICE_NOT_READY, IDS_EVENT_DEVICE_NOT_READY_NAME, IDS_EVENT_DEVICE_NOT_READY_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_DEVICE_NOT_READY); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_DEVICE_NOT_READY to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_DEVICE_READY: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_DEVICE_READY, IDS_EVENT_DEVICE_READY_NAME, IDS_EVENT_DEVICE_READY_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_DEVICE_READY); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_DEVICE_READY to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_FLATBED_LID_OPEN: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_FLATBED_LID_OPEN, IDS_EVENT_FLATBED_LID_OPEN_NAME, IDS_EVENT_FLATBED_LID_OPEN_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_FLATBED_LID_OPEN); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_FLATBED_LID_OPEN to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_FLATBED_LID_CLOSED: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_FLATBED_LID_CLOSED, IDS_EVENT_FLATBED_LID_CLOSED_NAME, IDS_EVENT_FLATBED_LID_CLOSED_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_FLATBED_LID_CLOSED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_FLATBED_LID_CLOSED to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_FEEDER_LOADED: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_FEEDER_LOADED, IDS_EVENT_FEEDER_LOADED_NAME, IDS_EVENT_FEEDER_LOADED_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_FEEDER_LOADED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_FEEDER_LOADED to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_FEEDER_EMPTIED: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_FEEDER_EMPTIED, IDS_EVENT_FEEDER_EMPTIED_NAME, IDS_EVENT_FEEDER_EMPTIED_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_FEEDER_EMPTIED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_FEEDER_EMPTIED to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_COVER_OPEN: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_COVER_OPEN, IDS_EVENT_COVER_OPEN_NAME, IDS_EVENT_COVER_OPEN_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_COVER_OPEN); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_COVER_OPEN to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_EVENT_COVER_CLOSED: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_EVENT_COVER_CLOSED, IDS_EVENT_COVER_CLOSED_NAME, IDS_EVENT_COVER_CLOSED_DESCRIPTION, + WIA_NOTIFICATION_EVENT, (LPCWSTR)WIA_ICON_COVER_CLOSED); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_EVENT_COVER_CLOSED to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_CMD_SYNCRONIZE: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_CMD_SYNCHRONIZE, IDS_CMD_SYNCHRONIZE_NAME, + IDS_CMD_SYNCHRONIZE_DESCRIPTION, 0, (LPCWSTR)WIA_ICON_SYNCHRONIZE); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_CMD_SYNCHRONIZE to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_CMD_DELETE_DEVICE_TREE: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_CMD_DELETE_DEVICE_TREE, IDS_CMD_DELETE_DEVICE_TREE_NAME, + IDS_CMD_DELETE_DEVICE_TREE_DESCRIPTION, 0, (LPCWSTR)WIA_ICON_DELETE_DEVICE_TREE); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_CMD_DELETE_DEVICE_TREE to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_CMD_BUILD_DEVICE_TREE: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_CMD_BUILD_DEVICE_TREE, IDS_CMD_BUILD_DEVICE_TREE_NAME, + IDS_CMD_BUILD_DEVICE_TREE_DESCRIPTION, 0, (LPCWSTR)WIA_ICON_BUILD_DEVICE_TREE); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_CMD_BUILD_DEVICE_TREE to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_CMD_START_FEEDER: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_CMD_START_FEEDER, IDS_CMD_START_FEEDER_NAME, + IDS_CMD_START_FEEDER_DESCRIPTION, 0, (LPCWSTR)WIA_ICON_START_FEEDER); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_CMD_START_FEEDER to the list of capabilities, hr = 0x%08X", hr)); + } + } + + // + // Add WIA_CMD_STOP_FEEDER: + // + if (SUCCEEDED(hr) && bAddCapabilities) + { + hr = m_tCapabilityManager.AddCapability(WIA_CMD_STOP_FEEDER, IDS_CMD_STOP_FEEDER_NAME, + IDS_CMD_STOP_FEEDER_DESCRIPTION, 0, (LPCWSTR)WIA_ICON_STOP_FEEDER); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add WIA_CMD_STOP_FEEDER to the list of capabilities, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + bGetCommands = (BOOL)(WIA_DEVICE_COMMANDS == (ulFlags & WIA_DEVICE_COMMANDS)); + bGetEvents = (BOOL)(WIA_DEVICE_EVENTS == (ulFlags & WIA_DEVICE_EVENTS)); + + if ((bGetCommands) && (bGetEvents)) + { + *ppCapabilities = m_tCapabilityManager.GetCapabilities(); + *pcelt = m_tCapabilityManager.GetNumCapabilities(); + WIAS_TRACE((g_hInst, "drvGetCapabilities, application is asking for Commands and Events, we have %d total capabilities", *pcelt)); + } + else if (bGetCommands) + { + *ppCapabilities = m_tCapabilityManager.GetCommands(); + *pcelt = m_tCapabilityManager.GetNumCommands(); + WIAS_TRACE((g_hInst, "drvGetCapabilities, application is asking for Commands, we have %d", *pcelt)); + } + else if (bGetEvents) + { + *ppCapabilities = m_tCapabilityManager.GetEvents(); + *pcelt = m_tCapabilityManager.GetNumEvents(); + WIAS_TRACE((g_hInst,"drvGetCapabilities, application is asking for Events, we have %d", *pcelt)); + } + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvGetCapabilities 0x%08X (%u events, %u commands)", + hr, m_tCapabilityManager.GetNumEvents(), m_tCapabilityManager.GetNumCommands())); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvDeleteItem. Deletes a driver item. The items +* supported by this sample driver cannot be deleted by the application. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* lFlags - reserved (set to 0) +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* STG_E_ACCESSDENIED to signal access denied (items cannot be deleted) +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvDeleteItem( + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(lFlags); + UNREFERENCED_PARAMETER(pWiasContext); + + HRESULT hr = STG_E_ACCESSDENIED; + + WIAEX_ERROR((g_hInst, "This item cannot be deleted, hr = 0x%08X", hr)); + + m_hrLastEdviceError = hr; + + *plDevErrVal = 0; + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvDeleteItem 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvFreeDrvItemContext. When a driver item is +* deleted, the WIA service frees the driver item context. This method +* informs the driver that the context is ready to be freed. The driver +* must free any memory allocated for the given driver item context. +* The driver won�t support items that can be deleted by the application +* but this method is going to be called by the WIA service the entire +* item tree is released at the end of a session. +* +* Parameters: +* +* lFlags - reserved (set to 0) +* pSpecContext - points to a device-specific context +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvFreeDrvItemContext( + LONG lFlags, + _In_reads_bytes_(sizeof(WIA_DRIVER_ITEM_CONTEXT)) + BYTE *pSpecContext, + _Out_ LONG *plDevErrVal) +{ + UNREFERENCED_PARAMETER(lFlags); + + *plDevErrVal = 0; + + WIA_DRIVER_ITEM_CONTEXT *pWiaDriverItemContext = (WIA_DRIVER_ITEM_CONTEXT *)pSpecContext; + + if (pWiaDriverItemContext && pWiaDriverItemContext->m_pUploadedImage) + { + pWiaDriverItemContext->m_pUploadedImage->Release(); + pWiaDriverItemContext->m_pUploadedImage = NULL; + } + + WIAS_TRACE((g_hInst, "IWiaMiniDrv::drvFreeDrvItemContext 0x%08X", S_OK)); + + // + // This method must not fail + // + + return S_OK; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvGetWiaFormatInfo. This method creates an array +* of WIA_FORMAT_INFO structures that describe the media types and image +* formats that the driver supports. +* +* Parameters: +* +* pWiasContext - points to a device-specific context +* lFlags - reserved (set to 0) +* pcelt - receives the number of items in the ppwfi array +* ppwfi - array of WIA_FORMAT_INFO structures filled on return +* plDevErrVal - always set to 0 by this driver (drvGetDeviceErrorStr unsupported) +* +* Return Value: +* +* S_OK if succeeds, a standard COM error code otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvGetWiaFormatInfo( + _In_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* pcelt, + _Out_ WIA_FORMAT_INFO** ppwfi, + _Out_ LONG* plDevErrVal) +{ + UNREFERENCED_PARAMETER(lFlags); + + HRESULT hr = S_OK; + GUID guidItemCategory = WIA_CATEGORY_ROOT; + LONG lNumFormats = 0; + WIA_FORMAT_INFO *pFormatInfo = NULL; + + WIAEX_TRACE_BEGIN; + + if ((!plDevErrVal) || (!pcelt) || (!ppwfi)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *plDevErrVal = 0; + } + + if (pWiasContext) + { + if (SUCCEEDED(hr)) + { + hr = wiasReadPropGuid(pWiasContext, WIA_IPA_ITEM_CATEGORY, &guidItemCategory, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_ITEM_CATEGORY, hr = 0x%08X", hr)); + } + } + + // + // This method is allowed to be called only on a transfer capable child item: + // + if (SUCCEEDED(hr)) + { + if (IsEqualGUID(WIA_CATEGORY_FEEDER, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for the Feeder..", hr)); + } + else if (IsEqualGUID(WIA_CATEGORY_FLATBED, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for the Flatbed..", hr)); + } + else if (IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for the Auto image source..", hr)); + } + else if (IsEqualGUID(WIA_CATEGORY_IMPRINTER, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for the Imprinter..", hr)); + } + else if (IsEqualGUID(WIA_CATEGORY_ENDORSER, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for the Endorser..", hr)); + } + else if (IsEqualGUID(WIA_CATEGORY_BARCODE_READER, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for the Barcode Reader..", hr)); + } + else if (IsEqualGUID(WIA_CATEGORY_PATCH_CODE_READER, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for the Patch Code Reader..", hr)); + } + else if (IsEqualGUID(WIA_CATEGORY_MICR_READER, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for the MICR Reader..", hr)); + } + else + { + if (IsEqualGUID(WIA_CATEGORY_ROOT, guidItemCategory)) + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for Root..", hr)); + } + else + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for an unknown item, assuming Root..", hr)); + } + guidItemCategory = WIA_CATEGORY_ROOT; + } + } + } + else + { + WIAS_TRACE((g_hInst, "drvGetWiaFormatInfo called for a NULL item context, assuming Root..", hr)); + guidItemCategory = WIA_CATEGORY_ROOT; + } + + // + // Check the number of available formats + // + if (SUCCEEDED(hr)) + { + // + // Note that the format info arrays are initialized on IStiUSD::Initialize + // so they may be available even before the Driver Item Tree is created: + // + if (IsEqualGUID(WIA_CATEGORY_FEEDER, guidItemCategory) || + IsEqualGUID(WIA_CATEGORY_FLATBED, guidItemCategory) || + IsEqualGUID(WIA_CATEGORY_AUTO, guidItemCategory) || + IsEqualGUID(WIA_CATEGORY_ROOT, guidItemCategory)) + { + lNumFormats = m_tFormatInfo.Size(); + pFormatInfo = (WIA_FORMAT_INFO *)m_tFormatInfo.Array(); + } + else if (IsEqualGUID(WIA_CATEGORY_IMPRINTER, guidItemCategory) || + IsEqualGUID(WIA_CATEGORY_ENDORSER, guidItemCategory)) + { + lNumFormats = m_tFormatInfoImprinterEndorser.Size(); + pFormatInfo = (WIA_FORMAT_INFO *)m_tFormatInfoImprinterEndorser.Array(); + } + else if (IsEqualGUID(WIA_CATEGORY_BARCODE_READER, guidItemCategory)) + { + lNumFormats = m_tFormatInfoBarcodeReader.Size(); + pFormatInfo = (WIA_FORMAT_INFO *)m_tFormatInfoBarcodeReader.Array(); + } + else if (IsEqualGUID(WIA_CATEGORY_PATCH_CODE_READER, guidItemCategory)) + { + lNumFormats = m_tFormatInfoPatchCodeReader.Size(); + pFormatInfo = (WIA_FORMAT_INFO *)m_tFormatInfoPatchCodeReader.Array(); + } + else if (IsEqualGUID(WIA_CATEGORY_MICR_READER, guidItemCategory)) + { + lNumFormats = m_tFormatInfoMicrReader.Size(); + pFormatInfo = (WIA_FORMAT_INFO *)m_tFormatInfoMicrReader.Array(); + } + + if ((!lNumFormats) || (!pFormatInfo)) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Unexpected, the format array must be initialized first, hr = 0x%08X", hr)); + } + } + + // + // Return the requested list: + // + if (SUCCEEDED(hr)) + { + *pcelt = lNumFormats; + *ppwfi = pFormatInfo; + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvGetWiaFormatInfo 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvNotifyPnpEvent. The WIA service notifies the +* driver of a supported PnP system event by calling this method. +* A real driver could respond to these events for example by releasing and +* redoing its connection with the Hardware device (and possibly signaling +* to a locally connected device that it can enter or resume power saving itself) +* when the computer goes into and exists stand-by and hibernation. +* +* The driver must check the pEventGUID parameter to determine what event +* is being processed. The events that are processed by the driver are: +* +* WIA_EVENT_POWER_SUSPEND - system is going to suspend/sleep mode +* WIA_EVENT_POWER_RESUME - system is waking up from suspend/sleep mode +* WIA_EVENT_DEVICE_DISCONNECTED - device is disconnected, driver will be unloaded +* WIA_EVENT_CANCEL_IO - pending IO is being cancelled +* +* The driver does not execute any special action on the following events: +* +* WIA_EVENT_DEVICE_CONNECTED (*)- driver initialization is being done on IStiUSD::Initialize +* +* (* - The driver logs a trace message following WIA_EVENT_DEVICE_CONNECTED +* and ensures the current device connection state is correctly recorded) +* +* Parameters: +* +* pEventGUI - GUID identifying the event +* bstrDeviceID - string containing the device's unique identifier +* ulReserved - reserved for system use +* +* Return Value: +* +* S_OK if succeeds, a standard COM error code otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvNotifyPnpEvent( + _In_ const GUID* pEventGUID, + _In_ BSTR bstrDeviceID, + ULONG ulReserved) +{ + UNREFERENCED_PARAMETER(ulReserved); + + HRESULT hr = S_OK; + + WIAEX_TRACE_BEGIN; + + if (!pEventGUID) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + if (bstrDeviceID) + { + WIAS_TRACE((g_hInst, "PnP event notification received for device ID %ws", bstrDeviceID)); + } + + if (WIA_EVENT_POWER_SUSPEND == *pEventGUID) + { + WIAS_TRACE((g_hInst, "WIA_EVENT_POWER_SUSPEND")); + + // + // Disable WIA events: + // + SetNotificationHandle(NULL); + } + else if (WIA_EVENT_POWER_RESUME == *pEventGUID) + { + WIAS_TRACE((g_hInst, "WIA_EVENT_POWER_RESUME")); + + if ((!m_hWiaEventStoredCopy) || (INVALID_HANDLE_VALUE == m_hWiaEventStoredCopy)) + { + hr = E_UNEXPECTED; + WIAEX_ERROR((g_hInst, "Failed to re-enable WIA events for WIA_EVENT_POWER_RESUME, invalid event handle, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + // + // Re-enable WIA events: + // + hr = SetNotificationHandle(m_hWiaEventStoredCopy); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to re-enable WIA events on WIA_EVENT_POWER_RESUME, hr = 0x%08X", hr)); + } + } + } + else if (WIA_EVENT_DEVICE_CONNECTED == *pEventGUID) + { + WIAS_TRACE((g_hInst, "WIA_EVENT_DEVICE_CONNECTED")); + + // + // When the driver receives this event the driver is already loaded and initialized + // and a new driver object is already created. Ensure the correct connection state + // is recorded - note that this is not required as when the driver is reloaded + // SetDeviceConnected(TRUE) is already executed from within the CWiaDriver + // constructor when the new driver object instance is created. + // + } + else if (WIA_EVENT_DEVICE_DISCONNECTED == *pEventGUID) + { + WIAS_TRACE((g_hInst, "WIA_EVENT_DEVICE_DISCONNECTED")); + + // + // The driver is notified that the scanner device is disconnected and that the WIA + // service will soon unload the driver. The device communication interface will be + // uninitialized when the driver will be unloaded. The SetDeviceConnected(FALSE) call + // below will record the current state so when the device communication interface will + // be uninitialized (when the driver object will be released by the WIA service before + // unloading the driver) the driver won't log excessive errors to the WIA trace log + // when device operation requests will fail because the device won't be available): + // + + // + // Disable WIA events: + // + SetNotificationHandle(NULL); + } + else if (WIA_EVENT_CANCEL_IO == *pEventGUID) + { + WIAS_TRACE((g_hInst, "WIA_EVENT_CANCEL_IO")); + } + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvNotifyPnpEvent 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IWiaMiniDrv::drvUnInitializeWia. The WIA service calls the +* IWiaMiniDrv::drvUnInitializeWia method when the resources associated +* with an application item tree are no longer needed. The driver must +* free all resources allocated for this application session. +* +* Parameters: +* +* pWiasContext - points to a device-specific context +* +* Return Value: +* +* S_OK if succeeds, a standard COM error code otherwise +* +\**************************************************************************/ + +HRESULT CWiaDriver::drvUnInitializeWia( + _Inout_ BYTE* pWiasContext) +{ + HRESULT hr = S_OK; + + WIAEX_TRACE_BEGIN; + + // + // Watch out to not prematurely fail this request... we must + // succeed freeing resources when this is called no matter what. + // + + if (InterlockedDecrement(&m_lClientsConnected) < 0) + { + WIAS_TRACE((g_hInst, + "drvUnInitializeWia, the client connection counter decremented below zero. Assuming no clients are currently connected and automatically setting to 0")); + m_lClientsConnected = 0; + } + + WIAS_TRACE((g_hInst, "drvUnInitializeWia, %d client(s) are currently connected to this driver", m_lClientsConnected)); + + if (!m_lClientsConnected) + { + // + // When the last client disconnects, destroy the WIA item tree. + // This should reduce the idle memory foot print of this driver: + // + DestroyDriverItemTree(); + } + + if (!pWiasContext) + { + WIAEX_ERROR((g_hInst, "drvUnInitializeWia called with NULL item context parameter (!!!); still, request completed, hr = 0x%08X", hr)); + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IWiaMiniDrv::drvUnInitializeWia 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Helper for CWiaDriver::drvDeviceCommand.Starts the scanner feeder. +* +* Parameters: +* +* None +* +* Return Value: +* +* S_OK if successful or an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT +CWiaDriver::StartFeeder() +{ + HRESULT hr = S_OK; + + if (!m_bFeederStarted) + { + // + // Start feeder + // + // ... + // + + m_bFeederStarted = TRUE; + WIAS_TRACE((g_hInst, "Feeder started")); + } + + return hr; +} + +/**************************************************************************\ +* +* Helper for CWiaDriver::drvDeviceCommand.Stops the scanner feeder. +* +* Parameters: +* +* None +* +* Return Value: +* +* S_OK if successful or an error HRESULT otherwise +* +\**************************************************************************/ + +HRESULT +CWiaDriver::StopFeeder() +{ + HRESULT hr = S_OK; + + if (m_bFeederStarted) + { + // + // Stop feeder + // + // ... + // + + m_bFeederStarted = FALSE; + WIAS_TRACE((g_hInst, "Feeder stopped")); + } + + return hr; +} diff --git a/wia/ProdScan/MiniDrv.h b/wia/ProdScan/MiniDrv.h new file mode 100644 index 00000000..4c4eb3f1 --- /dev/null +++ b/wia/ProdScan/MiniDrv.h @@ -0,0 +1,611 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: MiniDrv.h +* +* Project: Production Scanner Driver Sample +* +* Description: Contains the declaration for the CWiaDriver class +* implementing the main driver object +* +***************************************************************************/ + +#pragma once + +extern HINSTANCE g_hInst; + +// +// WIA mini-driver item context data, customized for this sample driver: +// +typedef struct _WIA_DRIVER_ITEM_CONTEXT +{ + // + // In-memory stream holding an images optionally uploaded by + // the WIA application to the Imprinter or the Endorser item. + // If such images are not uploaded by the application in the + // current WIA app session, when a image download is requested + // from one of these items, the driver loads its test image + // from resources: + // + IStream* m_pUploadedImage; + +} WIA_DRIVER_ITEM_CONTEXT, *PWIA_DRIVER_ITEM_CONTEXT; + +// +// INonDelegatingUnknown +// + +class INonDelegatingUnknown +{ +public: + virtual STDMETHODIMP + NonDelegatingQueryInterface(REFIID riid,LPVOID *ppvObj) = 0; + + virtual STDMETHODIMP_(ULONG) + NonDelegatingAddRef() = 0; + + virtual STDMETHODIMP_(ULONG) + NonDelegatingRelease() = 0; +}; + +// +// CWiaDriver class implementing INonDelegatingUnknown, +// IStiUSD (STI USD interface) and IWiaMiniDrv (WIA mini-driver interface): +// + +class CWiaDriver : + public INonDelegatingUnknown, + public IStiUSD, + public IWiaMiniDrv +{ +public: + + // + // Construction/Destruction Section + // + + CWiaDriver( + _In_opt_ LPUNKNOWN punkOuter); + + ~CWiaDriver(); + +private: + + // + // WIA driver internals + // + + LONG m_cRef; // Device object reference count. + LPUNKNOWN m_punkOuter; // Pointer to outer unknown. + IStiDevice* m_pIStiDevice; // STI device interface + IWiaDrvItem* m_pIDrvItemRoot; // WIA root item + IWiaLog* m_pIWiaLog; // WIA logging object + LONG m_lClientsConnected; // number of applications connected + BSTR m_bstrDeviceID; // WIA device ID + BSTR m_bstrRootFullItemName; // WIA root item (full item name) + HRESULT m_hrLastEdviceError; // Used for IStiUSD::GetLastError + + // + // Device path received by IStiUSD::Initialize, recorded for late initialization: + // + WCHAR m_wszDevicePath[MAX_PATH]; + + // + // Device Registry key path received by IStiUSD::Initialize, recorded for late initialization: + // + HKEY m_hDeviceKey; + + // + // Supported transfer file format - tymed combinations: + // + CBasicDynamicArray<WIA_FORMAT_INFO> m_tFormatInfo; + CBasicDynamicArray<WIA_FORMAT_INFO> m_tFormatInfoImprinterEndorser; + CBasicDynamicArray<WIA_FORMAT_INFO> m_tFormatInfoBarcodeReader; + CBasicDynamicArray<WIA_FORMAT_INFO> m_tFormatInfoPatchCodeReader; + CBasicDynamicArray<WIA_FORMAT_INFO> m_tFormatInfoMicrReader; + + // + // Driver capabilities (events and commands): + // + CWIACapabilityManager m_tCapabilityManager; + + // + // WIA event handle to signal to the WIA service + // when notifications from scanner device arrive. + // Currently used for scan events only: + // + HANDLE m_hWiaEvent; + HANDLE m_hWiaEventStoredCopy; + + // + // The last input source signaled for an unconsumed + // ScanAvailable event, if any, or an empty string + // otherwise (scanner not in scan available state + // -or- scan available input source unknown). + // When set the value is the format of a WIA item + // name (as reported by WIA_IPA_ITEM_NAME): + // + BSTR m_bstrScanAvailableItem; + + // + // Flag indicating if driver initialization performed + // during IStiUSD::Initialize is complete: + // + BOOL m_bInitialized; + + // + // Critical section to prevent race conditions when calling + // DestroyDriverItemTree from multiple threads (for example + // when the device is disconnected at the same time as the + // only WIA application accessing the driver ends its WIA + // session releasing its Application Item Tree root item): + // + CRITICAL_SECTION m_csDestroyDriverItemTree; + +public: + + // + // IUnknown methods: + // + + STDMETHODIMP + QueryInterface( + REFIID riid, + _COM_Outptr_ LPVOID *ppvObj); + + STDMETHODIMP_(ULONG) + AddRef(); + + STDMETHODIMP_(ULONG) + Release(); + + // + // IStiUSD methods: + // + + STDMETHOD(Initialize)(THIS_ + _In_ PSTIDEVICECONTROL pIStiDevControl, + DWORD dwStiVersion, + _In_ HKEY hParametersKey); + + STDMETHOD(GetCapabilities)(THIS_ + _Out_ PSTI_USD_CAPS pDevCaps); + + STDMETHOD(GetStatus)(THIS_ + _Inout_ PSTI_DEVICE_STATUS pDevStatus); + + STDMETHOD(DeviceReset)(THIS); + + STDMETHOD(Diagnostic)(THIS_ + _Inout_ LPDIAG pBuffer); + + STDMETHOD(Escape)(THIS_ + STI_RAW_CONTROL_CODE EscapeFunction, + _In_reads_bytes_(cbInDataSize) LPVOID lpInData, + DWORD cbInDataSize, + _Out_writes_bytes_(cbOutDataSize) LPVOID pOutData, + DWORD cbOutDataSize, + _Out_ LPDWORD pdwActualData); + + STDMETHOD(GetLastError)(THIS_ + _Out_ LPDWORD pdwLastDeviceError); + + STDMETHOD(LockDevice)(); + + STDMETHOD(UnLockDevice)(); + + STDMETHOD(RawReadData)(THIS_ + _Out_writes_bytes_(*lpdwNumberOfBytes) LPVOID lpBuffer, + _Inout_ LPDWORD lpdwNumberOfBytes, + _In_opt_ LPOVERLAPPED lpOverlapped); + + STDMETHOD(RawWriteData)(THIS_ + _In_reads_bytes_(dwNumberOfBytes) LPVOID lpBuffer, + DWORD dwNumberOfBytes, + _In_opt_ LPOVERLAPPED lpOverlapped); + + STDMETHOD(RawReadCommand)(THIS_ + _Out_writes_bytes_(*lpdwNumberOfBytes) LPVOID lpBuffer, + _Inout_ LPDWORD lpdwNumberOfBytes, + _In_opt_ LPOVERLAPPED lpOverlapped); + + STDMETHOD(RawWriteCommand)(THIS_ + _In_reads_bytes_(dwNumberOfBytes) LPVOID lpBuffer, + DWORD dwNumberOfBytes, + _In_opt_ LPOVERLAPPED lpOverlapped); + + STDMETHOD(SetNotificationHandle)(THIS_ + _In_opt_ HANDLE hEvent); + + STDMETHOD(GetNotificationData)(THIS_ + _Out_ LPSTINOTIFY lpNotify); + + STDMETHOD(GetLastErrorInfo)(THIS_ + _Out_ STI_ERROR_INFO *pLastErrorInfo); + + // + // IWiaMiniDrv methods: + // + + STDMETHOD(drvInitializeWia)(THIS_ + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _In_ BSTR bstrDeviceID, + _In_ BSTR bstrRootFullItemName, + _In_ IUnknown* pStiDevice, + _In_ IUnknown* pIUnknownOuter, + _Out_ IWiaDrvItem** ppIDrvItemRoot, + _Out_ IUnknown** ppIUnknownInner, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvAcquireItemData)(THIS_ + _In_ BYTE* pWiasContext, + LONG lFlags, + _In_ PMINIDRV_TRANSFER_CONTEXT pmdtc, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvInitItemProperties)(THIS_ + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvValidateItemProperties)(THIS_ + _Inout_ BYTE* pWiasContext, + LONG lFlags, + ULONG nPropSpec, + _In_reads_(nPropSpec) + const PROPSPEC *pPropSpec, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvWriteItemProperties)(THIS_ + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _In_ PMINIDRV_TRANSFER_CONTEXT pmdtc, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvReadItemProperties)(THIS_ + _In_ BYTE* pWiasContext, + LONG lFlags, + ULONG nPropSpec, + _In_ const PROPSPEC* pPropSpec, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvLockWiaDevice)(THIS_ + _In_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvUnLockWiaDevice)(THIS_ + _In_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvAnalyzeItem)(THIS_ + _In_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvGetDeviceErrorStr)(THIS_ + LONG lFlags, + LONG lDevErrVal, + _Out_ LPOLESTR* ppszDevErrStr, + _Out_ LONG* plDevErr); + + STDMETHOD(drvDeviceCommand)(THIS_ + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _In_ const GUID* plCommand, + _Out_ IWiaDrvItem** ppWiaDrvItem, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvGetCapabilities)(THIS_ + _In_opt_ BYTE* pWiasContext, + LONG ulFlags, + _Out_ LONG* pcelt, + _Out_ WIA_DEV_CAP_DRV** ppCapabilities, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvDeleteItem)(THIS_ + _Inout_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvFreeDrvItemContext)(THIS_ + LONG lFlags, + _In_reads_bytes_(sizeof(WIA_DRIVER_ITEM_CONTEXT)) + BYTE *pSpecContext, + _Out_ LONG *plDevErrVal); + + STDMETHOD(drvGetWiaFormatInfo)(THIS_ + _In_ BYTE* pWiasContext, + LONG lFlags, + _Out_ LONG* pcelt, + _Out_ WIA_FORMAT_INFO** ppwfi, + _Out_ LONG* plDevErrVal); + + STDMETHOD(drvNotifyPnpEvent)(THIS_ + _In_ const GUID* pEventGUID, + _In_ BSTR bstrDeviceID, + ULONG ulReserved); + + STDMETHOD(drvUnInitializeWia)(THIS_ + _Inout_ BYTE* pWiasContext); + +public: + + // + // INonDelegating Interface Section: + // + + STDMETHODIMP + NonDelegatingQueryInterface( + REFIID riid, + LPVOID* ppvObj); + + STDMETHODIMP_(ULONG) + NonDelegatingAddRef(); + + STDMETHODIMP_(ULONG) + NonDelegatingRelease(); + +private: + + // + // WIA_IPS_PAGE_SIZE valid values, kept in separate arrays for each orientation. + // This sample driver supports a single standard page size and a single orientation + // (portrait) but a real device driver should use all standard page sizes that + // fit into the available physical scan document dimensions for the feeder: + // + CBasicDynamicArray<LONG> m_lPortraitSizesArray; + CBasicDynamicArray<LONG> m_lLandscapeSizesArray; + + // + // Member that keeps track of the scanner's feeder control status: + // + BOOL m_bFeederStarted; + + // + // Mini-driver private methods: + // + + HRESULT InitializeDeviceConnection( + _In_ LPCWSTR wszDevicePath, + _In_ HKEY hDeviceKey); + + HRESULT + BuildDriverItemTree(); + + HRESULT + DestroyDriverItemTree(); + + // + // Property initialization methods: + // + + HRESULT + InitializeRootItemProperties( + _In_ BYTE* pWiasContext); + + HRESULT + InitializeChildItemProperties( + _In_ BYTE* pWiasContext, + UINT nDocumentHandlingSelect); + + HRESULT + InitializeCommonChildProperties( + _In_ BYTE* pWiasContext, + UINT nDocumentHandlingSelect); + + HRESULT + InitializeFlatbedFeederProperties( + _In_ BYTE* pWiasContext, + UINT nDocumentHandlingSelect); + + HRESULT + InitializeFeederSpecificProperties( + _In_ BYTE* pWiasContext); + + HRESULT + InitializeImprinterEndorserProperties( + _In_ BYTE* pWiasContext, + UINT nDocumentHandlingSelect); + + HRESULT + InitializeBarcodeReaderProperties( + _In_ BYTE* pWiasContext); + + HRESULT + InitializePatchCodeReaderProperties( + _In_ BYTE* pWiasContext); + + HRESULT + InitializeMicrReaderProperties( + _In_ BYTE* pWiasContext); + + HRESULT + InitializeFormatInfoArrays(); + + // + // Property validation methods: + // + + HRESULT + ValidateFormatProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect); + + HRESULT + ValidateRegionProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect); + + HRESULT + ValidateImageInfoProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect); + + HRESULT + ValidateFeedProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext); + + HRESULT + ValidateImprinterEndorserProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect); + + HRESULT + ValidateBarcodeReaderProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext); + + HRESULT + ValidatePatchCodeReaderProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext); + + HRESULT + ValidateMicrReaderProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext); + + HRESULT + ValidateColorDropProperty( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nChannel); + + HRESULT + ValidateColorDropProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect); + + HRESULT + UpdateImageInfoProperties( + _In_ BYTE *pWiasContext, + LONG lDataType); + + HRESULT + UpdateScanAvailableItemName( + _In_opt_ LPCWSTR wszInputSource); + + HRESULT + UpdateScanAvailableItemProperty( + _In_ BYTE *pWiasContext); + + LONG + GetValidPageSizes( + LONG lMaxWidth, + LONG lMaxHeight, + LONG lMinWidth, + LONG lMinHeight, + BOOL bPortrait, + CBasicDynamicArray<LONG>& arrayPageSizes); + + HRESULT + GetPageDimensions( + LONG lPageSize, + BOOL bPortrait, + LONG& lPageWidth, + LONG& lPageHeight); + + // + // Data transfer methods: + // + + HRESULT + 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 + 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 + 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 + IsDibValid( + _In_ IStream* pStream, + LONG lBitDepth, + LONG lWidth, + LONG lHeight); + + HRESULT + IsImprinterEndorserTextValid( + _In_ BYTE* pWiasContext, + _In_ IStream* pStream, + LONG nDocumentHandlingSelect); + + HRESULT + LoadTestDataResourceToStream( + ULONG ulResourceId, + _In_ IStream* pStream, + _Out_ ULONG* pulDataSize); + + HRESULT + LoadTestDataToStream( + _In_ BYTE* pWiasContext, + GUID guidItemCategory, + GUID guidFormat, + _In_ IStream* pStream, + _Out_ ULONG* pulDataSize); + + inline void + ComputeTransferProgress( + _Inout_ ULONG *pulPercentComplete, + ULONG ulEstimatedFileSize, + ULONG ulFileBytesWritten, + BOOL bDirectWIATransfer = TRUE, + ULONG ulResumeFrom = 0); + + // + // Manual feeder control methods: + // + + HRESULT + StartFeeder(); + + HRESULT + StopFeeder(); +}; + +// +// Simple inline function that converts an HRESULT to a Win32 error code: +// +inline DWORD +WIN32_FROM_HRESULT(HRESULT hr) +{ + return ((SUCCEEDED(hr) ? ERROR_SUCCESS : (HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : (hr)))); +} diff --git a/wia/ProdScan/ProdScan.def b/wia/ProdScan/ProdScan.def new file mode 100644 index 00000000..992d4052 --- /dev/null +++ b/wia/ProdScan/ProdScan.def @@ -0,0 +1,9 @@ +LIBRARY PRODSCAN + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + + diff --git a/wia/ProdScan/ProdScan.inf b/wia/ProdScan/ProdScan.inf new file mode 100644 index 00000000..9ff8d18b --- /dev/null +++ b/wia/ProdScan/ProdScan.inf @@ -0,0 +1,78 @@ +; +; ProdScan.inf - installation file for the Production Scanner Driver Sample +; +; Copyright (c) Microsoft Corporation. All rights reserved. +; +; Manufacturer: Microsoft +; +[Version] +Signature="$WINDOWS NT$" +Class=Image +ClassGUID={6bdd1fc6-810f-11d0-bec7-08002be2092f} +Provider=%ProviderString% +DriverVer=10/16/2015,1.0.0.2 +CatalogFile = prodscan.cat + +[SourceDisksFiles] +ProdScan.dll=1 + +[SourceDisksNames] +1=%Location%,,, + +[DestinationDirs] +DefaultDestDir = 11 + +[Manufacturer] +%ManufacturerName%=Models,NTx86,NTAMD64,NTIA64,NTARM,NTARM64 + +[Models.NTx86] +%ProdScan.DeviceDesc% = ProdScan.Device, WIADRIVER_PNP_ID, USB\MS_COMP_SCAN&MS_SUBCOMP_WIAPNPID + +[Models.NTAMD64] +%ProdScan.DeviceDesc% = ProdScan.Device, WIADRIVER_PNP_ID, USB\MS_COMP_SCAN&MS_SUBCOMP_WIAPNPID + +[Models.NTIA64] +%ProdScan.DeviceDesc% = ProdScan.Device, WIADRIVER_PNP_ID, USB\MS_COMP_SCAN&MS_SUBCOMP_WIAPNPID + +[Models.NTARM] +%ProdScan.DeviceDesc% = ProdScan.Device, WIADRIVER_PNP_ID, USB\MS_COMP_SCAN&MS_SUBCOMP_WIAPNPID + +[Models.NTARM64] +%ProdScan.DeviceDesc% = ProdScan.Device, WIADRIVER_PNP_ID, USB\MS_COMP_SCAN&MS_SUBCOMP_WIAPNPID + +[ProdScan.Device] +Include = sti.inf +Needs = STI.SerialSection +PortSelect = no +SubClass = StillImage +DeviceType = 1 +DeviceSubType = 1 +Capabilities = 0x31 +Events = ProdScan.Events +AddReg = ProdScan.AddReg +CopyFiles = ProdScan.CopyFiles +ICMProfiles = "sRGB Color Space Profile.icm" + +[ProdScan.CopyFiles] +ProdScan.dll + +[ProdScan.AddReg] +HKR,,HardwareConfig,1,1 +HKR,,USDClass,,"{EB135F56-B088-4bc7-9733-422F324B3A09}" +HKCR,CLSID\{EB135F56-B088-4bc7-9733-422F324B3A09},,,"Production Scanner Driver Sample" +HKCR,CLSID\{EB135F56-B088-4bc7-9733-422F324B3A09}\InProcServer32,,0x00020000,%%SystemRoot%%\System32\ProdScan.dll +HKCR,CLSID\{EB135F56-B088-4bc7-9733-422F324B3A09}\InProcServer32,ThreadingModel,,"Both" + +[ProdScan.Events] +ScanEvent = %ScanEvent.Desc%,{A6C5A715-8C6E-11d2-977A-0000F87A926F},* + +[ProdScan.Device.Services] +Include = sti.inf +Needs = STI.SerialSection.Services + +[Strings] +ManufacturerName="TODO-Set-Manufacturer" +ProviderString="TODO-Set-Provider" +Location="Production Scanner Driver Sample Installation Source" +ProdScan.DeviceDesc = "Production Scanner Test Driver" +ScanEvent.Desc="Scan" diff --git a/wia/ProdScan/ProdScan.rc b/wia/ProdScan/ProdScan.rc new file mode 100644 index 00000000..deaaff20 --- /dev/null +++ b/wia/ProdScan/ProdScan.rc @@ -0,0 +1,103 @@ +#include "resource.h" +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Production Scanner Driver Sample DLL" +#define VER_INTERNALNAME_STR "ProdScan" +#define VER_LEGALCOPYRIGHT_YEARS "2010" +#define VER_ORIGINALFILENAME_STR "ProdScan.dll" + +#include <common.ver> + +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + +IDS_EVENT_DEVICE_CONNECTED_NAME "Device connected" +IDS_EVENT_DEVICE_CONNECTED_DESCRIPTION "This PnP event is sent when the device is initially connected to the computer" + +IDS_EVENT_DEVICE_DISCONNECTED_NAME "Device disconnected" +IDS_EVENT_DEVICE_DISCONNECTED_DESCRIPTION "This PnP event is sent when the device is disconnected from the computer" + +IDS_EVENT_POWER_SUSPEND_NAME "Power suspend" +IDS_EVENT_POWER_SUSPEND_DESCRIPTION "This PnP event is sent when the computer enters stand-by or hibernation" + +IDS_EVENT_POWER_RESUME_NAME "Power resume" +IDS_EVENT_POWER_RESUME_DESCRIPTION "This PnP event is sent when the computer recovers from stand-by or hibernation" + +IDS_EVENT_TREE_UPDATED_NAME "Tree updated" +IDS_EVENT_TREE_UPDATED_DESCRIPTION "This event is sent by the driver when the item tree is updated" + +IDS_CMD_SYNCHRONIZE_NAME "Synchronize" +IDS_CMD_SYNCHRONIZE_DESCRIPTION "This command requests to the driver to rebuild its driver item tree" + +IDS_CMD_BUILD_DEVICE_TREE_NAME "Build item tree" +IDS_CMD_BUILD_DEVICE_TREE_DESCRIPTION "This command requests to the driver to build the driver item tree" + +IDS_CMD_DELETE_DEVICE_TREE_NAME "Delete item tree" +IDS_CMD_DELETE_DEVICE_TREE_DESCRIPTION "This command requests to the driver to delete the driver item tree" + +IDS_EVENT_SCAN_IMAGE_NAME "Scan" +IDS_EVENT_SCAN_IMAGE_DESCRIPTION "This event is sent when the device is ready to transfer an image file" + +IDS_EVENT_DEVICE_NOT_READY_NAME "Device not ready" +IDS_EVENT_DEVICE_NOT_READY_DESCRIPTION "This event is sent when the device becomes not ready to scan" + +IDS_EVENT_DEVICE_READY_NAME "Device ready" +IDS_EVENT_DEVICE_READY_DESCRIPTION "This event is sent when the device becomes ready to scan" + +IDS_EVENT_FLATBED_LID_OPEN_NAME "Flatbed lid open" +IDS_EVENT_FLATBED_LID_OPEN_DESCRIPTION "This event is sent when the device's flatbed lid is opened" + +IDS_EVENT_FLATBED_LID_CLOSED_NAME "Flatbed lid closed" +IDS_EVENT_FLATBED_LID_CLOSED_DESCRIPTION "This event is sent when the device's flatbed lid is closed" + +IDS_EVENT_FEEDER_LOADED_NAME "Feeder loaded" +IDS_EVENT_FEEDER_LOADED_DESCRIPTION "This event is sent when the device's feeder is loaded by the scanner operator" + +IDS_EVENT_FEEDER_EMPTIED_NAME "Feeder emptied" +IDS_EVENT_FEEDER_EMPTIED_DESCRIPTION "This event is sent when the device's feeder is unloaded by the scanner operator" + +IDS_EVENT_COVER_OPEN_NAME "Cover open" +IDS_EVENT_COVER_OPEN_DESCRIPTION "This event is sent when a device's scan path cover is opened" + +IDS_EVENT_COVER_CLOSED_NAME "Cover closed" +IDS_EVENT_COVER_CLOSED_DESCRIPTION "This event is sent when the last device's scan path cover is closed" + + +IDS_CMD_START_FEEDER_NAME "Start feeder" +IDS_CMD_START_FEEDER_DESCRIPTION "This command requests the driver to start the scanner feeder motor and prepare for scanning" + +IDS_CMD_STOP_FEEDER_NAME "Stop feeder" +IDS_CMD_STOP_FEEDER_DESCRIPTION "This command requests the driver to stop the scanner feeder motor" + +END + +// +// Test images (equivalent to the image obtained from scanning a 8.5" x 11" document at 300 DPI): +// +IDB_TESTIMAGE_GRAY RCDATA "Res\\TestGray.jpg" +IDB_TESTIMAGE_COLOR RCDATA "Res\\TestRGB.jpg" + +// +// Sample imprinter/endorser image for graphics data download: +// +IDB_TEST_IMPRINTER_IMAGE RCDATA "Res\\Printer.bmp" + +// +// Sample XML metadata files for barcode, patch code and MICR: +// +IDB_BARCODE_SAMPLE RCDATA "Res\\Barcodes.xml" +IDB_PATCH_CODE_SAMPLE RCDATA "Res\\PatchCod.xml" +IDB_MICR_SAMPLE RCDATA "Res\\Micr.xml" + + + + + + diff --git a/wia/ProdScan/ProdScan.vcxproj b/wia/ProdScan/ProdScan.vcxproj new file mode 100644 index 00000000..2b14d9a8 --- /dev/null +++ b/wia/ProdScan/ProdScan.vcxproj @@ -0,0 +1,229 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{041FE80B-1F18-4CF6-90DD-59C690C11A32}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{78CBD2C6-4539-4DE9-A02B-C6F72068B9BD}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType /> + <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>ProdScan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>ProdScan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>ProdScan</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>ProdScan</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <AdditionalOptions>/Wv:18 %(AdditionalOptions)</AdditionalOptions> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);wiaguid.lib;wiaservc.lib;ADVAPI32.lib;GDI32.lib;KERNEL32.lib;user32.lib;oleaut32.lib;ole32.lib;uuid.lib;shlwapi.lib;sti.lib;gdiplus.lib;Rpcrt4.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) /ignore:4070</AdditionalOptions> + <ModuleDefinitionFile>ProdScan.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <AdditionalOptions>/Wv:18 %(AdditionalOptions)</AdditionalOptions> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);wiaguid.lib;wiaservc.lib;ADVAPI32.lib;GDI32.lib;KERNEL32.lib;user32.lib;oleaut32.lib;ole32.lib;uuid.lib;shlwapi.lib;sti.lib;gdiplus.lib;Rpcrt4.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) /ignore:4070</AdditionalOptions> + <ModuleDefinitionFile>ProdScan.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <AdditionalOptions>/Wv:18 %(AdditionalOptions)</AdditionalOptions> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);wiaguid.lib;wiaservc.lib;ADVAPI32.lib;GDI32.lib;KERNEL32.lib;user32.lib;oleaut32.lib;ole32.lib;uuid.lib;shlwapi.lib;sti.lib;gdiplus.lib;Rpcrt4.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) /ignore:4070</AdditionalOptions> + <ModuleDefinitionFile>ProdScan.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + <ExceptionHandling> + </ExceptionHandling> + <AdditionalOptions>/Wv:18 %(AdditionalOptions)</AdditionalOptions> + </ClCompile> + <Midl> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <ResourceCompile> + <PreprocessorDefinitions>%(PreprocessorDefinitions);UNICODE;_UNICODE;WIA_DEBUG</PreprocessorDefinitions> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(SDK_INC_PATH)\gdiplus;$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);wiaguid.lib;wiaservc.lib;ADVAPI32.lib;GDI32.lib;KERNEL32.lib;user32.lib;oleaut32.lib;ole32.lib;uuid.lib;shlwapi.lib;sti.lib;gdiplus.lib;Rpcrt4.lib</AdditionalDependencies> + <AdditionalOptions>%(AdditionalOptions) /ignore:4070</AdditionalOptions> + <ModuleDefinitionFile>ProdScan.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="capman.cpp" /> + <ClCompile Include="events.cpp" /> + <ClCompile Include="fileconv.cpp" /> + <ClCompile Include="initprop.cpp" /> + <ClCompile Include="minidrv.cpp" /> + <ClCompile Include="propman.cpp" /> + <ClCompile Include="scanjobs.cpp" /> + <ClCompile Include="server.cpp" /> + <ClCompile Include="stiusd.cpp" /> + <ClCompile Include="validate.cpp" /> + <ClCompile Include="wiautil.cpp" /> + <ResourceCompile Include="prodscan.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/wia/ProdScan/ProdScan.vcxproj.Filters b/wia/ProdScan/ProdScan.vcxproj.Filters new file mode 100644 index 00000000..4c4d310b --- /dev/null +++ b/wia/ProdScan/ProdScan.vcxproj.Filters @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{ECCF4056-E569-4826-9D38-531DFB8C6CCD}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{73D904EF-DF55-4DAA-8742-42AC29EDC98C}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{CFB16486-5B70-43CB-B3FF-E78C405F0399}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="capman.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="events.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="fileconv.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="initprop.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="minidrv.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="propman.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="scanjobs.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="server.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="stiusd.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="validate.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="wiautil.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <None Include="ProdScan.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="prodscan.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/wia/ProdScan/PropMan.cpp b/wia/ProdScan/PropMan.cpp new file mode 100644 index 00000000..6dc0e86f --- /dev/null +++ b/wia/ProdScan/PropMan.cpp @@ -0,0 +1,1499 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: PropMan.cpp +* +* Project: Production Scanner Driver Sample +* +* Description: Contains the class implementation of the +* CWIAPropertyManager class that encapsulates +* WIA property creation for this driver. +* +***************************************************************************/ + +#include "stdafx.h" + +/**************************************************************************\ +* +* CWIAPropertyManager constructor +* +\**************************************************************************/ + +CWIAPropertyManager::CWIAPropertyManager() +{ + return; +} + +/**************************************************************************\ +* +* CWIAPropertyManager destructor +* +\**************************************************************************/ + +CWIAPropertyManager::~CWIAPropertyManager() +{ + // + // Cleanup any items contained in the property list before exiting: + // + for (INT i = 0; i < m_List.Size(); i++) + { + PWIA_PROPERTY_INFO_DATA pPropertyData = m_List[i]; + + if (pPropertyData) + { + // + // Delete contents: + // + DeletePropertyData(pPropertyData); + + // + // Delete container: + // + delete pPropertyData; + } + } +} + + +/**************************************************************************\ +* +* This function finds the specified property, and removes it +* from the list of properties +* +* Parameters: +* +* lPropertyID - Property ID of the property to find +* +* Return Value: +* +* Pointer to property information list, NULL if error +* +\**************************************************************************/ + +PWIA_PROPERTY_INFO_DATA CWIAPropertyManager::FindProperty( + LONG lPropertyID) +{ + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + for (INT i = 0; i< m_List.Size(); i++) + { + PWIA_PROPERTY_INFO_DATA pPropertyData = m_List[i]; + + if (pPropertyData->pid == (ULONG)lPropertyID) + { + pInfo = pPropertyData; + break; + } + } + + return pInfo; +} + +/**************************************************************************\ +* +* This function deletes the contents of a WIA_PROPERTY_DATA structure +* +* Parameters: +* +* pInfo - pointer containing the property data +* +* Return Value: +* +* S_OK or a a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::DeletePropertyData( + _Inout_ PWIA_PROPERTY_INFO_DATA pInfo) +{ + HRESULT hr = E_INVALIDARG; + + if (pInfo) + { + // + // Delete any allocated lists: + // + if (pInfo->wpi.lAccessFlags & WIA_PROP_LIST) + { + if (pInfo->pv.vt & VT_I4) + { + if (pInfo->wpi.ValidVal.List.pList) + { + LocalFree(pInfo->wpi.ValidVal.List.pList); + pInfo->wpi.ValidVal.List.pList = NULL; + } + } + + if (pInfo->pv.vt & VT_CLSID) + { + if (pInfo->wpi.ValidVal.ListGuid.pList) + { + LocalFree(pInfo->wpi.ValidVal.ListGuid.pList); + pInfo->wpi.ValidVal.ListGuid.pList = NULL; + } + } + } + + // + // Free any allocated BSTRs: + // + + if (VT_BSTR == pInfo->pv.vt) + { + SysFreeString(pInfo->pv.bstrVal); + pInfo->pv.bstrVal = NULL; + } + + // + // Delete any allocated GUIDs: + // + + if (VT_CLSID == pInfo->pv.vt) + { + delete pInfo->pv.puuid; + pInfo->pv.puuid = NULL; + } + + hr = S_OK; + } + return hr; +} + +/**************************************************************************\ +* +* This function allocates a WIA_PROPERTY_INFO_DATA strucuture +* and initializes the members. +* +* Parameters: +* +* none +* +* Return Value: +* +* Pointer to the new structure +* +\**************************************************************************/ + +PWIA_PROPERTY_INFO_DATA CWIAPropertyManager::AllocatePropertyData() +{ + + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + pInfo = new WIA_PROPERTY_INFO_DATA; + + if (pInfo) + { + // + // Erase all values in newly allocated property data structure: + // + memset(pInfo, 0, sizeof(WIA_PROPERTY_INFO_DATA)); + + // + // Properly initialize the property variant: + // + PropVariantInit(&pInfo->pv); + } + + return pInfo; +} + +/**************************************************************************\ +* +* This function finds the property specified by lPropertyID and deletes the +* contents of the WIA_PROPERTY_INFO_DATA. +* +* Parameters: +* +* lPropertyID - Property ID of the property to remove and delete +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::RemovePropertyAndDeleteData( + LONG lPropertyID) +{ + + // + // Find any existing property with the same ID, and remove it from the list: + // + + PWIA_PROPERTY_INFO_DATA pInfo = FindProperty(lPropertyID); + + if (pInfo) + { + // + // Find and remove the property info from the list and delete the contents: + // + m_List.Delete(m_List.Find(pInfo)); + delete pInfo; + pInfo = NULL; + } + + return S_OK; +} + +/**************************************************************************\ +* +* This function adds a new property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* lCurrValue - Current Property Value +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue) +{ + + HRESULT hr = E_INVALIDARG; + + if (pszName) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + + // + // Populate the data in the structure, and add it to the property list: + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.lVal = lCurrValue; + pInfo->pv.vt = VT_I4; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + + m_List.Append(pInfo); + + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + + return hr; +} + +/**************************************************************************\ +* +* This function adds a new array-of-LONG single-value property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* ulCurrValueItems - Number of VT_I4 items in the current value array +* pCurrValue - Current Property Value, as an array of VT_I4 (LONG) +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + ULONG ulCurrValueItems, + _In_reads_(ulCurrValueItems) + LONG *pCurrValue) +{ + + HRESULT hr = E_INVALIDARG; + + if (pszName && pCurrValue && ulCurrValueItems) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + // + // Populate the data in the structure, and add it to the property list + // + // For a VT_VECTOR | VT_I4 the correct PROPVARIANT member is: cal (type: CAL) + // + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.cal.cElems = ulCurrValueItems; + pInfo->pv.cal.pElems = pCurrValue; + pInfo->pv.vt = VT_I4 | VT_VECTOR; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + + m_List.Append(pInfo); + + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + + return hr; +} + +/**************************************************************************\ +* +* This function adds a new VT_VECTOR | VT_UI1 (array-of-BYTEs single-value) +* property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* pbCurrValue - Current Property Value (BYTE vector) +* ulNumItems - Number of items in the current propery value vector +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + _In_reads_(ulNumItems) + BYTE* pbCurrValue, + ULONG ulNumItems) +{ + HRESULT hr = E_INVALIDARG; + + if (pszName) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + // + // Populate the data in the structure, and add it to the property list + // + // Note: for a VT_VECTOR | VT_UI1 the correct PROPVARIANT member is: caub (type: CAUB) + // + // From MSDN: + // + // "If the type indicator is combined with VT_VECTOR by using an OR operator, the value is one of the counted array values. + // This creates a DWORD count of elements, followed by a pointer to the specified repetitions of the value. + // For example, a type indicator of VT_LPSTR | VT_VECTOR has a DWORD element count, followed by a pointer to an array of LPSTR elements. + // VT_VECTOR can be combined by an OR operator with the following types: VT_I1, VT_UI1, VT_I2, VT_UI2, VT_BOOL, VT_I4, VT_UI4, VT_R4, + // VT_R8, VT_ERROR, VT_I8, VT_UI8, VT_CY, VT_DATE, VT_FILETIME, VT_CLSID, VT_CF, VT_BSTR, VT_LPSTR, VT_LPWSTR, and VT_VARIANT". + // + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.caub.cElems = ulNumItems; + pInfo->pv.caub.pElems = pbCurrValue; + pInfo->pv.vt = VT_UI1 | VT_VECTOR; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + + m_List.Append(pInfo); + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + return hr; +} + +/**************************************************************************\ +* +* This function adds a new property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* lCurrValue - Current Property Value +* lValidBits - Valid bit values +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue, + LONG lValidBits) +{ + + HRESULT hr = E_INVALIDARG; + if (pszName) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // when a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // allocate a property info structure + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + + // + // populate the data in the structure, and add it to the property list + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.lVal = lCurrValue; + pInfo->pv.vt = VT_I4; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + pInfo->wpi.ValidVal.Flag.Nom = lCurrValue; + pInfo->wpi.ValidVal.Flag.ValidBits = lValidBits; + m_List.Append(pInfo); + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + return hr; +} + +/**************************************************************************\ +* +* This function adds a new property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* lCurrValue - Current Property Value +* lNomValue - Property Nominal Value +* lMinValue - Property Minimum Value +* lMaxValue - Property Maximum Value +* lInc - Property Increment Value +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue, + LONG lNomValue, + LONG lMinValue, + LONG lMaxValue, + LONG lInc) +{ + HRESULT hr = E_INVALIDARG; + + if (pszName && + (lMinValue <= lMaxValue) && + (lNomValue >= lMinValue) && + (lNomValue <= lMaxValue) && + (lCurrValue >= lMinValue) && + (lCurrValue <= lMaxValue)) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + // + // Populate the data in the structure, and add it to the property list: + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.lVal = lCurrValue; + pInfo->pv.vt = VT_I4; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + pInfo->wpi.ValidVal.Range.Inc = lInc; + pInfo->wpi.ValidVal.Range.Min = lMinValue; + pInfo->wpi.ValidVal.Range.Max = lMaxValue; + pInfo->wpi.ValidVal.Range.Nom = lNomValue; + + m_List.Append(pInfo); + hr = S_OK; + } + else + { + hr = E_INVALIDARG; + } + } + + return hr; +} + +/**************************************************************************\ +* +* This function adds a new property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* lCurrValue - Current Property Value +* lNomValue - Property Nominal Value +* pValueList - List of Valid Values +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue, + LONG lNomValue, + _In_ CBasicDynamicArray<LONG>* pValueList) +{ + + HRESULT hr = E_INVALIDARG; + + if (pszName && pValueList && pValueList->Size()) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + LONG *pLongList = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + LONG lNumValues = (LONG)pValueList->Size(); + if (lNumValues) + { + pLongList = (LONG*)LocalAlloc(LPTR, (sizeof(LONG) * lNumValues)); + if (pLongList) + { + for(INT iIndex = 0; iIndex < lNumValues; iIndex++) + { + pLongList[iIndex] = ((*pValueList)[iIndex]); + } + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + + // + // Populate the data in the structure, and add it to the property list: + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.lVal = lCurrValue; + pInfo->pv.vt = VT_I4; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + pInfo->wpi.ValidVal.List.pList = (BYTE*)pLongList; + pInfo->wpi.ValidVal.List.Nom = lNomValue; + pInfo->wpi.ValidVal.List.cNumList = lNumValues; + + m_List.Append(pInfo); + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + } + else + { + hr = E_INVALIDARG; + } + + if (FAILED(hr)) + { + if (pLongList) + { + LocalFree(pLongList); + pLongList = NULL; + } + } + } + return hr; +} + +/**************************************************************************\ +* +* This function adds a new VT_UI4 single value property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* ulCurrValue - Current Property Value +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddPropertyUL( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + ULONG ulCurrValue) +{ + + HRESULT hr = E_INVALIDARG; + + if (pszName) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + + // + // Populate the data in the structure, and add it to the property list: + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.lVal = ulCurrValue; + pInfo->pv.vt = VT_UI4; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + + m_List.Append(pInfo); + + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + + return hr; +} + +/**************************************************************************\ +* +* This function adds a new VT_UI4 range property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* ulCurrValue - Current Property Value +* ulNomValue - Property Nominal Value +* ulMinValue - Property Minimum Value +* ulMaxValue - Property Maximum Value +* ulInc - Property Increment Value +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddPropertyUL( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + ULONG ulCurrValue, + ULONG ulNomValue, + ULONG ulMinValue, + ULONG ulMaxValue, + ULONG ulInc) +{ + HRESULT hr = E_INVALIDARG; + + if (pszName && + (ulMinValue <= ulMaxValue) && + (ulNomValue >= ulMinValue) && + (ulNomValue <= ulMaxValue) && + (ulCurrValue >= ulMinValue) && + (ulCurrValue <= ulMaxValue)) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + // + // Populate the data in the structure, and add it to the property list: + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.lVal = ulCurrValue; + pInfo->pv.vt = VT_UI4; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + pInfo->wpi.ValidVal.Range.Inc = ulInc; + pInfo->wpi.ValidVal.Range.Min = ulMinValue; + pInfo->wpi.ValidVal.Range.Max = ulMaxValue; + pInfo->wpi.ValidVal.Range.Nom = ulNomValue; + + m_List.Append(pInfo); + hr = S_OK; + } + else + { + hr = E_INVALIDARG; + } + } + + return hr; +} + +/**************************************************************************\ +* +* This function adds a new property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* bstrCurrValue - Current Property Value +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + _In_ BSTR bstrCurrValue) +{ + HRESULT hr = E_INVALIDARG; + + if (pszName && bstrCurrValue) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + // + // Populate the data in the structure, and add it to the property list: + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.bstrVal = SysAllocString(bstrCurrValue); + pInfo->pv.vt = VT_BSTR; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + + m_List.Append(pInfo); + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + return hr; +} + +/**************************************************************************\ +* +* This function adds a new array-of-BSTR single-value property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* ulCurrValueItems - Number of VT_BSTR items in the current value array +* pCurrValue - Current Property Value, as an array of VT_BSTR +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + ULONG ulCurrValueItems, + _In_reads_(ulCurrValueItems) + BSTR *pCurrValue) +{ + + HRESULT hr = E_INVALIDARG; + + if (pszName && pCurrValue && ulCurrValueItems) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + // + // Populate the data in the structure, and add it to the property list + // + // For a VT_VECTOR | VT_BSTR the correct PROPVARIANT member is: cabstr (type: CABSTR) + // + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.cabstr.cElems = ulCurrValueItems; + pInfo->pv.cabstr.pElems = pCurrValue; + pInfo->pv.vt = VT_VECTOR | VT_BSTR; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + + m_List.Append(pInfo); + + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + + return hr; +} + +/**************************************************************************\ +* +* This function adds a new property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* guidCurrValue - Current Property Value +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + GUID guidCurrValue) +{ + HRESULT hr = E_INVALIDARG; + + if (pszName) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + // + // Allocate memory for a new GUID value and copy the data to it. + // This memory is going to be freed when DeletePropertyData will + // be called for this VT_CLSID property: + // +#pragma prefast(suppress:__WARNING_MEMORY_LEAK, "pguid is not leaked") + GUID *pguid = new GUID; + if (pguid) + { + memcpy(pguid, &guidCurrValue, sizeof(GUID)); + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + // + // Populate the data in the structure, and add it to the property list: + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.puuid = pguid; + pInfo->pv.vt = VT_CLSID; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + + m_List.Append(pInfo); + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + } + + return hr; +} + +/**************************************************************************\ +* +* This function adds a new property to the property list. +* +* If a property exists with the same property ID: +* +* 1. The old property is removed from the list, and the contents destroyed +* 2. The new property is added to the list. +* +* Parameters: +* +* lPropertyID - Property ID +* pszName - Property NAME +* lAccessFlags - Property Access Flags +* guidCurrValue - Current Property Value +* guidNomValue - Property Nominal Value +* pValueList - List of Valid Values +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + GUID guidCurrValue, + GUID guidNomValue, + _In_ CBasicDynamicArray<GUID>* pValueList) +{ + + HRESULT hr = E_INVALIDARG; + + if (pszName && pValueList) + { + PWIA_PROPERTY_INFO_DATA pInfo = NULL; + GUID *pguid = NULL; + GUID *pguidList = NULL; + + // + // When a property is being added, always remove any existing property that has the same + // property ID. Any call to AddProperty() means that the property being added should be + // treated as the latest. + // + + RemovePropertyAndDeleteData(lPropertyID); + + LONG lNumValues = (LONG)pValueList->Size(); + if (lNumValues) + { + pguidList = (GUID*)LocalAlloc(LPTR,(sizeof(GUID) * lNumValues)); + if (pguidList) + { + for (INT iIndex = 0; iIndex < lNumValues; iIndex++) + { + pguidList[iIndex] = ((*pValueList)[iIndex]); + } + + hr = S_OK; + + if (SUCCEEDED(hr)) + { +#pragma prefast(suppress:__WARNING_ALIASED_MEMORY_LEAK, "pguid is not leaked") + pguid = new GUID; + if (pguid) + { + *pguid = guidCurrValue; + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + } + + if (SUCCEEDED(hr)) + { + // + // Allocate a property info structure: + // + + pInfo = AllocatePropertyData(); + if (pInfo) + { + + // + // Populate the data in the structure, and add it to the property list: + // + + pInfo->pszName = pszName; + pInfo->pid = lPropertyID; + pInfo->pv.puuid = pguid; + pInfo->pv.vt = VT_CLSID; + pInfo->ps.ulKind = PRSPEC_PROPID; + pInfo->ps.propid = pInfo->pid; + pInfo->wpi.lAccessFlags = lAccessFlags; + pInfo->wpi.vt = pInfo->pv.vt; + pInfo->wpi.ValidVal.ListGuid.pList = pguidList; + pInfo->wpi.ValidVal.ListGuid.Nom = guidNomValue; + pInfo->wpi.ValidVal.ListGuid.cNumList = lNumValues; + + m_List.Append(pInfo); + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + } + } + else + { + hr = E_OUTOFMEMORY; + } + } + else + { + hr = E_INVALIDARG; + } + + if (FAILED(hr)) + { + // + // Free memory any allocated memory if failure occurs: + // + + if (pguidList) + { + LocalFree(pguidList); + pguidList = NULL; + } + + if (pguid) + { + delete pguid; + pguid = NULL; + } + } + } + return hr; +} + +/**************************************************************************\ +* +* This function removes a property from the property list. +* +* Parameters: +* +* lPropertyID - Property ID +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::RemoveProperty( + LONG lPropertyID) +{ + return RemovePropertyAndDeleteData(lPropertyID); +} + +/**************************************************************************\ +* +* This function uses WIA helper functions to upload the properties +* to the Application Item Tree item created by the WIA service. +* +* Parameters: +* +* pWiasContext - WIA Context provided by the WIA service +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CWIAPropertyManager::SetItemProperties( + _Inout_ BYTE* pWiasContext) +{ + HRESULT hr = E_INVALIDARG; + + if (pWiasContext) + { + hr = S_OK; + + // + // Get current number of properties in the list: + // + + LONG lNumProps = m_List.Size(); + if (lNumProps > 0) + { + LONG lIndex = 0; + LPOLESTR *pszName = NULL; + PROPID *ppid = NULL; + PROPVARIANT *ppv = NULL; + PROPSPEC *pps = NULL; + WIA_PROPERTY_INFO *pwpi = NULL; + + // + // Allocate arrays of structures needed to contain the property data: + // + + #pragma prefast(suppress:__WARNING_MEMORY_LEAK_EXCEPTION, "When using a new operator that throws this can leak:") + pszName = new LPOLESTR[lNumProps]; + #pragma prefast(suppress:__WARNING_MEMORY_LEAK_EXCEPTION, "When using a new operator that throws this can leak:") + ppid = new PROPID[lNumProps]; + #pragma prefast(suppress:__WARNING_MEMORY_LEAK_EXCEPTION, "When using a new operator that throws this can leak:") + ppv = new PROPVARIANT[lNumProps]; + #pragma prefast(suppress:__WARNING_MEMORY_LEAK_EXCEPTION, "When using a new operator that throws this can leak:") + pps = new PROPSPEC[lNumProps]; + #pragma prefast(suppress:__WARNING_MEMORY_LEAK_EXCEPTION, "When using a new operator that throws this can leak:") + pwpi = new WIA_PROPERTY_INFO[lNumProps]; + + if (pszName && ppid && ppv && pps && pwpi) + { + // + // Copy the property data into the proper structures: + // + for(INT i = 0; i < lNumProps; i++) + { + PWIA_PROPERTY_INFO_DATA pPropertyData = m_List[i]; + if (pPropertyData) + { + pszName[lIndex] = pPropertyData->pszName; + ppid[lIndex] = pPropertyData->pid; + memcpy(&ppv[lIndex], &pPropertyData->pv, sizeof(PROPVARIANT)); + memcpy(&pps[lIndex], &pPropertyData->ps, sizeof(PROPSPEC)); + memcpy(&pwpi[lIndex], &pPropertyData->wpi, sizeof(WIA_PROPERTY_INFO)); + lIndex++; + } + } + + // + // Send the property names to the WIA service: + // + #pragma prefast(suppress:__WARNING_USING_UNINIT_VAR, "ppid, *ppid, pszName and *pszName are initialized above" + hr = wiasSetItemPropNames(pWiasContext, lNumProps, ppid, pszName); + if (SUCCEEDED(hr)) + { + // + // Send the property values to the WIA service: + // + #pragma prefast(suppress:__WARNING_USING_UNINIT_VAR, "pps and *pps are initialized above" + hr = wiasWriteMultiple(pWiasContext, lNumProps, pps, ppv); + if (SUCCEEDED(hr)) + { + // + // Send the property valid values to the WIA service: + // + #pragma prefast(suppress:__WARNING_USING_UNINIT_VAR, "pwpi and *pvpi are initialized above" + hr = wiasSetItemPropAttribs(pWiasContext, lNumProps, pps, pwpi); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties, wiasSetItemPropAttribs failed")); + } + } + else + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties, wiasWriteMultiple failed")); + } + } + else + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties, wiasSetItemPropNames failed")); + } + } + else + { + WIAEX_ERROR((g_hInst, "CWIAPropertyManager::SetItemProperties, failed to allocate memory for property arrays")); + hr = E_OUTOFMEMORY; + } + + // + // Always delete any temporary memory allocated before exiting the function. + // The WIA service makes a copy of the information during the "wias" helper calls. + // + + if (pszName) + { + delete [] pszName; + pszName = NULL; + } + + if (ppid) + { + delete [] ppid; + ppid = NULL; + } + + if (ppv) + { + delete [] ppv; + ppid = NULL; + } + + if (pps) + { + delete [] pps; + ppid = NULL; + } + + if (pwpi) + { + delete [] pwpi; + ppid = NULL; + } + } + } + + return hr; +} diff --git a/wia/ProdScan/PropMan.h b/wia/ProdScan/PropMan.h new file mode 100644 index 00000000..c7436b92 --- /dev/null +++ b/wia/ProdScan/PropMan.h @@ -0,0 +1,205 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: PropMan.h +* +* Project: Production Scanner Driver Sample +* +* Description: This file contains the class definition of the +* CWIAPropertyManager class that encapsulates +* WIA property creation for this driver. +* +***************************************************************************/ + +#pragma once + + +// +// Structure definitions: +// + +typedef struct _WIA_PROPERTY_INFO_DATA{ + LPOLESTR pszName; // property name + PROPID pid; // property id + PROPVARIANT pv; // property variant + PROPSPEC ps; // property spec + WIA_PROPERTY_INFO wpi; // property info +} WIA_PROPERTY_INFO_DATA,*PWIA_PROPERTY_INFO_DATA; + +// +// WIA access flag combinations: +// + +#define RN (WIA_PROP_READ | WIA_PROP_NONE) +#define RF (WIA_PROP_READ | WIA_PROP_FLAG) +#define RW (WIA_PROP_READ | WIA_PROP_WRITE | WIA_PROP_NONE) +#define RWL (WIA_PROP_READ | WIA_PROP_WRITE | WIA_PROP_LIST) +#define RWR (WIA_PROP_READ | WIA_PROP_WRITE | WIA_PROP_RANGE) +#define RWF (WIA_PROP_READ | WIA_PROP_WRITE | WIA_PROP_FLAG) +#define RWLC (WIA_PROP_READ | WIA_PROP_WRITE | WIA_PROP_LIST|WIA_PROP_CACHEABLE) +#define RWRC (WIA_PROP_READ | WIA_PROP_WRITE | WIA_PROP_RANGE|WIA_PROP_CACHEABLE) +#define RWFC (WIA_PROP_READ | WIA_PROP_WRITE | WIA_PROP_FLAG|WIA_PROP_CACHEABLE) + +// +// CWIAPropertyManager class: +// + +class CWIAPropertyManager +{ +private: + CBasicDynamicArray<PWIA_PROPERTY_INFO_DATA> m_List; + + PWIA_PROPERTY_INFO_DATA + FindProperty( + LONG lPropertyID); + + HRESULT + DeletePropertyData( + _Inout_ PWIA_PROPERTY_INFO_DATA pInfo); + + PWIA_PROPERTY_INFO_DATA + AllocatePropertyData(); + + HRESULT + RemovePropertyAndDeleteData( + LONG lPropertyID); + +public: + CWIAPropertyManager(); + ~CWIAPropertyManager(); + + // + // LONG type property creation: + // + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue); + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + ULONG ulCurrValueItems, + _In_reads_(ulCurrValueItems) LONG *pCurrValue); + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + _In_reads_(ulNumItems) BYTE *pbCurrValue, + ULONG ulNumItems); + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue, + LONG lNomValue, + LONG lMinValue, + LONG lMaxValue, + LONG lInc); + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue, + LONG lNomValue, + _In_ LONG *plValues, + LONG lNumValues); + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue, + LONG lNomValue, + _In_ CBasicDynamicArray<LONG> *pValueList); + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + LONG lCurrValue, + LONG lValidBits); + + // + // ULONG type property creation + // + + HRESULT + AddPropertyUL( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + ULONG ulCurrValue); + + HRESULT + AddPropertyUL( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + ULONG ulCurrValue, + ULONG ulNomValue, + ULONG ulMinValue, + ULONG ulMaxValue, + ULONG ulInc); + + // + // BSTR type property creation: + // + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + _In_ BSTR bstrCurrValue); + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + ULONG ulCurrValueItems, + _In_reads_(ulCurrValueItems) BSTR *pCurrValue); + + // + // GUID type property creation: + // + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + GUID guidCurrValue); + + HRESULT + AddProperty( + LONG lPropertyID, + _In_ LPOLESTR pszName, + LONG lAccessFlags, + GUID guidCurrValue, + GUID guidNomValue, + _In_ CBasicDynamicArray<GUID> *pValueList); + + HRESULT + RemoveProperty( + LONG lPropertyID); + + HRESULT + SetItemProperties( + _Inout_ BYTE *pWiasContext); +}; diff --git a/wia/ProdScan/Res/Barcodes.xml b/wia/ProdScan/Res/Barcodes.xml new file mode 100644 index 00000000..6d63d8ca --- /dev/null +++ b/wia/ProdScan/Res/Barcodes.xml @@ -0,0 +1,36 @@ +<?xml version="1.0"?> +<catalog xmlns:wbar="http://schemas.microsoft.com/windows/2010/04/wia/barcode"> + <wbar:BarcodeDetectionReport> + <wbar:Count>3</wbar:Count> + <wbar:ListOfBarcodes> + <wbar:BarcodeInfo> + <wbar:Type>0</wbar:Type> + <wbar:Page>0</wbar:Page> + <wbar:Confidence>5</wbar:Confidence> + <wbar:XOffset>0</wbar:XOffset> + <wbar:YOffset>0</wbar:YOffset> + <wbar:Rotation>90</wbar:Rotation> + <wbar:Text>036000291452</wbar:Text> + </wbar:BarcodeInfo> + <wbar:BarcodeInfo> + <wbar:Type>2</wbar:Type> + <wbar:Page>0</wbar:Page> + <wbar:Confidence>9</wbar:Confidence> + <wbar:XOffset>2</wbar:XOffset> + <wbar:YOffset>1000</wbar:YOffset> + <wbar:Rotation>0</wbar:Rotation> + <wbar:Text>3117013206375</wbar:Text> + </wbar:BarcodeInfo> + <wbar:BarcodeInfo> + <wbar:Type>7</wbar:Type> + <wbar:Page>0</wbar:Page> + <wbar:Confidence>10</wbar:Confidence> + <wbar:XOffset>0</wbar:XOffset> + <wbar:YOffset>2000</wbar:YOffset> + <wbar:Rotation>0</wbar:Rotation> + <wbar:Text>This is a Full ASCII Code 39 example</wbar:Text> + </wbar:BarcodeInfo> + </wbar:ListOfBarcodes> + </wbar:BarcodeDetectionReport> +</catalog> + diff --git a/wia/ProdScan/Res/Micr.xml b/wia/ProdScan/Res/Micr.xml new file mode 100644 index 00000000..50b8b969 --- /dev/null +++ b/wia/ProdScan/Res/Micr.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<catalog xmlns:wmicr="http://schemas.microsoft.com/windows/2010/04/wia/micr"> + <wmicr:MicrDetectionReport> + <wmicr:Count>2</wmicr:Count> + <wmicr:Placeholder>?</wmicr:Placeholder> + <wmicr:ListOfMicrStrings> + <wmicr:MicrInfo> + <wmicr:Page>0</wmicr:Page> + <wmicr:Text>1234567890</wmicr:Text> + </wmicr:MicrInfo> + <wmicr:MicrInfo> + <wmicr:Page>1</wmicr:Page> + <wmicr:Text>987?543?10</wmicr:Text> + </wmicr:MicrInfo> + </wmicr:ListOfMicrStrings> + </wmicr:MicrDetectionReport> +</catalog> diff --git a/wia/ProdScan/Res/PatchCod.xml b/wia/ProdScan/Res/PatchCod.xml new file mode 100644 index 00000000..ad465aa5 --- /dev/null +++ b/wia/ProdScan/Res/PatchCod.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<catalog xmlns:wpat="http://schemas.microsoft.com/windows/2010/04/wia/patchcode"> + <wpat:PatchCodeDetectionReport> + <wpat:Count>2</wpat:Count> + <wpat:ListOfPatchCodes> + <wpat:PatchCodeInfo> + <wpat:Type>2</wpat:Type> + </wpat:PatchCodeInfo> + <wpat:PatchCodeInfo> + <wpat:Type>1</wpat:Type> + </wpat:PatchCodeInfo> + </wpat:ListOfPatchCodes> + </wpat:PatchCodeDetectionReport> +</catalog> diff --git a/wia/ProdScan/Res/Printer.bmp b/wia/ProdScan/Res/Printer.bmp Binary files differnew file mode 100644 index 00000000..6371a918 --- /dev/null +++ b/wia/ProdScan/Res/Printer.bmp diff --git a/wia/ProdScan/Res/TestGray.jpg b/wia/ProdScan/Res/TestGray.jpg Binary files differnew file mode 100644 index 00000000..02bd768d --- /dev/null +++ b/wia/ProdScan/Res/TestGray.jpg diff --git a/wia/ProdScan/Res/TestRGB.jpg b/wia/ProdScan/Res/TestRGB.jpg Binary files differnew file mode 100644 index 00000000..dab44312 --- /dev/null +++ b/wia/ProdScan/Res/TestRGB.jpg 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; + } + } +} diff --git a/wia/ProdScan/Server.cpp b/wia/ProdScan/Server.cpp new file mode 100644 index 00000000..95debdf8 --- /dev/null +++ b/wia/ProdScan/Server.cpp @@ -0,0 +1,476 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Name: Server.cpp +* +* Description: Contains various COM server infrastructure implemented by +* the Production Scanner Driver Sample: INonDelegating and +* IUnknown implementations, IClassFactory interface declaration +* and implementation, DLL entry points including DllMain. +* +***************************************************************************/ + +#include "stdafx.h" + +extern HINSTANCE g_hInst; + +// +// Production Scanner Driver Sample GUID +// +// {EB135F56-B088-4bc7-9733-422F324B3A09} +// +DEFINE_GUID(CLSID_ProdScan, 0xeb135f56, 0xb088, 0x4bc7, 0x97, 0x33, 0x42, 0x2f, 0x32, 0x4b, 0x3a, 0x9); + +// +// CWiaDriver::INonDelegating interface implementation +// + +HRESULT CWiaDriver::NonDelegatingQueryInterface( + REFIID riid, + LPVOID* ppvObj) +{ + HRESULT hr = S_OK; + + if (!ppvObj) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *ppvObj = NULL; + + // + // The Production Scanner Driver Sample object exports the standard WIA mini-driver COM interfaces: + // + // - IUnknown + // - IStiUSD + // - IWiaMiniDrv + // + if (IsEqualIID(riid, IID_IUnknown)) + { + *ppvObj = static_cast<INonDelegatingUnknown*>(this); + } + else if (IsEqualIID(riid, IID_IStiUSD)) + { + *ppvObj = static_cast<IStiUSD*>(this); + } + else if (IsEqualIID(riid, IID_IWiaMiniDrv)) + { + *ppvObj = static_cast<IWiaMiniDrv*>(this); + } + else + { + hr = E_NOINTERFACE; + WIAEX_ERROR((g_hInst, + "Unsupported interface (0x%08X, 0x%04X, 0x%04X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X), hr = 0x%08X", + riid.Data1, riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3], + riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7], hr)); + } + } + + if (SUCCEEDED(hr)) + { + reinterpret_cast<IUnknown*>(*ppvObj)->AddRef(); + } + + return hr; +} + +ULONG CWiaDriver::NonDelegatingAddRef() +{ + return InterlockedIncrement(&m_cRef); +} + +ULONG CWiaDriver::NonDelegatingRelease() +{ + ULONG ulRef = InterlockedDecrement(&m_cRef); + if (!ulRef) + { + WIAS_TRACE((g_hInst, "INonDelegating::NonDelegatingRelease, deleting main driver object (%p, process: %u)..", + this, GetCurrentProcessId())); + delete this; + } + + return ulRef; +} + +// +// CWiaDriver::IClassFactory interface declaration and implementation +// + +class CWiaDriverClassFactory : public IClassFactory +{ +public: + CWiaDriverClassFactory() + : m_cRef(1) + { + return; + } + + ~CWiaDriverClassFactory() + { + return; + } + + HRESULT __stdcall + QueryInterface( + REFIID riid, + _COM_Outptr_ LPVOID* ppv) + { + HRESULT hr = S_OK; + + if (!ppv) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *ppv = NULL; + if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) + { + *ppv = static_cast<IClassFactory*>(this); + reinterpret_cast<IUnknown*>(*ppv)->AddRef(); + } + else + { + hr = E_NOINTERFACE; + WIAEX_ERROR((g_hInst, + "Unsupported interface (0x%08X, 0x%04X, 0x%04X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X), hr = 0x%08X", + riid.Data1, riid.Data2, riid.Data3, riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3], + riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7], hr)); + + } + } + + return hr; + } + + ULONG __stdcall + AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + ULONG __stdcall + Release() + { + ULONG ulRef = InterlockedDecrement(&m_cRef); + if (!ulRef) + { + WIAS_TRACE((g_hInst, "IClassFactory::Release, deleting driver class factory object..")); + delete this; + } + return ulRef; + } + + HRESULT __stdcall +#pragma prefast(suppress:__WARNING_INVALID_PARAM_VALUE_2, "Set ppvObject to NULL if failed.") + CreateInstance( + _In_opt_ IUnknown* pUnkOuter, + _In_ REFIID riid, + _COM_Outptr_ void** ppvObject) + { + HRESULT hr = S_OK; + CWiaDriver *pNewDriverObject = NULL; + + WIAEX_TRACE_BEGIN; + + if (!ppvObject) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *ppvObject = NULL; + + if ((pUnkOuter) && (!IsEqualIID(riid, IID_IUnknown))) + { + hr = CLASS_E_NOAGGREGATION; + WIAEX_ERROR((g_hInst, + "NULL outer IUnknown* and not requesting IID_IUnknown, hr = 0x%08X (CLASS_E_NOAGGREGATION)", hr)); + } + } + + if (SUCCEEDED(hr)) + { + #pragma prefast(suppress:__WARNING_ALIASED_MEMORY_LEAK, "The main driver object instance is freed when the driver is unloaded") + pNewDriverObject = new CWiaDriver(pUnkOuter); + if (pNewDriverObject) + { + hr = pNewDriverObject->NonDelegatingQueryInterface(riid, ppvObject); + pNewDriverObject->NonDelegatingRelease(); + + if (SUCCEEDED(hr)) + { + WIAS_TRACE((g_hInst, "IClassFactory::CreateInstance, created main driver object (%p, process: %u)", + pNewDriverObject, GetCurrentProcessId())); + } + } + else + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate WIA driver class object, hr = 0x%08X", hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; + } + + HRESULT __stdcall + LockServer( + BOOL fLock) + { + UNREFERENCED_PARAMETER(fLock); + return S_OK; + } + +private: + LONG m_cRef; +}; + +// +// IUnknown implementation for CWiaDriver: +// + +HRESULT CWiaDriver::QueryInterface( + REFIID riid, + _COM_Outptr_ LPVOID *ppvObj) +{ + HRESULT hr = S_OK; + + if (!ppvObj) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *ppvObj = NULL; + + if (!m_punkOuter) + { + hr = E_NOINTERFACE; + WIAEX_ERROR((g_hInst, "NULL outer IUnknown*, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = m_punkOuter->QueryInterface(riid, ppvObj); + } + + return hr; +} + +ULONG CWiaDriver::AddRef() +{ + ULONG ulRef = 0; + + if (!m_punkOuter) + { + WIAEX_ERROR((g_hInst, "NULL outer IUnknown*, returning 0")); + } + else + { + ulRef = m_punkOuter->AddRef(); + } + + return ulRef; +} + +ULONG CWiaDriver::Release() +{ + ULONG ulRef = 0; + + if (!m_punkOuter) + { + WIAEX_ERROR((g_hInst, "NULL outer IUnknown*, returning 0")); + } + else + { + ulRef = m_punkOuter->Release(); + } + + return ulRef; +} + +/**************************************************************************\ +* DllMain +* +* Main DLL entry point +* +* Parameters: +* +* hInst - handle to the DLL module +* dwReason - indicates why the DLL entry-point function is being called +* lpReserved - if fdwReason is DLL_PROCESS_ATTACH lpvReserved is NULL for +* dynamic loads and non-NULL for static loads; if fdwReason +* is DLL_PROCESS_DETACH, lpvReserved is NULL if DllMain has +* been called by using FreeLibrary and non-NULL if DllMain +* has been called during process termination. +* +* Return Value: +* +* When the system calls the DllMain function with the DLL_PROCESS_ATTACH +* value in this particular case the function returns TRUE every time +* indicating it succeeds. +* +\**************************************************************************/ + +extern "C" __declspec(dllexport) BOOL APIENTRY DllMain( + HINSTANCE hInst, + DWORD dwReason, + _Reserved_ LPVOID lpReserved) +{ + UNREFERENCED_PARAMETER(lpReserved); + + g_hInst = hInst; + + switch(dwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(g_hInst); + WIAS_TRACE((g_hInst, "DLL_PROCESS_ATTACH (process: %u, thread: %u)", + GetCurrentProcessId(), GetCurrentThreadId())); + break; + + case DLL_PROCESS_DETACH: + WIAS_TRACE((g_hInst, "DLL_PROCESS_DETACH (process: %u, thread: %u)", + GetCurrentProcessId(), GetCurrentThreadId())); + break; + } + + return TRUE; +} + +/**************************************************************************\ +* DllCanUnloadNow +* +* Parameters: none +* +* Determines whether the DLL that implements this function is in use. +* If not, the caller can unload the DLL from memory. +* +* Return Value: S_OK (indicating the DLL can be unloaded at any time) +* +\**************************************************************************/ + +extern "C" HRESULT __stdcall DllCanUnloadNow(void) +{ + return S_OK; +} + +/**************************************************************************\ +* DllGetClassObject +* +* Retrieves the class object from a DLL object handler or object application. +* DllGetClassObject is called from within the CoGetClassObject function +* when the class context is a DLL. +* +* Parameters: +* +* rclsid - CLSID that will associate the correct data and code +* riid - reference to the identifier of the interface that the caller +* is to use to communicate with the class object. Usually, this +* is IID_IClassFactory (the interface identifier for IClassFactory) +* ppv - [out] address of pointer variable that receives the interface +* pointer requested in riid; upon successful return, *ppv contains +* the requested interface pointer; if an error occurs, this is NULL. +* +* Return Values: +* +* S_OK +* E_INVALIDARG +* CLASS_E_CLASSNOTAVAILABLE +* E_OUTOFMEMORY +* +\**************************************************************************/ + +extern "C" HRESULT __stdcall DllGetClassObject( + _In_ REFCLSID rclsid, + _In_ REFIID riid, + _Outptr_ LPVOID* ppv) +{ + HRESULT hr = S_OK; + CWiaDriverClassFactory *pNewClassFactory = NULL; + + WIAEX_TRACE_BEGIN; + + if (!ppv) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameters, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + *ppv = NULL; + + if (IsEqualCLSID(rclsid, CLSID_ProdScan)) + { + #pragma prefast(suppress:__WARNING_ALIASED_MEMORY_LEAK, "The final CWiaDriverClassFactory::Release frees the memory") + pNewClassFactory = new CWiaDriverClassFactory; + if (pNewClassFactory) + { + hr = pNewClassFactory->QueryInterface(riid, ppv); + pNewClassFactory->Release(); + } + else + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate WIA driver class factory object, hr = 0x%08X", hr)); + } + } + else + { + hr = CLASS_E_CLASSNOTAVAILABLE; + WIAEX_ERROR((g_hInst, "Class not available, hr = 0x%08X (CLASS_E_CLASSNOTAVAILABLE)", hr)); + } + } + + return hr; +} + +/**************************************************************************\ +* DllRegisterServer +* +* Instructs an in-process server to create its registry entries for all +* classes supported in this server module. +* +* Parameters: none +* +* Return Value: S_OK (registry entries - none - were created successfully) +* +\**************************************************************************/ + +extern "C" HRESULT __stdcall DllRegisterServer() +{ + return S_OK; +} + +/**************************************************************************\ +* DllUnregisterServer +* +* Instructs an in-process server to remove only those entries created +* through DllRegisterServer. +* +* Parameters: none +* +* Return Value: S_OK (registry entries - none - were removed successfully) +* +\**************************************************************************/ + +extern "C" HRESULT __stdcall DllUnregisterServer() +{ + return S_OK; +} diff --git a/wia/ProdScan/StiUSD.cpp b/wia/ProdScan/StiUSD.cpp new file mode 100644 index 00000000..7fd61b8a --- /dev/null +++ b/wia/ProdScan/StiUSD.cpp @@ -0,0 +1,821 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Name: StiUSD.cpp +* +* Description: Contains the IStiUSD interface implementation for +* the Production Scanner Driver Sample +* +* +***************************************************************************/ + +#include "stdafx.h" + + +/**************************************************************************\ +* +* Helper for IStiUSD::Initialize implementation. Placeholder where the driver +* would initialize its communication interface with the scanner device (register +* itself to receive event notifications from the device, read the scanner device +* configuration). Also initializes the the available transfer formats and the +* capabilities needed for the sample driver. +* +* Parameters: +* +* wszDevicePath - device path name +* hDeviceKey - device key in Registry +* +* Return Value: +* +* S_OK if it suceeds, a standard COM error code if initialization fails +* +\**************************************************************************/ + +HRESULT CWiaDriver::InitializeDeviceConnection( + _In_ LPCWSTR wszDevicePath, + _In_ HKEY hDeviceKey) +{ + HRESULT hr = S_OK; + + WIAEX_TRACE_BEGIN; + + // + // Validate parameters: + // + if ((!wszDevicePath) || (!hDeviceKey)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // Initialize here the connection with the scanner device identified by wszDevicePath. + // + // For example: + // + // if (SUCCEEDED(hr)) + // { + // hr = m_ScannerDevice.Initialize(wszDevicePath); + // if (FAILED(hr)) + // { + // WIAEX_ERROR((g_hInst, "Failed to initialize connection with scanner device described by %ws, hr = 0x%08X", wszDevicePath, hr)); + // } + // } + // + + // + // Initialize the valid format information arrays, matching the current scanner configuration if available: + // + if (SUCCEEDED(hr)) + { + hr = InitializeFormatInfoArrays(); + if(FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize WIA_FORMAT_INFO arrays, hr = 0x%08X", hr)); + } + } + + // + // Initialize the page size array used by this driver (only one list, for portrait orientation). + // + // This sample driver supports Letter, and also pretends (without actually doing it) custom + // and auto-detect document sizes: + // + if (SUCCEEDED(hr)) + { + // + // WIA_PAGE_CUSTOM is always supported: + // + m_lPortraitSizesArray.Append(WIA_PAGE_CUSTOM); + + // + // WIA_PAGE_AUTO is also supported if the scanner reports that it supports automatic document size detection: + // + m_lPortraitSizesArray.Append(WIA_PAGE_AUTO); + + // + // Other standard page sizes: + // + GetValidPageSizes(MAX_SCAN_AREA_WIDTH, MAX_SCAN_AREA_HEIGHT, MIN_SCAN_AREA_WIDTH, MIN_SCAN_AREA_HEIGHT, TRUE, m_lPortraitSizesArray); + } + + // + // Create the capability manager which will be initialized (uniquely per + // driver session) during the first IWiaMiniDrv::drvGetCapabilities call: + // + if (SUCCEEDED(hr)) + { + hr = m_tCapabilityManager.Initialize(g_hInst); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to initialize the WIA driver capability manager object, hr = 0x%08X", hr)); + } + } + + WIAEX_TRACE_FUNC_HR; + + return hr; +} + + +/**************************************************************************\ +* +* Implements IStiUSD::Initialize. Initializes an instance of the COM object +* that defines the IStiUSD interface. When this method is called the driver +* receives a pointer to an IStiDeviceControl COM interface. +* +* Parameters: +* +* pIStiDevControl - caller-supplied pointer to the IStiDeviceControl interface +* dwStiVersion - caller-supplied STI version number +* (value defined as STI_VERSION_x in sti.h) +* hParametersKey - caller-supplied handle to the registry key under +* which device-specific information is to be stored. +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. +* +\**************************************************************************/ + +HRESULT CWiaDriver::Initialize( + _In_ PSTIDEVICECONTROL pIStiDevControl, + DWORD dwStiVersion, + _In_ HKEY hParametersKey) +{ + UNREFERENCED_PARAMETER(dwStiVersion); + + HRESULT hr = S_OK; + DWORD cchDevicePath = sizeof(m_wszDevicePath) / sizeof(WCHAR); + + WIAEX_TRACE_BEGIN; + + // + // Validate parameters: + // + if ((!pIStiDevControl) || (!hParametersKey)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + + // + // Get the device path name from PnP: + // + if (SUCCEEDED(hr)) + { + memset(m_wszDevicePath, 0, sizeof(m_wszDevicePath)); + + hr = pIStiDevControl->GetMyDevicePortName(m_wszDevicePath, cchDevicePath); + + if(FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "IStiDeviceControl::GetMyDevicePortName failed, hr = 0x%08X", hr)); + } + } + + // + // Initialize the connection with the scanner: + // + if (SUCCEEDED(hr)) + { + m_hDeviceKey = hParametersKey; + + hr = InitializeDeviceConnection(m_wszDevicePath, m_hDeviceKey); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "InitializeDeviceConnection for DevicePath %ws failed, hr = 0x%08X", m_wszDevicePath, hr)); + } + } + + if (SUCCEEDED(hr)) + { + m_hrLastEdviceError = STI_ERROR_NO_ERROR; + m_bInitialized = TRUE; + } + else + { + m_hrLastEdviceError = hr; + m_bInitialized = FALSE; + } + + WIAEX_TRACE((g_hInst, "IStiUSD::Initialize 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::GetCapabilities. Returns the still image +* device's capabilities. +* +* Parameters: +* +* pDevCaps - caller-supplied pointer to an empty STI_USD_CAPS structure +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. +* +\**************************************************************************/ + +HRESULT CWiaDriver::GetCapabilities( + _Out_ PSTI_USD_CAPS pDevCaps) +{ + HRESULT hr = S_OK; + + WIAEX_TRACE_BEGIN; + + if (!pDevCaps) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X",hr)); + } + + if (SUCCEEDED(hr)) + { + // + // The sample driver supports device notifications (required), known also as interrupt events. + // Polling (optional) it is not needed so STI_GENCAP_POLLING_NEEDED is not reported here: + // + + memset(pDevCaps, 0, sizeof(STI_USD_CAPS)); + + pDevCaps->dwVersion = STI_VERSION_3; + pDevCaps->dwGenericCaps = STI_GENCAP_WIA | STI_USD_GENCAP_NATIVE_PUSHSUPPORT | STI_GENCAP_NOTIFICATIONS; + + WIAS_TRACE((g_hInst, "Device capabilities: 0x%08X", pDevCaps->dwGenericCaps)); + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAEX_TRACE((g_hInst, "IStiUSD::GetCapabilities 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::GetStatus. Returns the status for the still image device. +* GetStatus is called by the WIA service for two major operations: +* +* 1. Checking device ON-LINE status. +* 2. Polling for device events (like a push button event) +* +* Parameters: +* +* pDevStatus - caller-supplied pointer to an STI_DEVICE_STATUS structure +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. +* +\**************************************************************************/ + +HRESULT CWiaDriver::GetStatus( + _Inout_ PSTI_DEVICE_STATUS pDevStatus) +{ + HRESULT hr = S_OK; + + if (!pDevStatus) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X",hr)); + } + + // + // A driver may be requested to report one or both of the following: + // + // STI_DEVSTATUS_EVENTS_STATE - The driver should fill in the dwEventHandlingState member + // STI_DEVSTATUS_ONLINE_STATE - The driver should fill in the dwOnlineState member + // + // In this case STI_DEVSTATUS_EVENTS_STATE is not expected nor supported as this driver + // does not support polling events (if the driver previously set the STI_GENCAP_POLLING_NEEDED + // flag in the device's STI_DEV_CAPS structure, the IStiUSD::GetStatus method is the means + // by which the Event Monitor determines if a still image device event has occurred; + // the Event Monitor will call the method, specifying STI_DEVSTATUS_EVENT_STATE + // in the supplied STI_DEVICE_STATUS structure; the driver must poll the device + // and set STI_EVENTHANDLING_PENDING if an event has occurred) + // + // If the caller specifies STI_DEVSTATUS_ONLINE_STATE in the supplied + // STI_DEVICE_STATUS structure, the driver should set the appropriate flag + // in the STI_DEVICE_STATUS structure's dwOnlineState member. + // + + if (SUCCEEDED(hr)) + { + pDevStatus->dwOnlineState = 0; + pDevStatus->dwHardwareStatusCode = 0; + pDevStatus->dwEventHandlingState = 0; + + // + // STI_DEVSTATUS_ONLINE_STATE: + // + if (pDevStatus->StatusMask & STI_DEVSTATUS_ONLINE_STATE) + { + // + // This sample driver is always online and ready: + // + pDevStatus->dwOnlineState = STI_ONLINESTATE_OPERATIONAL; + } + + // + // STI_DEVSTATUS_EVENTS_STATE: + // + else if (pDevStatus->StatusMask & STI_DEVSTATUS_EVENTS_STATE) + { + // + // Polled events are not supported so we don't have to return anyting here: + // + pDevStatus->dwEventHandlingState &= ~STI_EVENTHANDLING_PENDING; + } + } + + if (FAILED(hr)) + { + m_hrLastEdviceError = hr; + } + + WIAS_TRACE((g_hInst, "IStiUSD::GetStatus 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::DeviceReset. Resets the still image device to a known, +* initialized state. The sample driver will return S_OK without to execute +* any reset operation. The success-do-nothing approach is needed to ensure +* compatibility with the requirements for the IStiUSD interface implementation. +* +* Parameters: +* +* None +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. The sample driver always returns S_OK. +* +\**************************************************************************/ + +HRESULT CWiaDriver::DeviceReset() +{ + WIAS_TRACE((g_hInst, "IStiUSD::DeviceReset 0x%08X", S_OK)); + return S_OK; +} + +/**************************************************************************\ +* +* Implements IStiUSD::Diagnostic. If the driver is initialized when Diagnostic +* is called the driver should attempt to communicate with the device and +* determine if the device is online and operational. This sample driver +* does not do any special device validation. +* +* Parameters: +* +* pBuffer - caller-supplied pointer to an STI_DIAG structure to receive +* testing status information +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. +* +\**************************************************************************/ + +HRESULT CWiaDriver::Diagnostic( + _Inout_ LPDIAG pBuffer) +{ + HRESULT hr = S_OK; + + WIAEX_TRACE((g_hInst, "IStiUSD::Diagnostic..")); + + if ((!pBuffer) || (pBuffer->dwSize < sizeof(STI_DIAG)) || (pBuffer->sErrorInfo.dwSize < sizeof(STI_ERROR_INFO))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter (buffer: %p, size: %u bytes (needed: %u), error: %u bytes (needed: %u)), hr = 0x%08X", + pBuffer, pBuffer ? pBuffer->dwSize : 0, sizeof(STI_DIAG), + pBuffer ? pBuffer->sErrorInfo.dwSize : 0, sizeof(STI_ERROR_INFO), hr)); + m_hrLastEdviceError = STIERR_INVALID_PARAM; + } + + if (SUCCEEDED(hr)) + { + pBuffer->dwVendorDiagCode = 0; + pBuffer->dwStatusMask = 0; + pBuffer->sErrorInfo.dwGenericError = NOERROR; + pBuffer->sErrorInfo.dwVendorError = 0; + memset(pBuffer->sErrorInfo.szExtendedErrorText, 0, sizeof(pBuffer->sErrorInfo.szExtendedErrorText)); + + if (STI_DIAGCODE_HWPRESENCE == pBuffer->dwBasicDiagCode) + { + WIAEX_TRACE((g_hInst, "STI_DIAGCODE_HWPRESENCE..")); + } + else + { + WIAEX_ERROR((g_hInst, "Unknown basic diag code requested (and ignored): %u", pBuffer->dwBasicDiagCode)); + } + } + + WIAEX_TRACE((g_hInst, "IStiUSD::Diagnostic 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::Escape. This method allows an application to send +* directly to the device a proprietary command ID and optionally send +* and receive data. +* +* The sample driver returns STIERR_UNSUPPORTED. +* +* Parameters: +* +* EscapeFunction - caller-supplied, vendor-defined, DWORD-sized value +* representing an I/O operation +* lpInData - caller-supplied pointer to a buffer containing data +* sent to the device +* cbInDataSize - caller-supplied length, in bytes, of the buffer +* pointed to by lpInData +* pOutData - caller-supplied pointer to a memory buffer to +* receive data from the device +* cbOutDataSize - caller-supplied length, in bytes, of the buffer +* pointed to by lpOutData +* pdwActualData - receives the number of bytes actually written to pOutData +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. This driver returns STIERR_UNSUPPORTED. +* +\**************************************************************************/ + +HRESULT CWiaDriver::Escape( + STI_RAW_CONTROL_CODE EscapeFunction, + _In_reads_bytes_(cbInDataSize) + LPVOID lpInData, + DWORD cbInDataSize, + _Out_writes_bytes_(cbOutDataSize) + LPVOID pOutData, + DWORD cbOutDataSize, + _Out_ LPDWORD pdwActualData) +{ + UNREFERENCED_PARAMETER(EscapeFunction); + UNREFERENCED_PARAMETER(cbInDataSize); + UNREFERENCED_PARAMETER(lpInData); + UNREFERENCED_PARAMETER(cbOutDataSize); + UNREFERENCED_PARAMETER(pOutData); + + WIAEX_ERROR((g_hInst, "This method is not implemented or supported for this driver")); + + HRESULT hr = STIERR_UNSUPPORTED; + + if (pdwActualData) + { + *pdwActualData = 0; + } + + m_hrLastEdviceError = hr; + + WIAEX_TRACE((g_hInst, "IStiUSD::Escape 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::LockDevice. The sample driver returns S_OK. This is +* a must considering that the driver cannot function otherwise (the WIA Service +* expects this method to succeed for a properly installed driver and a working +* scanner device). +* +* Parameters: +* +* None +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. This sample driver returns S_OK. +* +\**************************************************************************/ + +HRESULT CWiaDriver::LockDevice() +{ + return S_OK; +} + +/**************************************************************************\ +* +* Implements IStiUSD::UnLockDevice. The sample driver returns S_OK. This is +* a must considering that the driver cannot function otherwise (the WIA Service +* expects this method to succeed for a properly installed driver and a +* working scanner device). +* +* Parameters: +* +* None +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. This sample driver returns S_OK. +* +\**************************************************************************/ + +HRESULT CWiaDriver::UnLockDevice() +{ + return S_OK; +} + +/**************************************************************************\ +* +* Implements IStiUSD::RawReadData. Reads data from the still image device. +* +* Parameters: +* +* lpBuffer - caller-supplied pointer to a buffer to receive data +* read from the device. +* lpdwNumberOfBytes - caller-supplied pointer to a DWORD. The caller loads +* the DWORD with the number of bytes in the buffer pointed +* to by lpBuffer. The driver must replace this value with +* the number of bytes actually read. +* lpOverlapped - optional, caller-supplied pointer to an OVERLAPPED structure +* (described in the Microsoft Windows SDK documentation). +* +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. This driver returns STIERR_UNSUPPORTED. +* +\**************************************************************************/ + +HRESULT CWiaDriver::RawReadData( + _Out_writes_bytes_(*lpdwNumberOfBytes) + LPVOID lpBuffer, + _Inout_ LPDWORD lpdwNumberOfBytes, + _In_opt_ LPOVERLAPPED lpOverlapped) +{ + UNREFERENCED_PARAMETER(lpBuffer); + UNREFERENCED_PARAMETER(lpdwNumberOfBytes); + UNREFERENCED_PARAMETER(lpOverlapped); + + WIAEX_ERROR((g_hInst, "This method is not implemented or supported for this driver")); + + HRESULT hr = STIERR_UNSUPPORTED; + + m_hrLastEdviceError = hr; + + WIAEX_TRACE((g_hInst, "IStiUSD::RawReadData 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::RawWriteData. Writes data to the still image device. +* +* Parameters: +* +* lpBuffer - caller-supplied pointer to a buffer containing data +* to be sent to the device. +* dwNumberOfBytes - caller-supplied number of bytes to be written; this is +* the number of bytes in the buffer pointed to by lpBuffer. +* lpOverlapped - optional, caller-supplied pointer to an OVERLAPPED structure +* (described in the Microsoft Windows SDK documentation). +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. This driver returns STIERR_UNSUPPORTED. +* +\**************************************************************************/ + +HRESULT CWiaDriver::RawWriteData( + _In_reads_bytes_(dwNumberOfBytes) + LPVOID lpBuffer, + DWORD dwNumberOfBytes, + _In_opt_ LPOVERLAPPED lpOverlapped) +{ + UNREFERENCED_PARAMETER(lpBuffer); + UNREFERENCED_PARAMETER(dwNumberOfBytes); + UNREFERENCED_PARAMETER(lpOverlapped); + + WIAEX_ERROR((g_hInst, "This method is not implemented or supported for this driver")); + + HRESULT hr = STIERR_UNSUPPORTED; + + m_hrLastEdviceError = hr; + + WIAEX_TRACE((g_hInst, "IStiUSD::RawWriteData 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::RawReadCommand. Reads command information from +* the still image device. +* +* Parameters: +* +* lpBuffer - caller-supplied pointer to a buffer to receive the +* command read from the device. +* lpdwNumberOfBytes - caller-supplied pointer to a DWORD. The caller loads +* the DWORD with the number of bytes in the buffer pointed +* to by lpBuffer; The driver must replace this value with +* the number of bytes actually read. +* lpOverlapped - optional, caller-supplied pointer to an OVERLAPPED structure +* (described in the Microsoft Windows SDK documentation). +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. This driver returns STIERR_UNSUPPORTED. +* +\**************************************************************************/ + +HRESULT CWiaDriver::RawReadCommand( + _Out_writes_bytes_(*lpdwNumberOfBytes) + LPVOID lpBuffer, + _Inout_ LPDWORD lpdwNumberOfBytes, + _In_opt_ LPOVERLAPPED lpOverlapped) +{ + UNREFERENCED_PARAMETER(lpBuffer); + UNREFERENCED_PARAMETER(lpdwNumberOfBytes); + UNREFERENCED_PARAMETER(lpOverlapped); + + WIAEX_ERROR((g_hInst, "This method is not implemented or supported for this driver")); + + HRESULT hr = STIERR_UNSUPPORTED; + + m_hrLastEdviceError = hr; + + WIAEX_TRACE((g_hInst, "IStiUSD::RawReadCommand 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::RawWriteCommand. Writes command information to +* the still image device. +* +* Parameters: +* +* lpBuffer - caller-supplied pointer to a buffer containing data +* to be sent to the device. +* dwNumberOfBytes - caller-supplied number of bytes to be written; this is +* the number of bytes in the buffer pointed to by lpBuffer. +* lpOverlapped - optional, caller-supplied pointer to an OVERLAPPED structure +* (described in the Microsoft Windows SDK documentation). +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. This driver returns STIERR_UNSUPPORTED. +* +\**************************************************************************/ + +HRESULT CWiaDriver::RawWriteCommand( + _In_reads_bytes_(dwNumberOfBytes) + LPVOID lpBuffer, + DWORD dwNumberOfBytes, + _In_opt_ LPOVERLAPPED lpOverlapped) +{ + UNREFERENCED_PARAMETER(lpBuffer); + UNREFERENCED_PARAMETER(dwNumberOfBytes); + UNREFERENCED_PARAMETER(lpOverlapped); + + WIAEX_ERROR((g_hInst, "This method is not implemented or supported for this driver")); + + HRESULT hr = STIERR_UNSUPPORTED; + + m_hrLastEdviceError = hr; + + WIAEX_TRACE((g_hInst, "IStiUSD::RawWriteCommand 0x%08X", hr)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::GetLastError. Returns the last known error associated +* with the still image device. The driver should record the last STIERR_ error +* code (defined in stierr.h) or generic Win32 error code (retrieved using the +* GetLastError Win32 API) encountered during its operation. +* +* Parameters: +* +* pdwLastDeviceError - caller-supplied pointer to a buffer in which +* the error code will be stored +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. +* +\**************************************************************************/ + +HRESULT CWiaDriver::GetLastError( + _Out_ LPDWORD pdwLastDeviceError) +{ + // + // When this method is called the driver should also check the scanner state + // (as when re-initializing the current WIA_DPS_DOCUMENT_HANDLING_STATUS). + // + HRESULT hr = S_OK; + + if (!pdwLastDeviceError) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + m_hrLastEdviceError = STIERR_INVALID_PARAM; + } + + if (SUCCEEDED(hr)) + { + *pdwLastDeviceError = WIN32_FROM_HRESULT(m_hrLastEdviceError); + } + + WIAEX_TRACE((g_hInst, "IStiUSD::GetLastError 0x%08X (%u, 0x%08X)", hr, m_hrLastEdviceError, m_hrLastEdviceError)); + + return hr; +} + +/**************************************************************************\ +* +* Implements IStiUSD::GetLastErrorInfo. Returns information about the last +* known error associated with a still image device. The driver should record +* the last STIERR_ error code (defined in stierr.h) or generic Win32 error code +* (retrieved using the GetLastError Win32 API) encountered during its operation. +* This error code would be the only information packaged in the STI_ERROR_INFO +* to be returned by this method for the sample driver. The sample driver does +* not provide a vendor error ID and no additional text error description. +* +* Parameters: +* +* pLastErrorInfo - caller-supplied pointer to an STI_ERROR_INFO structure +* to receive error information +* +* Return Value: +* +* If the operation succeeds, the method must return S_OK. Otherwise, it +* should return one of the STIERR-prefixed error codes defined in stierr.h +* or another standard COM error code. +* +\**************************************************************************/ + +HRESULT CWiaDriver::GetLastErrorInfo( + _Out_ STI_ERROR_INFO *pLastErrorInfo) +{ + HRESULT hr = S_OK; + + if (!pLastErrorInfo) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X",hr)); + m_hrLastEdviceError = STIERR_INVALID_PARAM; + } + + if (SUCCEEDED(hr)) + { + memset(pLastErrorInfo, 0, sizeof(STI_ERROR_INFO)); + pLastErrorInfo->dwGenericError = WIN32_FROM_HRESULT(m_hrLastEdviceError); + } + + WIAEX_TRACE((g_hInst, "IStiUSD::GetLastErrorInfo 0x%08X (%u, 0x%08X)", hr, m_hrLastEdviceError, m_hrLastEdviceError)); + + // + // If successful reset the recorded error to not report it more than once: + // + if (SUCCEEDED(hr)) + { + m_hrLastEdviceError = STI_ERROR_NO_ERROR; + } + + return hr; +} diff --git a/wia/ProdScan/Validate.cpp b/wia/ProdScan/Validate.cpp new file mode 100644 index 00000000..44e20b1f --- /dev/null +++ b/wia/ProdScan/Validate.cpp @@ -0,0 +1,1994 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Name: Validate.cpp +* +* Description: This file contains code for WIA property validation +* performed by the Production Scanning Driver Sample +* for special categories of properties such as "format" +* and "scan region/document size" properties. +* +***************************************************************************/ + +#include "stdafx.h" + +/**************************************************************************\ +* +* Validates new current values for the following dependent WIA properties: +* +* WIA_IPA_DATATYPE (*) +* WIA_IPS_CUR_INTENT (*) +* WIA_IPA_DEPTH (*) +* WIA_IPA_FORMAT +* WIA_IPA_TYMED (skipped since its value cannot be changed for this driver) +* WIA_IPA_COMPRESSION +* +* The current values for the following properties could be changed by the driver +* when one of the above mentioned properties is changed: +* +* WIA_IPA_DATATYPE (*) +* WIA_IPS_CUR_INTENT (*) +* WIA_IPA_DEPTH (*) +* WIA_IPA_CHANNELS_PER_PIXEL (*) +* WIA_IPA_BITS_PER_CHANNEL (*) +* WIA_IPA_FORMAT +* WIA_IPA_TYMED (skipped since its value cannot be changed for this driver) +* WIA_IPA_FILENAME_EXTENSION +* WIA_IPA_COMPRESSION +* +* (*) - These properties are available only on the the Flatbed and Feeder items +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* nDocumentHandlingSelect - FLAT or FEED (as defined in wiadef.h) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise (E_INVALIDARG if +* an invalid combination is attempted) +* +\**************************************************************************/ + +HRESULT CWiaDriver::ValidateFormatProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect) +{ + HRESULT hr = S_OK; + + LONG lDataType = WIA_DATA_COLOR; + LONG lIntent = WIA_INTENT_NONE; + LONG lDepth = 24; + LONG lChannelsPerPixel = 3; + LONG lBitsPerChannel = 8; + GUID guidFormat = WiaImgFmt_UNDEFINED; + LONG lCompression = WIA_COMPRESSION_NONE; + BSTR bstrFileExtension = NULL; + BYTE bRawBitsPerChannel[3] = {}; + + BOOL bDataTypeChanged = FALSE; + BOOL bIntentChanged = FALSE; + BOOL bImageTypeIntentChanged = FALSE; + BOOL bDepthChanged = FALSE; + BOOL bFormatChanged = FALSE; + BOOL bCompressionChanged = FALSE; + + PROPSPEC ps[3] = {}; + ULONG nPropSpec = 0; + + if ((!pWiasContext) || (!pPropertyContext)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameters, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + // + // Check which color properties have been changed (ignore failures). + // Note that the Auto item supports only format, tymed and compression: + // + if ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect)) + { + wiasIsPropChanged(WIA_IPA_DATATYPE, pPropertyContext, &bDataTypeChanged); + wiasIsPropChanged(WIA_IPS_CUR_INTENT, pPropertyContext, &bIntentChanged); + wiasIsPropChanged(WIA_IPA_DEPTH, pPropertyContext, &bDepthChanged); + } + wiasIsPropChanged(WIA_IPA_FORMAT, pPropertyContext, &bFormatChanged); + wiasIsPropChanged(WIA_IPA_COMPRESSION, pPropertyContext, &bCompressionChanged); + + if ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect)) + { + // + // Read the current WIA_IPS_CUR_INTENT value: + // + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_CUR_INTENT, &lIntent, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_CUR_INTENT, hr = 0x%08X", hr)); + } + } + + // + // When the intent is changed check if an image type intent flag is set: + // + if (SUCCEEDED(hr)) + { + bImageTypeIntentChanged = (BOOL)(bIntentChanged && (WIA_INTENT_IMAGE_TYPE_MASK & lIntent)); + } + } + } + + // + // Read the other current property values (no matter if each respective property was changed or not): + // + + if (SUCCEEDED(hr) && (bDataTypeChanged || bIntentChanged || bDepthChanged) && + ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect))) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPA_DATATYPE, &lDataType, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_DATATYPE, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPA_DEPTH, &lDepth, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_DEPTH, hr = 0x%08X", hr)); + } + } + } + + if (SUCCEEDED(hr) && (bFormatChanged || bCompressionChanged)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPA_COMPRESSION, &lCompression, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_COMPRESSION, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropGuid(pWiasContext, WIA_IPA_FORMAT, &guidFormat, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_FORMAT, hr = 0x%08X", hr)); + } + } + } + + // + // If the application changed the image type intent the driver must consider + // that WIA_IPA_DATA_TYPE was changed to the value apropriate for the new + // intent no matter if the application set WIA_IPA_DATA_TYPE and to what value: + // + if (SUCCEEDED(hr) && bImageTypeIntentChanged) + { + // + // If multiple color intents are set at the same time consider + // just one and give the highest priority to highest bitdepth: + // + if (WIA_INTENT_IMAGE_TYPE_COLOR & lIntent) + { + bDataTypeChanged = TRUE; + lDataType = WIA_DATA_COLOR; + } + else if (WIA_INTENT_IMAGE_TYPE_GRAYSCALE & lIntent) + { + bDataTypeChanged = TRUE; + lDataType = WIA_DATA_GRAYSCALE; + } + } + + // + // Validate the new current values against the total supported values for + // each of the changed properties with write access in this category: + // + // WIA_IPA_DATATYPE + // WIA_IPS_CUR_INTENT + // WIA_IPA_DEPTH + // WIA_IPA_FORMAT + // WIA_IPA_TYMED (skipped here since its value cannot be changed for this driver) + // WIA_IPA_COMPRESSION + // + // This sample driver can validate color and format properties separately + // since none of its color modes (WIA_IPA_DATATYPE and WIA_IPA_DEPTH + // combinations) are dependent on format changes (WIA_IPA_FORMAT, + // WIA_IPA_TYMED -ignored here- and WIA_IPA_COMPRESSION combinations). + // If the driver would also support WIA_DATA_BW, WIA_COMPRESSION_G4 + // and WiaImgFmt_TIFF the driver would need to validate all the color + // and format properties together (for example WIA_DATA_BW and + // WIA_COMPRESSION_G4 do neither work with WiaImgFmt_EXIF). + // + if (SUCCEEDED(hr) && ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect)) && + (bDataTypeChanged || bIntentChanged || bDepthChanged)) + { + nPropSpec = 0; + + if (bDataTypeChanged) + { + ps[nPropSpec].ulKind = PRSPEC_PROPID; + ps[nPropSpec].propid = WIA_IPA_DATATYPE; + nPropSpec++; + } + + if (bIntentChanged) + { + ps[nPropSpec].ulKind = PRSPEC_PROPID; + ps[nPropSpec].propid = WIA_IPS_CUR_INTENT; + nPropSpec++; + } + + if (bDepthChanged) + { + ps[nPropSpec].ulKind = PRSPEC_PROPID; + ps[nPropSpec].propid = WIA_IPA_DEPTH; + nPropSpec++; + } + + hr = wiasValidateItemProperties(pWiasContext, nPropSpec, ps); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Invalid color property value(s) requested, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr) && (bFormatChanged || bCompressionChanged)) + { + nPropSpec = 0; + + if (bFormatChanged) + { + ps[nPropSpec].ulKind = PRSPEC_PROPID; + ps[nPropSpec].propid = WIA_IPA_FORMAT; + nPropSpec++; + } + + if (bCompressionChanged) + { + ps[nPropSpec].ulKind = PRSPEC_PROPID; + ps[nPropSpec].propid = WIA_IPA_COMPRESSION; + nPropSpec++; + } + + hr = wiasValidateItemProperties(pWiasContext, nPropSpec, ps); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Invalid format property value(s) requested, hr = 0x%08X", hr)); + } + } + + // + // Additional validation for WIA_IPA_DATATYPE and WIA_IPA_DEPTH: + // + if (SUCCEEDED(hr) && ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect)) && + (bDataTypeChanged || bDepthChanged)) + { + if (bDataTypeChanged && bDepthChanged) + { + if (((WIA_DATA_COLOR == lDataType) && (8 == lDepth)) || + ((WIA_DATA_GRAYSCALE == lDataType) && (24 == lDepth))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Unsupported data type (%u) - depth (%u) combination requested, hr = 0x%08X", + lDataType, lDepth, hr)); + } + } + else if (bDataTypeChanged && (!bDepthChanged)) + { + if (WIA_DATA_COLOR == lDataType) + { + lDepth = 24; + } + else if (WIA_DATA_GRAYSCALE == lDataType) + { + lDepth = 8; + } + else if (WIA_DATA_AUTO == lDataType) + { + lDepth = WIA_DEPTH_AUTO; + } + } + else if ((!bDataTypeChanged) && bDepthChanged) + { + if (8 == lDepth) + { + lDataType = WIA_DATA_GRAYSCALE; + } + else if (24 == lDepth) + { + lDataType = WIA_DATA_COLOR; + } + else if (WIA_DEPTH_AUTO == lDepth) + { + lDataType = WIA_DATA_AUTO; + } + } + } + + // + // Additional validation for WIA_IPA_FORMAT and WIA_IPA_COMPRESSION (skipped for the + // sample non-image sources since those do not support compresssed data transfers): + // + if (SUCCEEDED(hr) && ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect) || + (AUTO_SOURCE == nDocumentHandlingSelect)) && (bFormatChanged || bCompressionChanged)) + { + if (bFormatChanged && (!bCompressionChanged)) + { + // + // If WIA_IPA_FORMAT if changed alone, update WIA_IPA_COMPRESSION to match: + // + if (IsEqualGUID(guidFormat, WiaImgFmt_EXIF)) + { + lCompression = WIA_COMPRESSION_JPEG; + } + else if (IsEqualGUID(guidFormat, WiaImgFmt_BMP) || IsEqualGUID(guidFormat, WiaImgFmt_RAW)) + { + lCompression = WIA_COMPRESSION_NONE; + } + } + else if ((!bFormatChanged) && bCompressionChanged) + { + // + // If WIA_IPA_COMPRESSION if changed alone, update WIA_IPA_FORMAT to match: + // + if ((WIA_COMPRESSION_JPEG == lCompression) || (WIA_COMPRESSION_AUTO == lCompression)) + { + guidFormat = WiaImgFmt_EXIF; + } + else if (WIA_COMPRESSION_NONE == lCompression) + { + guidFormat = WiaImgFmt_BMP; + } + } + else if (bFormatChanged && bCompressionChanged) + { + // + // If both WIA_IPA_FORMAT and WIA_IPA_COMPRESSION are changed, verify that their values work together: + // + if (((WIA_COMPRESSION_NONE == lCompression) && IsEqualGUID(guidFormat, WiaImgFmt_EXIF)) || + (((WIA_COMPRESSION_JPEG == lCompression) || (WIA_COMPRESSION_AUTO == lCompression)) && + (IsEqualGUID(guidFormat, WiaImgFmt_BMP) || IsEqualGUID(guidFormat, WiaImgFmt_RAW)))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Unsupported file format - compression mode combination, hr = 0x%08X", hr)); + } + } + } + + // + // Update current values: + // + + if (SUCCEEDED(hr) && ((FLAT == nDocumentHandlingSelect) || (FEED == nDocumentHandlingSelect)) && + (bDataTypeChanged || bIntentChanged || bDepthChanged)) + { + // + // WIA_IPA_DATATYPE: + // + hr = wiasWritePropLong(pWiasContext, WIA_IPA_DATATYPE, lDataType); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_DATATYPE, hr = 0x%08X", hr)); + } + else + { + wiasSetPropChanged(WIA_IPA_DATATYPE, pPropertyContext, TRUE); + } + + // + // WIA_IPS_CUR_INTENT + // + // If an image type intent is set we must make sure it matches the current WIA_IPA_DATATYPE. + // Don't do anything if the application changes any of the other intent flags. + // + if (SUCCEEDED(hr) && (lIntent & WIA_INTENT_IMAGE_TYPE_MASK)) + { + // + // Reset all current image type intent flags. + // + lIntent &= ~ WIA_INTENT_IMAGE_TYPE_MASK; + + // + // .. and add just the one apropriate with the current WIA_IPA_DATATYPE value: + // + + switch (lDataType) + { + case WIA_DATA_COLOR: + lIntent |= WIA_INTENT_IMAGE_TYPE_COLOR; + break; + + case WIA_DATA_GRAYSCALE: + lIntent |= WIA_INTENT_IMAGE_TYPE_GRAYSCALE; + } + + hr = wiasWritePropLong(pWiasContext, WIA_IPS_CUR_INTENT, lIntent); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_CUR_INTENT, hr = 0x%08X", hr)); + } + else + { + wiasSetPropChanged(WIA_IPS_CUR_INTENT, pPropertyContext, TRUE); + } + } + + // + // WIA_IPA_DEPTH: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPA_DEPTH, lDepth); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_DEPTH, hr = 0x%08X", hr)); + } + else + { + wiasSetPropChanged(WIA_IPA_DEPTH, pPropertyContext, TRUE); + } + } + + // + // WIA_IPA_CHANNELS_PER_PIXEL: + // + if (SUCCEEDED(hr)) + { + lChannelsPerPixel = (24 == lDepth) ? 3 : 1; + + hr = wiasWritePropLong(pWiasContext, WIA_IPA_CHANNELS_PER_PIXEL, lChannelsPerPixel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_CHANNELS_PER_PIXEL, hr = 0x%08X", hr)); + } + else + { + wiasSetPropChanged(WIA_IPA_CHANNELS_PER_PIXEL, pPropertyContext, TRUE); + } + } + + // + // WIA_IPA_BITS_PER_CHANNEL: + // + if (SUCCEEDED(hr)) + { + lBitsPerChannel = 8; + + hr = wiasWritePropLong(pWiasContext, WIA_IPA_BITS_PER_CHANNEL, lBitsPerChannel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_BITS_PER_CHANNEL, hr = 0x%08X", hr)); + } + else + { + wiasSetPropChanged(WIA_IPA_BITS_PER_CHANNEL, pPropertyContext, TRUE); + } + } + + // + // WIA_IPA_RAW_BITS_PER_CHANNEL + // + if (SUCCEEDED(hr)) + { + for (int i = 0; i < lChannelsPerPixel; i++) + { + bRawBitsPerChannel[i] = 8; + } + + hr = wiasWritePropBin(pWiasContext, WIA_IPA_RAW_BITS_PER_CHANNEL, lChannelsPerPixel, bRawBitsPerChannel); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_RAW_BITS_PER_CHANNEL, hr = 0x%08X", hr)); + } + else + { + wiasSetPropChanged(WIA_IPA_RAW_BITS_PER_CHANNEL, pPropertyContext, TRUE); + } + } + } + + if (SUCCEEDED(hr) && (bFormatChanged || bCompressionChanged)) + { + // + // WIA_IPA_COMPRESSION: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPA_COMPRESSION, lCompression); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_COMPRESSION, hr = 0x%08X", hr)); + } + else + { + wiasSetPropChanged(WIA_IPA_COMPRESSION, pPropertyContext, TRUE); + } + } + + // + // WIA_IPA_FORMAT: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropGuid(pWiasContext, WIA_IPA_FORMAT, guidFormat); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_FORMAT, hr = 0x%08X", hr)); + } + else + { + wiasSetPropChanged(WIA_IPA_COMPRESSION, pPropertyContext, TRUE); + } + } + + // + // WIA_IPA_FILENAME_EXTENSION: + // + + if (SUCCEEDED(hr)) + { + if (IsEqualGUID(guidFormat, WiaImgFmt_BMP)) + { + bstrFileExtension = SysAllocString(FILE_EXT_BMP); + } + else if (IsEqualGUID(guidFormat, WiaImgFmt_EXIF)) + { + bstrFileExtension = SysAllocString(FILE_EXT_JPG); + } + else if (IsEqualGUID(guidFormat, WiaImgFmt_RAW) || IsEqualGUID(guidFormat, WiaImgFmt_RAWBAR) || + IsEqualGUID(guidFormat, WiaImgFmt_RAWPAT) || IsEqualGUID(guidFormat, WiaImgFmt_RAWMIC)) + { + bstrFileExtension = SysAllocString(FILE_EXT_RAW); + } + else if (IsEqualGUID(guidFormat, WiaImgFmt_CSV)) + { + bstrFileExtension = SysAllocString(FILE_EXT_CSV); + } + else if (IsEqualGUID(guidFormat, WiaImgFmt_TXT)) + { + bstrFileExtension = SysAllocString(FILE_EXT_TXT); + } + else if (IsEqualGUID(guidFormat, WiaImgFmt_XMLBAR) || IsEqualGUID(guidFormat, WiaImgFmt_XMLPAT) || + IsEqualGUID(guidFormat, WiaImgFmt_XMLMIC)) + { + bstrFileExtension = SysAllocString(FILE_EXT_XML); + } + else + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Unsupported file format, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr) && (!bstrFileExtension)) + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Unable to allocate memory for new file extension, hr = 0x%08X", hr)); + } + + if (SUCCEEDED(hr)) + { + hr = wiasWritePropStr(pWiasContext, WIA_IPA_FILENAME_EXTENSION, bstrFileExtension); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_FILENAME_EXTENSION, hr = 0x%08X", hr)); + } + } + } + + if (bstrFileExtension) + { + SysFreeString(bstrFileExtension); + } + + return hr; +} + +/**************************************************************************\ +* +* Helper for IWiaMinIDrv::drvValidateItemProperties. +* +* Updates the following "image information" properties: +* +* WIA_IPA_PIXELS_PER_LINE +* WIA_IPA_NUMBER_OF_LINES +* WIA_IPA_BYTES_PER_LINE +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* nDocumentHandlingSelect - FLAT or FEED (as defined in wiadef.h) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::ValidateImageInfoProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect) +{ + HRESULT hr = S_OK; + LONG lXExtent = 0; + LONG lYExtent = 0; + LONG lPixelsPerLine = 0; + LONG lNumberOfLines = 0; + LONG lDepth = 0; + LONG lBytesPerLine = 0; + + if ((!pWiasContext) || (!pPropertyContext) || (AUTO_SOURCE == nDocumentHandlingSelect)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameters, hr = 0x%08X", hr)); + } + + // + // Read the current extent and bit depth values: + // + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPA_DEPTH, &lDepth, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPA_DEPTH, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_XEXTENT, &lXExtent, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_XEXTENT, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_YEXTENT, &lYExtent, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_YEXTENT, hr = 0x%08X", hr)); + } + } + + // + // WIA_IPA_PIXELS_PER_LINE - the image width, in pixels, for the final image + // WIA_IPA_NUMBER_OF_LINES - the image length, in pixels, for the final image + // WIA_IPA_BYTES_PER_LINE - line width in bytes matching WIA_IPA_PIXELS_PER_LINE and WIA_IPA_DEPTH + // + + lPixelsPerLine = lXExtent; + lNumberOfLines = lYExtent; + lBytesPerLine = BytesPerLine(lPixelsPerLine, lDepth); + + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPA_PIXELS_PER_LINE, lPixelsPerLine); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_PIXELS_PER_LINE, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPA_NUMBER_OF_LINES, lNumberOfLines); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_NUMBER_OF_LINES, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPA_BYTES_PER_LINE, lBytesPerLine); + + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPA_BYTES_PER_LINE, hr = 0x%08X", hr)); + } + } + + return hr; +} + +/**************************************************************************\ +* +* Validates new current values for the following dependent WIA properties: +* +* WIA_IPS_PAGE_SIZE +* WIA_IPS_ORIENTATION +* WIA_IPS_PAGE_WIDTH +* WIA_IPS_PAGE_HEIGHT +* WIA_IPS_XPOS +* WIA_IPS_YPOS +* WIA_IPS_XEXTENT +* WIA_IPS_YEXTENT +* WIA_IPS_XRES +* WIA_IPS_YRES +* WIA_IPS_XSCALING +* WIA_IPS_YSCALING +* WIA_IPS_LONG_DOCUMENT +* +* If the application changes the document size and orientation the driver +* should update the current and valid origin and extent properties to match +* at the current resolution the current full document dimensions. +* +* Resolution changes should also cause updated origins and extents (valid and current). +* +* Orientation changes should result in updated valid page size lists and +* updated current and valid origin and extents values. +* +* The driver is going to consider the origins and extents to configure +* the scan area (crop region) and the current page size to configure +* the physical document size, if needed. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* nDocumentHandlingSelect - FLAT or FEED (as defined in wiadef.h) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise (E_INVALIDARG if +* an invalid combination is attempted) +* +\**************************************************************************/ + +HRESULT CWiaDriver::ValidateRegionProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect) +{ + HRESULT hr = S_OK; + + if ((!pWiasContext) || (!pPropertyContext) || (AUTO_SOURCE == nDocumentHandlingSelect)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameters, hr = 0x%08X", hr)); + } + + LONG lXRes = 0; + LONG lYRes = 0; + LONG lMinXExtent = (MIN_SCAN_AREA_WIDTH * OPTICAL_RESOLUTION) / 1000; + LONG lMinYExtent = (MIN_SCAN_AREA_HEIGHT * OPTICAL_RESOLUTION) / 1000; + LONG lMaxXExtent = (MAX_SCAN_AREA_WIDTH * OPTICAL_RESOLUTION) / 1000; + LONG lMaxYExtent = (MAX_SCAN_AREA_HEIGHT * OPTICAL_RESOLUTION) / 1000; + + LONG lXPos = 0; + LONG lYPos = 0; + LONG lXExtent = lMaxXExtent; + LONG lYExtent = lMaxYExtent; + + LONG lPageWidth = MAX_SCAN_AREA_WIDTH; + LONG lPageHeight = MAX_SCAN_AREA_HEIGHT; + LONG lPageSize = WIA_PAGE_LETTER; + LONG lOrientation = PORTRAIT; + LONG lMaxWidth = MAX_SCAN_AREA_WIDTH; + LONG lMaxHeight = MAX_SCAN_AREA_HEIGHT; + LONG lLongDocument = WIA_LONG_DOCUMENT_DISABLED; + + BOOL bPageSizeChanged = FALSE; + BOOL bOrientationChanged = FALSE; + BOOL bLongDocumentChanged = FALSE; + + // + // Read the current property values (no matter if they were changed or not): + // + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_XPOS, &lXPos, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_XPOS, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_YPOS, &lYPos, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_YPOS, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_XEXTENT, &lXExtent, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_XEXTENT, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_YEXTENT, &lYExtent, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_YEXTENT, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_XRES, &lXRes, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_XRES, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_YRES, &lYRes, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_YRES, hr = 0x%08X", hr)); + } + } + + // + // In order to validate scan frame or resolution changes we must know + // the maximum scan area size. For Flatbed this is the total bed size. + // For Feeder this is the size of the currently selected document size. + // + if (SUCCEEDED(hr) &&(FEED == nDocumentHandlingSelect)) + { + wiasIsPropChanged(WIA_IPS_PAGE_SIZE, pPropertyContext, &bPageSizeChanged); + wiasIsPropChanged(WIA_IPS_ORIENTATION, pPropertyContext, &bOrientationChanged); + wiasIsPropChanged(WIA_IPS_LONG_DOCUMENT, pPropertyContext, &bLongDocumentChanged); + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_ORIENTATION, &lOrientation, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_ORIENTATION, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_PAGE_SIZE, &lPageSize, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_PAGE_SIZE, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_LONG_DOCUMENT, &lLongDocument, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_LONG_DOCUMENT, hr = 0x%08X", hr)); + } + } + + // + // When WIA_LONG_DOCUMENT_ENABLED is set, if the application is not doing this update, + // the driver must update itself the current WIA_IPS_PAGE_SIZE property value to WIA_PAGE_AUTO. + // If WIA_LONG_DOCUMENT_ENABLED is set and the application changes WIA_IPS_PAGE_SIZE to + // another value than WIA_PAGE_AUTO, the driver must self-update the WIA_IPS_LONG_DOCUMENT + // property to WIA_LONG_DOCUMENT_DISABLED. + // + if (SUCCEEDED(hr) && (bPageSizeChanged || bLongDocumentChanged)) + { + if ((WIA_PAGE_AUTO != lPageSize) && (WIA_LONG_DOCUMENT_ENABLED == lLongDocument)) + { + if (bLongDocumentChanged && bPageSizeChanged) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid WIA_IPS_PAGE_SIZE value for WIA_LONG_DOCUMENT_ENABLED, hr = 0x%08X", hr)); + } + else if (bLongDocumentChanged && (!bPageSizeChanged)) + { + lPageSize = WIA_PAGE_AUTO; + bPageSizeChanged = TRUE; + } + else if ((!bLongDocumentChanged) && bPageSizeChanged) + { + lLongDocument = WIA_LONG_DOCUMENT_DISABLED; + bLongDocumentChanged = TRUE; + } + } + } + + // + // Validate the current WIA_IPS_PAGE_SIZE against the valid values + // apropriate with the current WIA_IPS_ORIENTATION: + // + if (SUCCEEDED(hr) && (bPageSizeChanged || bOrientationChanged)) + { + CBasicDynamicArray<LONG> &lPageSizesArray = (PORTRAIT == lOrientation) ? m_lPortraitSizesArray : m_lLandscapeSizesArray; + + if (-1 == lPageSizesArray.Find(lPageSize)) + { + if (bPageSizeChanged) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid WIA_IPS_PAGE_SIZE value, hr = 0x%08X", hr)); + } + else if (WIA_PAGE_AUTO != lPageSize) + { + // + // This means that the application did not request the page size + // to be changed, just changed the orientation and the current + // page size is not supported in the new orientation so we should + // quietly select WIA_PAGE_CUSTOM: + // + lPageSize = WIA_PAGE_CUSTOM; + lPageWidth = lMaxWidth; + lPageHeight = lMaxHeight; + bPageSizeChanged = TRUE; + } + } + else + { + // + // For WIA_PAGE_CUSTOM the page dimensions are the maximum dimensions of the scan area, + // same for WIA_PAGE_AUTO (real page dimensions being detected here only after the + // document has been scanned and reflected in the final image layout and dimensions): + // + if ((WIA_PAGE_CUSTOM == lPageSize) || (WIA_PAGE_AUTO == lPageSize)) + { + lPageWidth = lMaxWidth; + lPageHeight = lMaxHeight; + } + else + { + // + // The page sizes from the current array are guaranteed to fit in + // the total scan acquisition area limits, minimum and maximum: + // + hr = GetPageDimensions(lPageSize, (BOOL)(PORTRAIT == lOrientation), lPageWidth, lPageHeight); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Unable to retrieve dimensions for the current document size, hr = 0x%08X", hr)); + } + } + } + + // + // For Feeder the actual maximum dimensions for the scan area + // are dictated by the current document size selected: + // + if (SUCCEEDED(hr)) + { + lMaxWidth = lPageWidth; + lMaxHeight = lPageHeight; + } + } + } + + // + // Use wiasUpdateScanRect to validate the current extents and + // resolutions and update all their dependent properties: + // + if (SUCCEEDED(hr)) + { + hr = wiasUpdateScanRect(pWiasContext, pPropertyContext, lMaxWidth, lMaxHeight); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "wiasUpdateScanRect(max width = %u, max height = %u) failed, hr = 0x%08X", lMaxWidth, lMaxHeight, hr)); + } + } + + // + // For feeder special validation must be performed for WIA_IPS_ORIENTATION and WIA_IPS_PAGE_SIZE changes: + // + if (FEED == nDocumentHandlingSelect) + { + // + // Update valid list of values for WIA_IPS_PAGE_SIZE according with the new set WIA_IPS_ORIENTATION: + // + if (SUCCEEDED(hr) && bOrientationChanged) + { + LONG lNumPageSizes = 0; + CBasicDynamicArray<LONG> &lPageSizesArray = (PORTRAIT == lOrientation) ? m_lPortraitSizesArray : m_lLandscapeSizesArray; + lNumPageSizes = lPageSizesArray.Size(); + + hr = wiasSetValidListLong(pWiasContext, WIA_IPS_PAGE_SIZE, (ULONG)lNumPageSizes, + lPageSize, (LONG *)lPageSizesArray.Array()); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update valid WIA_IPS_PAGE_SIZE values, hr = 0x%08X", hr)); + } + } + + // + // If the current WIA_IPS_PAGE_SIZE is changed updated all the dependent properties: + // + if (SUCCEEDED(hr) && bPageSizeChanged) + { + // + // Update current WIA_IPS_PAGE_SIZE value: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_PAGE_SIZE, lPageSize); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_PAGE_SIZE, hr = 0x%08X", hr)); + } + } + + // + // Update current WIA_IPS_PAGE_WIDTH: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_PAGE_WIDTH, lPageWidth); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_PAGE_WIDTH, hr = 0x%08X", hr)); + } + } + + // + // Update current WIA_IPS_LONG_DOCUMENT: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_LONG_DOCUMENT, lLongDocument); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_PAGE_HEIGHT, hr = 0x%08X", hr)); + } + } + + // + // Update current WIA_IPS_PAGE_HEIGHT: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_PAGE_HEIGHT, lPageHeight); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_PAGE_HEIGHT, hr = 0x%08X", hr)); + } + } + + // + // If the current page size is changed always update the current and valid + // WIA_IPS_XPOS, WIA_IPS_YPOS, WIA_IPS_XEXTENT and WIA_IPS_YEXTENT to match + // the entire area of the currently selected document size, overwriting any + // direct change for any of these properties requested at the same time with + // the change for WIA_IPS_ORIENTATION or WIA_IPS_PAGESIZE. + // + + // + // Read current WIA_IPS_XRES: + // + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_XRES, &lXRes, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_XRES, hr = 0x%08X", hr)); + } + } + + // + // Read current WIA_IPS_YRES: + // + if (SUCCEEDED(hr)) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_YRES, &lYRes, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_YRES, hr = 0x%08X", hr)); + } + } + + // + // Compute the new extent limits: + // + if (SUCCEEDED(hr)) + { + lMinXExtent = (MIN_SCAN_AREA_WIDTH * lXRes) / 1000; + if (!lMinXExtent) + { + lMinXExtent = 1; + } + lMinYExtent = (MIN_SCAN_AREA_HEIGHT * lYRes) / 1000; + if (!lMinYExtent) + { + lMinYExtent = 1; + } + lMaxXExtent = (lPageWidth * lXRes) / 1000; + lMaxYExtent = (lPageHeight * lYRes) / 1000; + + if ((lMaxXExtent < 1) || (lMaxYExtent < 1) || (lMinXExtent > lMaxXExtent) || (lMinYExtent > lMaxYExtent)) + { + hr = E_FAIL; + WIAEX_ERROR((g_hInst, "Unable to update the extent limits to match the new document size, hr = 0x%08X", hr)); + } + } + + if (SUCCEEDED(hr)) + { + // + // The new current scan region, covering the entire document size: + // + lXPos = 0; + lYPos = 0; + lXExtent = lMaxXExtent; + lYExtent = lMaxYExtent; + + // + // Set new valid values for WIA_IPS_XPOS: + // + hr = wiasSetValidRangeLong(pWiasContext, WIA_IPS_XPOS, 0, lXPos, lMaxXExtent - lMinXExtent, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update valid WIA_IPS_XPOS, hr = 0x%08X", hr)); + } + + // + // Set new valid values for WIA_IPS_YPOS: + // + if (SUCCEEDED(hr)) + { + hr = wiasSetValidRangeLong(pWiasContext, WIA_IPS_YPOS, 0, lYPos, lMaxYExtent - lMinYExtent, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update valid WIA_IPS_YPOS, hr = 0x%08X", hr)); + } + } + + // + // Set new valid values for WIA_IPS_XEXTENT: + // + if (SUCCEEDED(hr)) + { + hr = wiasSetValidRangeLong(pWiasContext, WIA_IPS_XEXTENT, lMinXExtent, lXExtent, lMaxXExtent - lXPos, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update valid WIA_IPS_XEXTENT, hr = 0x%08X", hr)); + } + } + + // + // Set new valid values for WIA_IPS_XEXTENT: + // + if (SUCCEEDED(hr)) + { + hr = wiasSetValidRangeLong(pWiasContext, WIA_IPS_YEXTENT, lMinYExtent, lYExtent, lMaxYExtent - lYPos, 1); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update valid WIA_IPS_YEXTENT, hr = 0x%08X", hr)); + } + } + + // + // Set new current value for WIA_IPS_XPOS: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_XPOS, lXPos); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_XPOS, hr = 0x%08X", hr)); + } + } + + // + // Set new current value for WIA_IPS_YPOS: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_YPOS, lYPos); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_YPOS, hr = 0x%08X", hr)); + } + } + + // + // Set new current value for WIA_IPS_XEXTENT: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_XEXTENT, lXExtent); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_XEXTENT, hr = 0x%08X", hr)); + } + } + + // + // Set new current value for WIA_IPS_YEXTENT: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_YEXTENT, lYExtent); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update WIA_IPS_YEXTENT, hr = 0x%08X", hr)); + } + } + } + } + } + + return hr; +} + +/**************************************************************************\ +* +* Validates new current values for the following dependent WIA properties: +* +* WIA_IPS_DOCUMENT_HANDLING_SELECT: either DUPLEX or FRONT_ONLY can be set +* +* WIA_IPS_PAGES: the valid range and current value are updated to match +* the current WIA_IPS_DOCUMENT_HANDLING_SELECT +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise (E_INVALIDARG if +* an invalid combination is attempted) +* +\**************************************************************************/ + +HRESULT CWiaDriver::ValidateFeedProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext) +{ + HRESULT hr = S_OK; + BOOL bHandlingSelectChanged = TRUE; + LONG lFeederHandlingSelect = FRONT_ONLY; + BOOL bPagesChanged = TRUE; + LONG lPages = 1; + LONG lMaxPages = 0x7FFFFFFF; //maximum value for a signed 32-bit integer + LONG lMinPages = 0; //ALL_PAGES + LONG lStepPages = 1; + WIAS_CHANGED_VALUE_INFO wiasValInfo = {}; + + if ((!pWiasContext) || (!pPropertyContext)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameters, hr = 0x%08X", hr)); + } + + // + // Check if WIA_IPS_DOCUMENT_HANDLING_SELECT was changed: + // + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(wiasGetChangedValueLong(pWiasContext, pPropertyContext, TRUE, WIA_IPS_DOCUMENT_HANDLING_SELECT, &wiasValInfo))) + { + lFeederHandlingSelect = wiasValInfo.Current.lVal; + bHandlingSelectChanged = wiasValInfo.bChanged; + } + else + { + bHandlingSelectChanged = FALSE; + } + } + + // + // Check if WIA_IPS_PAGES was changed and read its current value: + // + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(wiasGetChangedValueLong(pWiasContext, pPropertyContext, TRUE, WIA_IPS_PAGES, &wiasValInfo))) + { + lPages = wiasValInfo.Current.lVal; + bPagesChanged = wiasValInfo.bChanged; + } + else + { + bPagesChanged = FALSE; + + if (bHandlingSelectChanged) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_PAGES, &lPages, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_PAGES on the Feeder item, hr = 0x%08X", hr)); + } + } + } + } + + // + // If WIA_IPS_DOCUMENT_HANDLING_SELECT is changed verify that only FRONT_ONLY + // or DUPLEX is requested to be set, only one at a time: + // + if (SUCCEEDED(hr) && bHandlingSelectChanged) + { + if ((DUPLEX != lFeederHandlingSelect) && (FRONT_ONLY != lFeederHandlingSelect)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, + "WIA_IPS_DOCUMENT_HANDLING_SELECT validation failed, only FRONT_ONLY and DUPLEX are valid, only one at a time, hr = 0x%08X", hr)); + } + } + + // + // Read the current WIA_IPS_DOCUMENT_HANDLING_SELECT value if not changed and WIA_IPS_PAGES is changed: + // + if (SUCCEEDED(hr) && (!bHandlingSelectChanged) && bPagesChanged) + { + hr = wiasReadPropLong(pWiasContext, WIA_IPS_DOCUMENT_HANDLING_SELECT, &lFeederHandlingSelect, NULL, TRUE); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Error reading current WIA_IPS_DOCUMENT_HANDLING_SELECT on the Feeder item, hr = 0x%08X", hr)); + } + } + + // + // If WIA_IPS_DOCUMENT_HANDLING_SELECT is changed and WIA_IPS_PAGES is not, update the valid and, + // if needed, current values for WIA_IPS_PAGES. Note that the step WIA_IPS_PAGES value must remain + // 1 for duplex in order to allow a legacy WIA 1.0 application to indirectly disable duplex by + // settings WIA_DPS_PAGES to 1: + // + if (SUCCEEDED(hr) && bHandlingSelectChanged && (!bPagesChanged)) + { + if (DUPLEX == lFeederHandlingSelect) + { + lMaxPages = 0x7FFFFFFE; //maximum even value for a signed 32-bit integer + lMinPages = 0; //ALL_PAGES + lStepPages = 1; //not 2 + + // + // Round up the current WIA_IPS_PAGES value to the nearest even number: + // + if ((lPages > 0) && (lPages % 2)) + { + if (lPages <= (lMaxPages - 1)) + { + lPages += 1; + } + else if (lPages >= (lMinPages + 2)) + { + lPages -= 1; + } + else + { + lPages = 2; + } + } + } + else + { + lMaxPages = 0x7FFFFFFF; //maximum value for a signed 32-bit integer + lMinPages = 0; //ALL_PAGES + lStepPages = 1; + } + + // + // Update the range of valid WIA_IPS_PAGES values: + // + hr = wiasSetValidRangeLong(pWiasContext, WIA_IPS_PAGES, lMinPages, lPages, lMaxPages, lStepPages); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update valid WIA_IPS_PAGES, hr = 0x%08X", hr)); + } + + // + // Update the current WIA_IPS_PAGES value: + // + if (SUCCEEDED(hr)) + { + hr = wiasWritePropLong(pWiasContext, WIA_IPS_PAGES, lPages); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update current WIA_IPS_PAGES, hr = 0x%08X", hr)); + } + } + } + + // + // If WIA_IPS_PAGES is changed and WIA_IPS_DOCUMENT_HANDLING_SELECT is not, check if + // we need to disable duplex as result of a WIA_IPS_PAGES current value change to 1. + // We'll leave duplex enabled if the application changes WIA_IPS_PAGE to another + // odd value, the application being responsible in this case to decide itself if + // these pages are to be scanned duplex (and the last side discarded) or simplex: + // + if (SUCCEEDED(hr) && (!bHandlingSelectChanged) && bPagesChanged) + { + if ((DUPLEX == lFeederHandlingSelect) && (1 == lPages)) + { + lFeederHandlingSelect = FRONT_ONLY; + + hr = wiasWritePropLong(pWiasContext, WIA_IPS_DOCUMENT_HANDLING_SELECT, lFeederHandlingSelect); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to update current WIA_IPS_DOCUMENT_HANDLING_SELECT, hr = 0x%08X", hr)); + } + } + } + + // + // If both WIA_IPS_PAGES and WIA_IPS_DOCUMENT_HANDLING_SELECT are changed at the + // same time, check if DUPLEX and an odd WIA_IPS_PAGES value are set. We'll allow + // odd values other than 1 while DUPLEX is set but not 1: + // + if (SUCCEEDED(hr) && bHandlingSelectChanged && bPagesChanged) + { + if ((DUPLEX == lFeederHandlingSelect) && (1 == lPages)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "WIA_IPS_PAGES cannot be set to 1 while setting WIA_IPS_DOCUMENT_HADLING_SELECT to DUPLEX, hr = 0x%08X", hr)); + } + } + + return hr; +} + +/**************************************************************************\ +* +* Helper for IWiaMinIDrv::drvValidateItemProperties. +* +* Executes additional validation for the imprinter/endorser specific properties. +* The only imprinter/enorser property that this sample driver needs to validate +* here is WIA_IPS_PRINTER_ENDORSER_STRING - the characters submitted by the +* application must match WIA_IPS_PRINTER_ENDORSER_VALID_CHARACTERS. Unsupported +* WIA_IPS_PRINTER_ENDORSER_VALID_FORMAT_SPECIFIERS are quietly ignored by the +* driver (pretended to be printed/endorsed as-is). +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* nDocumentHandlingSelect - IMPRINTER or ENDORSER (defined in wiadef.h) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::ValidateImprinterEndorserProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect) +{ + HRESULT hr = S_OK; + BOOL bStringChanged = TRUE; + BSTR bstrNewString = NULL; + BSTR bstrOldString = NULL; + WIAS_CHANGED_VALUE_INFO wiasValInfo = {}; + + if ((!pWiasContext) || (!pPropertyContext) || + ((IMPRINTER != nDocumentHandlingSelect) && (ENDORSER != nDocumentHandlingSelect))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::ValidateImprinterEndorserProperties failed, invalid parameter, hr = 0x%08X", hr)); + } + + // + // Check if WIA_IPS_PRINTER_ENDORSER_STRING is changed by the application: + // + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(wiasGetChangedValueStr(pWiasContext, pPropertyContext, TRUE, WIA_IPS_PRINTER_ENDORSER_STRING, &wiasValInfo))) + { + // + // wiasGetChangedValueStr allocates both Current and Old BSTRs, read them both + // even if not using the Old value, we'll need to free both of them when done: + // + bstrNewString = wiasValInfo.Current.bstrVal; + bstrOldString = wiasValInfo.Old.bstrVal; + bStringChanged = wiasValInfo.bChanged; + } + else + { + bStringChanged = FALSE; + } + } + + // + // If WIA_IPS_PRINTER_ENDORSER_STRING is changed, check if all characters are valid: + // + if (SUCCEEDED(hr) && bStringChanged && bstrNewString) + { + 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; + ULONG i = 0; + BOOL bFound = FALSE; + + while (bstrNewString[i] != NULL) + { + bFound = FALSE; + + for (ULONG j = 0; j < ulValidChars; j++) + { + if (bstrNewString[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", + bstrNewString[i], bstrNewString[i], hr)); + break; + } + } + } + + // + // This sample driver supports only one line of text for its Imprinter/Endorser. + // Check that the new WIA_IPS_PRINTER_ENDORSER_STRING value does not contain any + // special '$N$'sequences ('new line'). This sample driver will ignore other + // formatting sequences that may be contained by the new string: + // + if (SUCCEEDED(hr) && bStringChanged && bstrNewString) + { + WCHAR szNewLine[] = L"$N$"; + + if (wcsstr(bstrNewString, szNewLine)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid new line ($N$) format sequence for WIA_IPS_PRINTER_ENDORSER_STRING, only one line of text supported, submitted value: %ws, hr = 0x%08X", + bstrNewString, hr)); + } + } + + // + // Important: both the WIAS_CHANGED_VALUE_INFO::Current.bstrVal and + // WIAS_CHANGED_VALUE_INFO::Old.bstrVal BSTRs must be freed. + // + if (bstrNewString) + { + SysFreeString(bstrNewString); + } + if (bstrOldString) + { + SysFreeString(bstrOldString); + } + + return hr; +} + +/**************************************************************************\ +* +* Helper for IWiaMinIDrv::drvValidateItemProperties. +* +* Validates WIA_IPS_ENABLED_BARCODE_TYPES +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::ValidateBarcodeReaderProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext) +{ + HRESULT hr = S_OK; + BOOL bBarcodeTypesChanged = TRUE; + + if ((!pWiasContext) || (!pPropertyContext)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::ValidateImprinterEndorserProperties failed, invalid parameter, hr = 0x%08X", hr)); + } + + // + // Check if WIA_IPS_ENABLED_BARCODE_TYPES is changed by the application: + // + if (SUCCEEDED(hr)) + { + // + // wiasIsPropChanged fails if the property is not changed, do not fail ValidateBarcodeReaderProperties: + // + wiasIsPropChanged(WIA_IPS_ENABLED_BARCODE_TYPES, pPropertyContext, &bBarcodeTypesChanged); + } + + // + // If WIA_IPS_ENABLED_BARCODE_TYPES is changed, read the new (vector) array of values and validate against the valid values: + // + if (SUCCEEDED(hr) && bBarcodeTypesChanged) + { + PROPSPEC ps = {}; + PROPVARIANT pv = {}; + + ps.ulKind = PRSPEC_PROPID; + ps.propid = WIA_IPS_ENABLED_BARCODE_TYPES; + + PropVariantInit(&pv); + + hr = wiasReadMultiple(pWiasContext, 1, &ps, &pv, NULL); + if (SUCCEEDED(hr)) + { + if ((VT_VECTOR | VT_I4) == pv.vt) + { + ULONG ulValidValues = ARRAYSIZE(g_lSupportedBarcodeTypes); + BOOL bFound = FALSE; + + for (ULONG i = 0; i < pv.cal.cElems; i++) + { + bFound = FALSE; + + for (ULONG j = 0; j < ulValidValues; j++) + { + if (g_lSupportedBarcodeTypes[j] == pv.cal.pElems[i]) + { + bFound = TRUE; + break; + } + } + + if (!bFound) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Unsupported barcode type: %u, hr = 0x%08X", pv.cal.pElems[i], hr)); + break; + } + } + + } + else + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid value type for WIA_IPS_ENABLED_BARCODE_TYPES, expected VT_VECTOR | VT_I4 (%u), got %u, hr = 0x%08X", + VT_VECTOR | VT_I4, pv.vt, hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "wiasReadMultiple(WIA_IPS_ENABLED_BARCODE_TYPES) failed, hr = 0x%08X", hr)); + } + + PropVariantClear(&pv); + } + + return hr; +} + +/**************************************************************************\ +* +* Helper for IWiaMinIDrv::drvValidateItemProperties. +* +* Validates WIA_IPS_ENABLED_PATCH_CODE_TYPES +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::ValidatePatchCodeReaderProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext) +{ + HRESULT hr = S_OK; + BOOL bPatchCodeTypesChanged = TRUE; + + if ((!pWiasContext) || (!pPropertyContext)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::ValidateImprinterEndorserProperties failed, invalid parameter, hr = 0x%08X", hr)); + } + + // + // Check if WIA_IPS_ENABLED_PATCH_CODE_TYPES is changed by the application: + // + if (SUCCEEDED(hr)) + { + // + // wiasIsPropChanged fails if the property is not changed, do not fail ValidatePatchCodeReaderProperties: + // + wiasIsPropChanged(WIA_IPS_ENABLED_PATCH_CODE_TYPES, pPropertyContext, &bPatchCodeTypesChanged); + } + + // + // If WIA_IPS_ENABLED_PATCH_CODE_TYPES is changed, read the new (vector) array of values and validate against the valid values: + // + if (SUCCEEDED(hr) && bPatchCodeTypesChanged) + { + PROPSPEC ps = {}; + PROPVARIANT pv = {}; + + ps.ulKind = PRSPEC_PROPID; + ps.propid = WIA_IPS_ENABLED_PATCH_CODE_TYPES; + + PropVariantInit(&pv); + + hr = wiasReadMultiple(pWiasContext, 1, &ps, &pv, NULL); + if (SUCCEEDED(hr)) + { + if ((VT_VECTOR | VT_I4) == pv.vt) + { + ULONG ulValidValues = ARRAYSIZE(g_lSupportedPatchCodeTypes); + BOOL bFound = FALSE; + + for (ULONG i = 0; i < pv.cal.cElems; i++) + { + bFound = FALSE; + + for (ULONG j = 0; j < ulValidValues; j++) + { + if (g_lSupportedPatchCodeTypes[j] == pv.cal.pElems[i]) + { + bFound = TRUE; + break; + } + } + + if (!bFound) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Unsupported patch code type: %u, hr = 0x%08X", pv.cal.pElems[i], hr)); + break; + } + } + + } + else + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid value type for WIA_IPS_ENABLED_PATCH_CODE_TYPES, expected VT_VECTOR | VT_I4 (%u), got %u, hr = 0x%08X", + VT_VECTOR | VT_I4, pv.vt, hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "wiasReadMultiple(WIA_IPS_ENABLED_PATCH_CODE_TYPES) failed, hr = 0x%08X", hr)); + } + + PropVariantClear(&pv); + } + + return hr; +} + +/**************************************************************************\ +* +* Helper for IWiaMinIDrv::drvValidateItemProperties. +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::ValidateMicrReaderProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext) +{ + HRESULT hr = S_OK; + + if ((!pWiasContext) || (!pPropertyContext)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::ValidateMicrReaderProperties failed, invalid parameter, hr = 0x%08X", hr)); + } + + // + // Nothing special to validate here + // + + return hr; +} + +/**************************************************************************\ +* +* Helper for CWiaDriver::ValidateColorDropProperties. +* +* Validates a RGB color drop property WIA_IPS_COLOR_DROP_RED, WIA_IPS_COLOR_DROP_GREEN and +* WIA_IPS_COLOR_DROP_BLUE +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* nChannel - WIA_COLOR_DROP_RED, WIA_COLOR_DROP_GREEN or +* WIA_COLOR_DROP_BLUE (as defined in wiadef.h) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::ValidateColorDropProperty( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nChannel) +{ + HRESULT hr = S_OK; + BOOL bChanged = TRUE; + UINT nProp = 0; + + if ((!pWiasContext) || (!pPropertyContext) || + ((WIA_COLOR_DROP_RED != nChannel) && (WIA_COLOR_DROP_GREEN != nChannel) && (WIA_COLOR_DROP_BLUE != nChannel))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::ValidateColorDropProperty failed, invalid parameter, hr = 0x%08X", hr)); + } + + switch (nChannel) + { + case WIA_COLOR_DROP_RED: + nProp = WIA_IPS_COLOR_DROP_RED; + break; + + case WIA_COLOR_DROP_GREEN: + nProp = WIA_IPS_COLOR_DROP_GREEN; + break; + + case WIA_COLOR_DROP_BLUE: + nProp = WIA_IPS_COLOR_DROP_BLUE; + } + + // + // Check if this WIA_IPS_COLOR_DROP_* property is changed by the application: + // + if (SUCCEEDED(hr)) + { + // + // wiasIsPropChanged fails if the property is not changed, do not fail validation because of this: + // + wiasIsPropChanged(nProp, pPropertyContext, &bChanged); + } + + // + // If this WIA_IPS_COLOR_DROP_* property is changed, read the new (vector) array of values and validate + // both against WIA_IPS_COLOR_DROP_MULTI and the valid range of values (from 0 and 100, inclusive): + // + if (SUCCEEDED(hr) && bChanged) + { + PROPSPEC ps = {}; + PROPVARIANT pv = {}; + + ps.ulKind = PRSPEC_PROPID; + ps.propid = nProp; + + PropVariantInit(&pv); + + hr = wiasReadMultiple(pWiasContext, 1, &ps, &pv, NULL); + if (SUCCEEDED(hr)) + { + if ((VT_VECTOR | VT_I4) == pv.vt) + { + if (pv.cal.cElems > g_lMaxDropColors) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Unsupported number of drop out entries for property %u: %u, supported up to: %u, hr = 0x%08X", + nProp, pv.cal.cElems, g_lMaxDropColors, hr)); + } + else + { + for (ULONG i = 0; i < pv.cal.cElems; i++) + { + if ((pv.cal.pElems[i] < 0) || (pv.cal.pElems[i] > 100)) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, + "Unsupported color drop value for property %u at vector position %u: %u, valid range is from 0 to 100 inclusive, hr = 0x%08X", + nProp, i, pv.cal.pElems[i], hr)); + break; + } + } + } + } + else + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid value type for property %u, expected VT_VECTOR | VT_I4 (%u), got %u, hr = 0x%08X", + nProp, VT_VECTOR | VT_I4, pv.vt, hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "wiasReadMultiple(property %u) failed, hr = 0x%08X", nProp, hr)); + } + + PropVariantClear(&pv); + } + + return hr; +} + +/**************************************************************************\ +* +* Helper for IWiaMinIDrv::drvValidateItemProperties. +* +* Validates WIA_IPS_COLOR_DROP_RED, WIA_IPS_COLOR_DROP_GREEN and +* WIA_IPS_COLOR_DROP_BLUE +* +* Parameters: +* +* pWiasContext - pointer to the item context +* pPropertyContext - pointer to the property context which +* indicates which properties are being written +* nDocumentHandlingSelect - FLAT or FEED (as defined in wiadef.h) +* +* Return Value: +* +* S_OK if successful, an error HRESULT otherwise +* +\**************************************************************************/ +HRESULT CWiaDriver::ValidateColorDropProperties( + _In_ BYTE* pWiasContext, + _In_ WIA_PROPERTY_CONTEXT* pPropertyContext, + UINT nDocumentHandlingSelect) +{ + HRESULT hr = S_OK; + + if ((!pWiasContext) || (!pPropertyContext) || + ((FLAT != nDocumentHandlingSelect) && (FEED != nDocumentHandlingSelect))) + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "CWiaDriver::ValidateColorDropProperties failed, invalid parameter, hr = 0x%08X", hr)); + } + + // + // Validate WIA_IPS_COLOR_DROP_RED (error logging is covered by ValidateColorDropProperty): + // + if (SUCCEEDED(hr)) + { + hr = ValidateColorDropProperty(pWiasContext, pPropertyContext, WIA_COLOR_DROP_RED); + } + + // + // Validate WIA_IPS_COLOR_DROP_GREEN (error logging is covered by ValidateColorDropProperty): + // + if (SUCCEEDED(hr)) + { + hr = ValidateColorDropProperty(pWiasContext, pPropertyContext, WIA_COLOR_DROP_GREEN); + } + + // + // Validate WIA_IPS_COLOR_DROP_BLUE (error logging is covered by ValidateColorDropProperty): + // + if (SUCCEEDED(hr)) + { + hr = ValidateColorDropProperty(pWiasContext, pPropertyContext, WIA_COLOR_DROP_BLUE); + } + + return hr; +} diff --git a/wia/ProdScan/WiaUtil.cpp b/wia/ProdScan/WiaUtil.cpp new file mode 100644 index 00000000..7378ae0a --- /dev/null +++ b/wia/ProdScan/WiaUtil.cpp @@ -0,0 +1,406 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: WiaUtil.cpp +* +* Project: Production Scanner Driver Sample +* +* Description: This file contains implementation of helper functions +* declared in wiautil.h +* +***************************************************************************/ + +#include "stdafx.h" + +/**************************************************************************\ +* +* This function creates a full WIA item name from a given WIA item name. +* The new full item name is created by concatenating the WIA item name +* with the parent's full item name. +* +* (e.g. 0000\Root + Flatbed = 0000\Root\Flatbed) +* +* +* Parameters: +* +* pParent - IWiaDrvItem interface of the parent WIA driver item +* bstrItemName - Name of the WIA item +* pbstrFullItemName - Returned full item name. +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT MakeFullItemName( + _In_ IWiaDrvItem* pParent, + _In_ BSTR bstrItemName, + _Out_ BSTR* pbstrFullItemName) +{ + HRESULT hr = S_OK; + + if (pParent && bstrItemName && pbstrFullItemName) + { + BSTR bstrParentFullItemName = NULL; + hr = pParent->GetFullItemName(&bstrParentFullItemName); + if (SUCCEEDED(hr)) + { + WCHAR wFullItemName[MAX_PATH * 2] = {}; + + hr = StringCbPrintf(wFullItemName, sizeof(wFullItemName), TEXT("%ws\\%ws"), bstrParentFullItemName, bstrItemName); + if (SUCCEEDED(hr)) + { + *pbstrFullItemName = SysAllocString(wFullItemName); + if (*pbstrFullItemName) + { + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for BSTR full item name, hr = 0x%08X", hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "Failed to allocate memory for BSTR full item name, hr = 0x%08X", hr)); + } + + SysFreeString(bstrParentFullItemName); + bstrParentFullItemName = NULL; + } + else + { + WIAEX_ERROR((g_hInst, "Failed to produce the full item name, hr = 0x%08X", hr)); + } + } + else + { + hr = E_INVALIDARG; + WIAEX_ERROR((g_hInst, "Invalid parameter, hr = 0x%08X", hr)); + } + return hr; +} + +/**************************************************************************\ +* +* This function creates a WIA child item +* +* Parameters: +* +* wszItemName - Item name +* pIWiaMiniDrv - WIA minidriver interface +* pParent - Parent's WIA driver item interface +* lItemFlags - Item flags +* guidItemCategory - Item category +* ppChild - Pointer to the newly created child item +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT CreateWIAChildItem( + _In_ LPOLESTR pszItemName, + _In_ IWiaMiniDrv *pIWiaMiniDrv, + _In_ IWiaDrvItem *pParent, + LONG lItemFlags, + GUID guidItemCategory, + _Inout_opt_ IWiaDrvItem **ppChild) +{ + UNREFERENCED_PARAMETER(guidItemCategory); + + HRESULT hr = E_INVALIDARG; + + if (pszItemName && pIWiaMiniDrv && pParent) + { + BSTR bstrItemName = SysAllocString(pszItemName); + BSTR bstrFullItemName = NULL; + IWiaDrvItem *pIWiaDrvItem = NULL; + + if (bstrItemName) + { + hr = MakeFullItemName(pParent, bstrItemName, &bstrFullItemName); + if (SUCCEEDED(hr)) + { + WIA_DRIVER_ITEM_CONTEXT *pWiaDriverItemContext = NULL; + hr = wiasCreateDrvItem(lItemFlags, + bstrItemName, + bstrFullItemName, + pIWiaMiniDrv, + sizeof(WIA_DRIVER_ITEM_CONTEXT), + (BYTE **)&pWiaDriverItemContext, + &pIWiaDrvItem); + + if (SUCCEEDED(hr)) + { + // + // Initialize the item context data: + // + memset(pWiaDriverItemContext, 0, sizeof(WIA_DRIVER_ITEM_CONTEXT)); + pWiaDriverItemContext->m_pUploadedImage = NULL; + + hr = pIWiaDrvItem->AddItemToFolder(pParent); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to add the new WIA item (%ws) to the specified parent item, hr = 0x%08X", + bstrFullItemName, hr)); + pIWiaDrvItem->Release(); + pIWiaDrvItem = NULL; + } + + if (SUCCEEDED(hr)) + { + // + // If a child iterface pointer parameter was specified, then the caller + // expects to have the newly created child interface pointer returned to + // them (do not release the newly created item in this case). + // + + if (ppChild) + { + *ppChild = pIWiaDrvItem; + pIWiaDrvItem = NULL; + } + else if (pIWiaDrvItem) + { + // + // The newly created child has been added to the tree, and is no longer + // needed. Release it. + // + + pIWiaDrvItem->Release(); + pIWiaDrvItem = NULL; + } + } + } + else + { + WIAEX_ERROR((g_hInst, "Failed to create the new WIA driver item, hr = 0x%08X", hr)); + } + + SysFreeString(bstrItemName); + bstrItemName = NULL; + SysFreeString(bstrFullItemName); + bstrFullItemName = NULL; + } + else + { + WIAEX_ERROR((g_hInst, "Failed to create the new WIA item's full item name, hr = 0x%08X", hr)); + } + } + else + { + // + // Failed to allocate memory for bstrItemName. + // + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for BSTR storage item name")); + } + + } + else + { + WIAEX_ERROR((g_hInst, "Invalid parameter")); + } + return hr; +} + +/**************************************************************************\ +* +* This function returns the WIA driver item context data stored with the +* driver item. The context is initialized and stored at WIA item creation. +* See CreateWIAChildItem function. +* +* Parameters: +* +* pWiasContext - Pointer to the WIA item context +* ppWiaDriverItemContext - Pointer to the WIA driver item context data +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT wiasGetDriverItemPrivateContext( + _In_ BYTE* pWiasContext, + _Out_ BYTE** ppWiaDriverItemContext) +{ + HRESULT hr = E_INVALIDARG; + + if (pWiasContext && ppWiaDriverItemContext) + { + IWiaDrvItem *pIWiaDrvItem = NULL; + + hr = wiasGetDrvItem(pWiasContext, &pIWiaDrvItem); + + if (SUCCEEDED(hr)) + { + hr = pIWiaDrvItem->GetDeviceSpecContext(ppWiaDriverItemContext); + + // + // The caller will handle the failure case. A failure, may mean that + // the the WIA item does not have a private device specific context + // stored. This is OK, because is is not required. + // + } + else + { + WIAEX_ERROR((g_hInst, "Failed to get the WIA driver item from the application item, hr = 0x%08X", hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "Invalid parameter")); + } + return hr; +} + + +/**************************************************************************\ +* +* This function allocates a buffer to be used during data transfers. +* FreeTransferBuffer should be called to free the memory allocated by +* this function. +* +* Parameters: +* +* ppBuffer - Pointer to the allocated buffer; the caller must +* call FreeTransferBuffer() when finished with this buffer +* pulBufferSize - Size of the buffer allocated +* +* Return Value: +* +* S_OK or a standard COM error code +* +\**************************************************************************/ + +HRESULT AllocateTransferBuffer( + _Outptr_result_bytebuffer_(*pulBufferSize) BYTE** ppBuffer, + _Out_ ULONG* pulBufferSize) +{ + HRESULT hr = S_OK; + + if (ppBuffer && pulBufferSize) + { + // + // Set the buffer size to DEFAULT_BUFFER_SIZE + // + *pulBufferSize = DEFAULT_BUFFER_SIZE; + + // + // Allocate the memory + // + *ppBuffer = (BYTE*) CoTaskMemAlloc(*pulBufferSize); + if (*ppBuffer) + { + hr = S_OK; + } + else + { + hr = E_OUTOFMEMORY; + WIAEX_ERROR((g_hInst, "Failed to allocate memory for transfer buffer, hr = 0x%08X", hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "Invalid parameter")); + hr = E_INVALIDARG; + } + + return hr; +} + +/**************************************************************************\ +* +* This function frees any memory allocated using AllocateTransferBuffer() +* +* Parameters: +* +* pBuffer - Pointer to a buffer allocated with an AllocateTransferBuffer +* call. If NULL, this call does nothing. +* +* Return Value: +* +* None +* +\**************************************************************************/ + +void FreeTransferBuffer( + _In_opt_ BYTE* pBuffer) +{ + if (pBuffer) + { + CoTaskMemFree(pBuffer); + } +} + +/**************************************************************************\ +* +* This function queues a WIA event using the passed in WIA item context. +* +* Parameters: +* +* pWiasContext - Pointer to the WIA item context +* guidWIAEvent - WIA event to queue +* +* Return Value: +* +* None +* +\**************************************************************************/ + +void QueueWIAEvent( + _In_ BYTE* pWiasContext, + const GUID& guidWIAEvent) +{ + HRESULT hr = S_OK; + BSTR bstrDeviceID = NULL; + BSTR bstrFullItemName = NULL; + BYTE* pRootItemContext = NULL; + + hr = wiasReadPropStr(pWiasContext, WIA_IPA_FULL_ITEM_NAME, &bstrFullItemName, NULL, TRUE); + + if (SUCCEEDED(hr)) + { + hr = wiasGetRootItem(pWiasContext, &pRootItemContext); + if (SUCCEEDED(hr)) + { + hr = wiasReadPropStr(pRootItemContext, WIA_DIP_DEV_ID, &bstrDeviceID, NULL, TRUE); + if (SUCCEEDED(hr)) + { + hr = wiasQueueEvent(bstrDeviceID, &guidWIAEvent, bstrFullItemName); + if (FAILED(hr)) + { + WIAEX_ERROR((g_hInst, "Failed to queue WIA event, hr = 0x%08X", hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "Failed to read the WIA_DIP_DEV_ID property, hr = 0x%08X", hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "Failed to get the Root item from child item, using wiasGetRootItem, hr = 0x%08X", hr)); + } + } + else + { + WIAEX_ERROR((g_hInst, "Failed to read WIA_IPA_FULL_ITEM_NAME property, hr = 0x%08X", hr)); + } + + if (bstrFullItemName) + { + SysFreeString(bstrFullItemName); + } + + if (bstrDeviceID) + { + SysFreeString(bstrDeviceID); + } +} diff --git a/wia/ProdScan/WiaUtil.h b/wia/ProdScan/WiaUtil.h new file mode 100644 index 00000000..d395c86c --- /dev/null +++ b/wia/ProdScan/WiaUtil.h @@ -0,0 +1,59 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: WiaUtil.h +* +* Project: Production Scanning Driver Sample +* +* Description: This file contains various helper functions for the driver. +* +***************************************************************************/ + +#pragma once + +HRESULT +MakeFullItemName( + _In_ IWiaDrvItem* pParent, + _In_ BSTR bstrItemName, + _Out_ BSTR* pbstrFullItemName); + +HRESULT +CreateWIAChildItem( + _In_ LPOLESTR pszItemName, + _In_ IWiaMiniDrv *pIWiaMiniDrv, + _In_ IWiaDrvItem *pParent, + LONG lItemFlags, + GUID guidItemCategory, + _Inout_opt_ IWiaDrvItem **ppChild = NULL); + +HRESULT +wiasGetDriverItemPrivateContext( + _In_ BYTE* pWiasContext, + _Out_ BYTE** ppWiaDriverItemContext); + +HRESULT AllocateTransferBuffer( + _Outptr_result_bytebuffer_(*pulBufferSize) BYTE** ppBuffer, + _Out_ ULONG* pulBufferSize); + +void +FreeTransferBuffer( + _In_opt_ BYTE* pBuffer); + +void +QueueWIAEvent( + _In_ BYTE* pWiasContext, + const GUID& guidWIAEvent); + +inline LONG +BytesPerLine( + LONG lImageWidth, + LONG lBitDepth) +{ + // + // The number of bytes per line include the padding necessary to make each uncompressed + // line (DIB or Raw) DWORD aligned. When the image data is compressed the number of bytes + // per line calculated here describes the original uncompressed image: + // + return (((lImageWidth * lBitDepth) + 31) / 32) * 4; +}; diff --git a/wia/ProdScan/readme.mht b/wia/ProdScan/readme.mht new file mode 100644 index 00000000..7fe6e3bc --- /dev/null +++ b/wia/ProdScan/readme.mht @@ -0,0 +1,956 @@ +MIME-Version: 1.0 +Content-Type: multipart/related; boundary="----=_NextPart_01CB1E95.4412BC30" + +This document is a Single File Web Page, also known as a Web Archive file. If you are seeing this message, your browser or editor doesn't support Web Archive files. Please download a browser that supports Web Archive, such as Windows� Internet Explorer�. + +------=_NextPart_01CB1E95.4412BC30 +Content-Location: file:///C:/2A821E05/readme.htm +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="windows-1252" + +<html xmlns:v=3D"urn:schemas-microsoft-com:vml" +xmlns:o=3D"urn:schemas-microsoft-com:office:office" +xmlns:w=3D"urn:schemas-microsoft-com:office:word" +xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml" +xmlns=3D"http://www.w3.org/TR/REC-html40"> + +<head> +<meta http-equiv=3DContent-Type content=3D"text/html; charset=3Dwindows-125= +2"> +<meta name=3DProgId content=3DWord.Document> +<meta name=3DGenerator content=3D"Microsoft Word 14"> +<meta name=3DOriginator content=3D"Microsoft Word 14"> +<link rel=3DFile-List href=3D"readme_files/filelist.xml"> +<!--[if gte mso 9]><xml> + <o:DocumentProperties> + <o:Author>Marius Niculescu</o:Author> + <o:LastAuthor>Marius Niculescu</o:LastAuthor> + <o:Revision>3</o:Revision> + <o:TotalTime>16</o:TotalTime> + <o:Created>2010-06-30T13:55:00Z</o:Created> + <o:LastSaved>2010-07-08T16:01:00Z</o:LastSaved> + <o:Pages>1</o:Pages> + <o:Words>769</o:Words> + <o:Characters>4388</o:Characters> + <o:Company>Microsoft Corporation</o:Company> + <o:Lines>36</o:Lines> + <o:Paragraphs>10</o:Paragraphs> + <o:CharactersWithSpaces>5147</o:CharactersWithSpaces> + <o:Version>14.00</o:Version> + </o:DocumentProperties> + <o:OfficeDocumentSettings> + <o:AllowPNG/> + </o:OfficeDocumentSettings> +</xml><![endif]--> +<link rel=3DthemeData href=3D"readme_files/themedata.thmx"> +<link rel=3DcolorSchemeMapping href=3D"readme_files/colorschememapping.xml"> +<!--[if gte mso 9]><xml> + <w:WordDocument> + <w:SpellingState>Clean</w:SpellingState> + <w:GrammarState>Clean</w:GrammarState> + <w:TrackMoves>false</w:TrackMoves> + <w:TrackFormatting/> + <w:PunctuationKerning/> + <w:ValidateAgainstSchemas/> + <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> + <w:IgnoreMixedContent>false</w:IgnoreMixedContent> + <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> + <w:DoNotPromoteQF/> + <w:LidThemeOther>EN-US</w:LidThemeOther> + <w:LidThemeAsian>X-NONE</w:LidThemeAsian> + <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> + <w:Compatibility> + <w:BreakWrappedTables/> + <w:SnapToGridInCell/> + <w:WrapTextWithPunct/> + <w:UseAsianBreakRules/> + <w:DontGrowAutofit/> + <w:SplitPgBreakAndParaMark/> + <w:EnableOpenTypeKerning/> + <w:DontFlipMirrorIndents/> + <w:OverrideTableStyleHps/> + </w:Compatibility> + <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> + <m:mathPr> + <m:mathFont m:val=3D"Cambria Math"/> + <m:brkBin m:val=3D"before"/> + <m:brkBinSub m:val=3D"--"/> + <m:smallFrac m:val=3D"off"/> + <m:dispDef/> + <m:lMargin m:val=3D"0"/> + <m:rMargin m:val=3D"0"/> + <m:defJc m:val=3D"centerGroup"/> + <m:wrapIndent m:val=3D"1440"/> + <m:intLim m:val=3D"subSup"/> + <m:naryLim m:val=3D"undOvr"/> + </m:mathPr></w:WordDocument> +</xml><![endif]--><!--[if gte mso 9]><xml> + <w:LatentStyles DefLockedState=3D"false" DefUnhideWhenUsed=3D"true" + DefSemiHidden=3D"true" DefQFormat=3D"false" DefPriority=3D"99" + LatentStyleCount=3D"267"> + <w:LsdException Locked=3D"false" Priority=3D"0" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Normal"/> + <w:LsdException Locked=3D"false" Priority=3D"0" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"heading 1"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"= +heading 2"/> + <w:LsdException Locked=3D"false" Priority=3D"0" QFormat=3D"true" Name=3D"= +heading 3"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"= +heading 4"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"= +heading 5"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"= +heading 6"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"= +heading 7"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"= +heading 8"/> + <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D"= +heading 9"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 1"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 2"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 3"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 4"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 5"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 6"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 7"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 8"/> + <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 9"/> + <w:LsdException Locked=3D"false" Priority=3D"35" QFormat=3D"true" Name=3D= +"caption"/> + <w:LsdException Locked=3D"false" Priority=3D"10" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Title"/> + <w:LsdException Locked=3D"false" Priority=3D"1" Name=3D"Default Paragraph= + Font"/> + <w:LsdException Locked=3D"false" Priority=3D"11" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtitle"/> + <w:LsdException Locked=3D"false" Priority=3D"0" Name=3D"Hyperlink"/> + <w:LsdException Locked=3D"false" Priority=3D"22" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Strong"/> + <w:LsdException Locked=3D"false" Priority=3D"20" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Emphasis"/> + <w:LsdException Locked=3D"false" Priority=3D"0" Name=3D"Normal (Web)"/> + <w:LsdException Locked=3D"false" Priority=3D"59" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Table Grid"/> + <w:LsdException Locked=3D"false" UnhideWhenUsed=3D"false" Name=3D"Placeho= +lder Text"/> + <w:LsdException Locked=3D"false" Priority=3D"1" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"No Spacing"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Shading"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light List"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Grid"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 1"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 2"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Dark List"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful List"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 1"/> + <w:LsdException Locked=3D"false" UnhideWhenUsed=3D"false" Name=3D"Revisio= +n"/> + <w:LsdException Locked=3D"false" Priority=3D"34" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"List Paragraph"/> + <w:LsdException Locked=3D"false" Priority=3D"29" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Quote"/> + <w:LsdException Locked=3D"false" Priority=3D"30" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Quote"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 1"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 2"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 3"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 4"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 5"/> + <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light List Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 6"/> + <w:LsdException Locked=3D"false" Priority=3D"19" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtle Emphasis"/> + <w:LsdException Locked=3D"false" Priority=3D"21" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Emphasis"/> + <w:LsdException Locked=3D"false" Priority=3D"31" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtle Reference"/> + <w:LsdException Locked=3D"false" Priority=3D"32" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Reference"/> + <w:LsdException Locked=3D"false" Priority=3D"33" SemiHidden=3D"false" + UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Book Title"/> + <w:LsdException Locked=3D"false" Priority=3D"37" Name=3D"Bibliography"/> + <w:LsdException Locked=3D"false" Priority=3D"39" QFormat=3D"true" Name=3D= +"TOC Heading"/> + </w:LatentStyles> +</xml><![endif]--> +<style> +<!-- + /* Font Definitions */ + @font-face + {font-family:Wingdings; + panose-1:5 0 0 0 0 0 0 0 0 0; + mso-font-charset:2; + mso-generic-font-family:auto; + mso-font-pitch:variable; + mso-font-signature:0 268435456 0 0 -2147483648 0;} +@font-face + {font-family:SimSun; + panose-1:2 1 6 0 3 1 1 1 1 1; + mso-font-alt:\5B8B\4F53; + mso-font-charset:134; + mso-generic-font-family:auto; + mso-font-pitch:variable; + mso-font-signature:3 680460288 22 0 262145 0;} +@font-face + {font-family:SimSun; + panose-1:2 1 6 0 3 1 1 1 1 1; + mso-font-alt:\5B8B\4F53; + mso-font-charset:134; + mso-generic-font-family:auto; + mso-font-pitch:variable; + mso-font-signature:3 680460288 22 0 262145 0;} +@font-face + {font-family:"\@SimSun"; + panose-1:2 1 6 0 3 1 1 1 1 1; + mso-font-charset:134; + mso-generic-font-family:auto; + mso-font-pitch:variable; + mso-font-signature:3 680460288 22 0 262145 0;} + /* Style Definitions */ + p.MsoNormal, li.MsoNormal, div.MsoNormal + {mso-style-unhide:no; + mso-style-qformat:yes; + mso-style-parent:""; + margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman","serif"; + mso-fareast-font-family:"Times New Roman"; + color:black;} +h1 + {mso-style-unhide:no; + mso-style-qformat:yes; + mso-style-link:"Heading 1 Char"; + mso-margin-top-alt:auto; + margin-right:0in; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + mso-outline-level:1; + font-size:24.0pt; + font-family:"Times New Roman","serif"; + mso-fareast-font-family:SimSun; + color:black; + font-weight:bold;} +h3 + {mso-style-noshow:yes; + mso-style-qformat:yes; + mso-style-link:"Heading 3 Char"; + mso-margin-top-alt:auto; + margin-right:0in; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + mso-outline-level:3; + font-size:13.5pt; + font-family:"Times New Roman","serif"; + mso-fareast-font-family:SimSun; + color:black; + font-weight:bold;} +a:link, span.MsoHyperlink + {color:blue; + text-decoration:underline; + text-underline:single;} +a:visited, span.MsoHyperlinkFollowed + {mso-style-noshow:yes; + mso-style-priority:99; + color:purple; + mso-themecolor:followedhyperlink; + text-decoration:underline; + text-underline:single;} +p + {mso-style-noshow:yes; + mso-margin-top-alt:auto; + margin-right:0in; + mso-margin-bottom-alt:auto; + margin-left:0in; + mso-pagination:widow-orphan; + font-size:12.0pt; + font-family:"Times New Roman","serif"; + mso-fareast-font-family:"Times New Roman"; + color:black;} +span.Heading1Char + {mso-style-name:"Heading 1 Char"; + mso-style-unhide:no; + mso-style-locked:yes; + mso-style-link:"Heading 1"; + mso-ansi-font-size:24.0pt; + mso-bidi-font-size:24.0pt; + font-family:"Times New Roman","serif"; + mso-ascii-font-family:"Times New Roman"; + mso-fareast-font-family:SimSun; + mso-hansi-font-family:"Times New Roman"; + mso-bidi-font-family:"Times New Roman"; + color:black; + mso-font-kerning:18.0pt; + font-weight:bold;} +span.Heading3Char + {mso-style-name:"Heading 3 Char"; + mso-style-noshow:yes; + mso-style-unhide:no; + mso-style-locked:yes; + mso-style-link:"Heading 3"; + mso-ansi-font-size:13.5pt; + mso-bidi-font-size:13.5pt; + font-family:"Times New Roman","serif"; + mso-ascii-font-family:"Times New Roman"; + mso-fareast-font-family:SimSun; + mso-hansi-font-family:"Times New Roman"; + mso-bidi-font-family:"Times New Roman"; + color:black; + font-weight:bold;} +span.SpellE + {mso-style-name:""; + mso-spl-e:yes;} +span.GramE + {mso-style-name:""; + mso-gram-e:yes;} +.MsoChpDefault + {mso-style-type:export-only; + mso-default-props:yes; + font-size:10.0pt; + mso-ansi-font-size:10.0pt; + mso-bidi-font-size:10.0pt; + font-family:"Calibri","sans-serif"; + mso-ascii-font-family:Calibri; + mso-ascii-theme-font:minor-latin; + mso-fareast-font-family:Calibri; + mso-fareast-theme-font:minor-latin; + mso-hansi-font-family:Calibri; + mso-hansi-theme-font:minor-latin; + mso-bidi-font-family:"Times New Roman"; + mso-bidi-theme-font:minor-bidi;} +@page WordSection1 + {size:8.5in 11.0in; + margin:1.0in 1.0in 1.0in 1.0in; + mso-header-margin:.5in; + mso-footer-margin:.5in; + mso-paper-source:0;} +div.WordSection1 + {page:WordSection1;} + /* List Definitions */ + @list l0 + {mso-list-id:1542011108; + mso-list-type:hybrid; + mso-list-template-ids:-1970876908 67698689 67698691 67698693 67698689 6769= +8691 67698693 67698689 67698691 67698693;} +@list l0:level1 + {mso-level-number-format:bullet; + mso-level-text:\F0B7; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:Symbol;} +@list l0:level2 + {mso-level-number-format:bullet; + mso-level-text:o; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:"Courier New";} +@list l0:level3 + {mso-level-number-format:bullet; + mso-level-text:\F0A7; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:Wingdings;} +@list l0:level4 + {mso-level-number-format:bullet; + mso-level-text:\F0B7; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:Symbol;} +@list l0:level5 + {mso-level-number-format:bullet; + mso-level-text:o; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:"Courier New";} +@list l0:level6 + {mso-level-number-format:bullet; + mso-level-text:\F0A7; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:Wingdings;} +@list l0:level7 + {mso-level-number-format:bullet; + mso-level-text:\F0B7; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:Symbol;} +@list l0:level8 + {mso-level-number-format:bullet; + mso-level-text:o; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:"Courier New";} +@list l0:level9 + {mso-level-number-format:bullet; + mso-level-text:\F0A7; + mso-level-tab-stop:none; + mso-level-number-position:left; + text-indent:-.25in; + font-family:Wingdings;} +ol + {margin-bottom:0in;} +ul + {margin-bottom:0in;} +--> +</style> +<!--[if gte mso 10]> +<style> + /* Style Definitions */ + table.MsoNormalTable + {mso-style-name:"Table Normal"; + mso-tstyle-rowband-size:0; + mso-tstyle-colband-size:0; + mso-style-noshow:yes; + mso-style-priority:99; + mso-style-parent:""; + mso-padding-alt:0in 5.4pt 0in 5.4pt; + mso-para-margin:0in; + mso-para-margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:10.0pt; + font-family:"Calibri","sans-serif"; + mso-ascii-font-family:Calibri; + mso-ascii-theme-font:minor-latin; + mso-hansi-font-family:Calibri; + mso-hansi-theme-font:minor-latin; + mso-bidi-font-family:"Times New Roman"; + mso-bidi-theme-font:minor-bidi;} +</style> +<![endif]--><!--[if gte mso 9]><xml> + <o:shapedefaults v:ext=3D"edit" spidmax=3D"1026"/> +</xml><![endif]--><!--[if gte mso 9]><xml> + <o:shapelayout v:ext=3D"edit"> + <o:idmap v:ext=3D"edit" data=3D"1"/> + </o:shapelayout></xml><![endif]--> +</head> + +<body lang=3DEN-US link=3Dblue vlink=3Dpurple style=3D'tab-interval:.5in'> + +<div class=3DWordSection1> + +<h1>Production Scanning WIA 2.0 Driver Sample</h1> + +<p class=3DMsoNormal><b style=3D'mso-bidi-font-weight:normal'><span +style=3D'color:red'>[This is preliminary documentation and subject to chang= +e.]<o:p></o:p></span></b></p> + +<h3>SUMMARY</h3> + +<p>This driver sample shows how to add Production Scanning features to a WIA +2.0 mini-driver. </p> + +<p>The sample driver implements the following programmable data source items +and related functionality: </p> + +<p style=3D'margin-top:0in;margin-right:0in;margin-bottom:0in;margin-left:.= +5in; +margin-bottom:.0001pt;text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !su= +pportLists]><span +style=3D'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-fa= +mily: +Symbol'><span style=3D'mso-list:Ignore'>�<span style=3D'font:7.0pt "Times N= +ew Roman"'> +</span></span></span><![endif]>Flatbed and Feeder, which both support 8-bpp +grayscale, 24-bpp RGB, Auto-Color, and implement necessary WIA property sup= +port +for Auto-Crop, Over Scan and Color Dropout. </p> + +<p style=3D'margin-top:0in;margin-right:0in;margin-bottom:0in;margin-left:.= +5in; +margin-bottom:.0001pt;text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !su= +pportLists]><span +style=3D'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-fa= +mily: +Symbol'><span style=3D'mso-list:Ignore'>�<span style=3D'font:7.0pt "Times N= +ew Roman"'> +</span></span></span><![endif]>Auto item, for Auto-Configured Scanning. The +Flatbed, Feeder and Auto items all support the DIB, EXIF and RAW image tran= +sfer +file formats.</p> + +<p style=3D'margin-top:0in;margin-right:0in;margin-bottom:0in;margin-left:.= +5in; +margin-bottom:.0001pt;text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !su= +pportLists]><span +style=3D'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-fa= +mily: +Symbol'><span style=3D'mso-list:Ignore'>�<span style=3D'font:7.0pt "Times N= +ew Roman"'> +</span></span></span><![endif]>Additionally the Feeder item implements the +necessary properties and commands demonstrating Job Separators, Long Docume= +nt +Scanning, Blank Page Detection, Multi-Feed Detection, Scan Ahead, and Feeder +Motor Control. </p> + +<p style=3D'margin-top:0in;margin-right:0in;margin-bottom:0in;margin-left:.= +5in; +margin-bottom:.0001pt;text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !su= +pportLists]><span +style=3D'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-fa= +mily: +Symbol'><span style=3D'mso-list:Ignore'>�<span style=3D'font:7.0pt "Times N= +ew Roman"'> +</span></span></span><![endif]>The Job Separators and Multi-feed Detection +functionality is simulated at predetermined scan page numbers.</p> + +<p style=3D'margin-top:0in;margin-right:0in;margin-bottom:0in;margin-left:.= +5in; +margin-bottom:.0001pt;text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !su= +pportLists]><span +style=3D'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-fa= +mily: +Symbol'><span style=3D'mso-list:Ignore'>�<span style=3D'font:7.0pt "Times N= +ew Roman"'> +</span></span></span><![endif]>Imprinter and Endorser, which both support t= +ext +and graphics bi-directional data transfers. Transfer formats supported are = +DIB +(for graphics), TXT and CSV (for text).</p> + +<p style=3D'margin-top:0in;margin-right:0in;margin-bottom:0in;margin-left:.= +5in; +margin-bottom:.0001pt;text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !su= +pportLists]><span +style=3D'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-fa= +mily: +Symbol'><span style=3D'mso-list:Ignore'>�<span style=3D'font:7.0pt "Times N= +ew Roman"'> +</span></span></span><![endif]>Barcode Reader, Patch Code Reader and MICR +Reader, which all support XML and RAW metadata download.</p> + +<p style=3D'margin-top:0in;margin-right:0in;margin-bottom:0in;margin-left:.= +5in; +margin-bottom:.0001pt;text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !su= +pportLists]><span +style=3D'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-fa= +mily: +Symbol;mso-no-proof:yes'><span style=3D'mso-list:Ignore'>�<span style=3D'fo= +nt:7.0pt "Times New Roman"'> +</span></span></span><![endif]>Full configuration for the standard scan eve= +nt +as well as the device status events. <span style=3D'mso-no-proof:yes'><o:p>= +</o:p></span></p> + +<p style=3D'margin-top:0in;margin-right:0in;margin-bottom:0in;margin-left:.= +5in; +margin-bottom:.0001pt;text-indent:-.25in;mso-list:l0 level1 lfo2'><![if !su= +pportLists]><span +style=3D'font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-fa= +mily: +Symbol;mso-no-proof:yes'><span style=3D'mso-list:Ignore'>�<span style=3D'fo= +nt:7.0pt "Times New Roman"'> +</span></span></span><![endif]>WIA <span class=3DGramE>trace</span> +instrumentation, including real-time sample scanning performance (Pages per +Minute, PPM) measurement.<span style=3D'mso-no-proof:yes'><o:p></o:p></span= +></p> + +<p><b>NOTES<o:p></o:p></b></p> + +<p>This sample requires WIA 2.0, thus the sample will work only on Windows +Vista and higher releases. </p> + +<p>The default test images for Flatbed, Feeder and the Auto source are a pa= +ir +of EXIF images loaded from resources, each in size of 2550 x 3300 pixels, +equivalent with a Letter document (8.5� x 11�) scanned at 300 DPI. The samp= +le +uses GDI+ to convert these test images to DIB. The sample RAW images are ob= +tained +from the DIB conversions.</p> + +<p>The default test image for the Imprinter and Endorser is 640 x 480 pixel= +s, 1-bpp +DIB loaded from resources. The WIA application can individually replace the +Imprinter and/or Endorser image for the current driver instance by uploadin= +g at +run time other 640 x 480 pixels, 1-bpp, DIB images.</p> + +<p class=3DMsoNormal>By default the sample reports to support a single stan= +dard +document size (WIA_PAGE_LETTER), which matches the test images that the sam= +ple +by default transfers. The sample also reports to support WIA_PAGE_AUTO and +WIA_PAGE_CUSTOM. The default scan area size can be changed by modifying the= + <span +style=3D'color:windowtext;mso-no-proof:yes'>MIN/MAX_SCAN_AREA_WIDTH/HEIGHT = +values +in Constants.h and rebuilding the sample: the sample will still transfer the +same test images but will report as supported for the Feeder all standard W= +IA document +sizes that fit into the simulated scan area minimum and maximum dimensions.= +</span></p> + +<p class=3DMsoNormal><o:p> </o:p></p> + +<p class=3DMsoNormal>When job separator detection is enabled the sample sim= +ulates +a job separator page every 3 pages. This number can be modified by editing = +the +JOB_SEPARATOR_AT_PAGE value in Constants.h and then rebuilding the sample.<= +/p> + +<p>When multi-feed detection is enabled the sample simulates a multi-feed e= +rror +every 5 pages. This number can be modified by editing the MULTI_FEED_AT_PAGE +value in Constants.h and then rebuilding the sample.</p> + +<p>By default when scanning from the Feeder with WIA_IPS_PAGES is set to +ALL_PAGES and from the Auto item the sample will stop (pretending the feede= +r is +empty) after scanning 200 pages. For Feeder, if the application requests a +WIA_IPS_PAGE value greater than 200 the sample honors this request. The 200 +page limit can be modified by changing the MAX_SCAN_PAGES value in Constant= +s.h +and rebuilding the sample.</p> + +<h3>BUILDING THE SAMPLE</h3> + +<p>To build the sample, complete the following steps:</p> + +<p style=3D'margin-left:.5in;text-indent:-.25in;tab-stops:list .5in'>1.<span +style=3D'font-size:7.0pt'> </span>On the <b +style=3D'mso-bidi-font-weight:normal'>Start</b> menu, under Windows DDK, se= +lect <b +style=3D'mso-bidi-font-weight:normal'>Free Build Environment</b> or <b +style=3D'mso-bidi-font-weight:normal'>Checked Build Environment</b> to open= + a +command window and set basic environment variables needed to build drivers.= + </p> + +<p style=3D'margin-left:.5in;text-indent:-.25in;tab-stops:list .5in'>2.<span +style=3D'font-size:7.0pt'> </span>Navigate to= + the +directory that contains the sample driver source code (that is, the directo= +ry +where this file is located).</p> + +<p style=3D'margin-left:.5in;text-indent:-.25in;tab-stops:list .5in'>3.<span +style=3D'font-size:7.0pt'> </span>Run the bui= +ld command. +After the build completes, the following DLL is built: ProdScan.dll.</p> + +<h3>INSTALLING THE SAMPLE</h3> + +<p>To install the sample, complete the following steps:</p> + +<p style=3D'margin-left:.5in;text-indent:-.25in;tab-stops:list .5in'>1. Ope= +n an +elevated command prompt and then type <b style=3D'mso-bidi-font-weight:norm= +al'>rundll32 +<span class=3DSpellE>sti_ci</span> <span class=3DSpellE>AddDevice</span></b= +>.</p> + +<p style=3D'margin-left:.5in;text-indent:-.25in;tab-stops:list .5in'>2. When +prompted for which scanner or camera you want to install, click <b +style=3D'mso-bidi-font-weight:normal'>Have Disk</b>, and then navigate to t= +he +directory that contains the driver INF file (ProdScan.inf) and the DLL +(ProdScan.dll).</p> + +<h3>USING THE SAMPLE</h3> + +<p>The WiaInfo2 WIA tool can be used to test the sample. WiaInfo2 can displ= +ay +all new programmable data source items, their properties, allow writable +properties to be changed, execute image and metadata download transfers as = +well +as upload data to the device.</p> + +<h3>RESOURCES</h3> + +<p>For information and the latest release of the Microsoft� Windows� Driver= + Kit +(WDK), see <a href=3D"http://msdn.microsoft.com/en-us/library/ff557573.aspx= +">http://msdn.microsoft.com/en-us/library/ff557573.aspx</a>.</p> + +<p>For information about WIA, see <a +href=3D"http://msdn.microsoft.com/en-us/library/ms630368(VS.85).aspx">http:= +//msdn.microsoft.com/en-us/library/ms630368(VS.85).aspx</a>.<o:p></o:p></p> + +<p>For questions about WIA and this sample, send e-mail to <a +href=3D"mailto:[email protected]"><span style=3D'mso-fareast-font-famil= +y:SimSun'>[email protected]</span></a>.</p> + +</div> + +</body> + +</html> + +------=_NextPart_01CB1E95.4412BC30 +Content-Location: file:///C:/2A821E05/readme_files/themedata.thmx +Content-Transfer-Encoding: base64 +Content-Type: application/vnd.ms-officetheme + +UEsDBBQABgAIAAAAIQDp3g+//wAAABwCAAATAAAAW0NvbnRlbnRfVHlwZXNdLnhtbKyRy07DMBBF +90j8g+UtSpyyQAgl6YLHjseifMDImSQWydiyp1X790zSVEKoIBZsLNkz954743K9Hwe1w5icp0qv +8kIrJOsbR12l3zdP2a1WiYEaGDxhpQ+Y9Lq+vCg3h4BJiZpSpXvmcGdMsj2OkHIfkKTS+jgCyzV2 +JoD9gA7NdVHcGOuJkTjjyUPX5QO2sB1YPe7l+Zgk4pC0uj82TqxKQwiDs8CS1Oyo+UbJFkIuyrkn +9S6kK4mhzVnCVPkZsOheZTXRNajeIPILjBLDsAyJX89nIBkt5r87nons29ZZbLzdjrKOfDZezE7B +/xRg9T/oE9PMf1t/AgAA//8DAFBLAwQUAAYACAAAACEApdan58AAAAA2AQAACwAAAF9yZWxzLy5y +ZWxzhI/PasMwDIfvhb2D0X1R0sMYJXYvpZBDL6N9AOEof2giG9sb69tPxwYKuwiEpO/3qT3+rov5 +4ZTnIBaaqgbD4kM/y2jhdj2/f4LJhaSnJQhbeHCGo3vbtV+8UNGjPM0xG6VItjCVEg+I2U+8Uq5C +ZNHJENJKRds0YiR/p5FxX9cfmJ4Z4DZM0/UWUtc3YK6PqMn/s8MwzJ5PwX+vLOVFBG43lExp5GKh +qC/jU72QqGWq1B7Qtbj51v0BAAD//wMAUEsDBBQABgAIAAAAIQBreZYWgwAAAIoAAAAcAAAAdGhl +bWUvdGhlbWUvdGhlbWVNYW5hZ2VyLnhtbAzMTQrDIBBA4X2hd5DZN2O7KEVissuuu/YAQ5waQceg +0p/b1+XjgzfO3xTVm0sNWSycBw2KZc0uiLfwfCynG6jaSBzFLGzhxxXm6XgYybSNE99JyHNRfSPV +kIWttd0g1rUr1SHvLN1euSRqPYtHV+jT9yniResrJgoCOP0BAAD//wMAUEsDBBQABgAIAAAAIQAw +3UMpqAYAAKQbAAAWAAAAdGhlbWUvdGhlbWUvdGhlbWUxLnhtbOxZT2/bNhS/D9h3IHRvYyd2Ggd1 +itixmy1NG8Ruhx5piZbYUKJA0kl9G9rjgAHDumGHFdhth2FbgRbYpfs02TpsHdCvsEdSksVYXpI2 +2IqtPiQS+eP7/x4fqavX7scMHRIhKU/aXv1yzUMk8XlAk7Dt3R72L615SCqcBJjxhLS9KZHetY33 +37uK11VEYoJgfSLXcduLlErXl5akD8NYXuYpSWBuzEWMFbyKcCkQ+AjoxmxpuVZbXYoxTTyU4BjI +3hqPqU/QUJP0NnLiPQaviZJ6wGdioEkTZ4XBBgd1jZBT2WUCHWLW9oBPwI+G5L7yEMNSwUTbq5mf +t7RxdQmvZ4uYWrC2tK5vftm6bEFwsGx4inBUMK33G60rWwV9A2BqHtfr9bq9ekHPALDvg6ZWljLN +Rn+t3slplkD2cZ52t9asNVx8if7KnMytTqfTbGWyWKIGZB8bc/i12mpjc9nBG5DFN+fwjc5mt7vq +4A3I4lfn8P0rrdWGizegiNHkYA6tHdrvZ9QLyJiz7Ur4GsDXahl8hoJoKKJLsxjzRC2KtRjf46IP +AA1kWNEEqWlKxtiHKO7ieCQo1gzwOsGlGTvky7khzQtJX9BUtb0PUwwZMaP36vn3r54/RccPnh0/ ++On44cPjBz9aQs6qbZyE5VUvv/3sz8cfoz+efvPy0RfVeFnG//rDJ7/8/Hk1ENJnJs6LL5/89uzJ +i68+/f27RxXwTYFHZfiQxkSim+QI7fMYFDNWcSUnI3G+FcMI0/KKzSSUOMGaSwX9nooc9M0pZpl3 +HDk6xLXgHQHlowp4fXLPEXgQiYmiFZx3otgB7nLOOlxUWmFH8yqZeThJwmrmYlLG7WN8WMW7ixPH +v71JCnUzD0tH8W5EHDH3GE4UDklCFNJz/ICQCu3uUurYdZf6gks+VuguRR1MK00ypCMnmmaLtmkM +fplW6Qz+dmyzewd1OKvSeoscukjICswqhB8S5pjxOp4oHFeRHOKYlQ1+A6uoSsjBVPhlXE8q8HRI +GEe9gEhZteaWAH1LTt/BULEq3b7LprGLFIoeVNG8gTkvI7f4QTfCcVqFHdAkKmM/kAcQohjtcVUF +3+Vuhuh38ANOFrr7DiWOu0+vBrdp6Ig0CxA9MxEVvrxOuBO/gykbY2JKDRR1p1bHNPm7ws0oVG7L +4eIKN5TKF18/rpD7bS3Zm7B7VeXM9olCvQh3sjx3uQjo21+dt/Ak2SOQEPNb1Lvi/K44e//54rwo +ny++JM+qMBRo3YvYRtu03fHCrntMGRuoKSM3pGm8Jew9QR8G9Tpz4iTFKSyN4FFnMjBwcKHAZg0S +XH1EVTSIcApNe93TREKZkQ4lSrmEw6IZrqSt8dD4K3vUbOpDiK0cEqtdHtjhFT2cnzUKMkaq0Bxo +c0YrmsBZma1cyYiCbq/DrK6FOjO3uhHNFEWHW6GyNrE5lIPJC9VgsLAmNDUIWiGw8iqc+TVrOOxg +RgJtd+uj3C3GCxfpIhnhgGQ+0nrP+6hunJTHypwiWg8bDPrgeIrVStxamuwbcDuLk8rsGgvY5d57 +Ey/lETzzElA7mY4sKScnS9BR22s1l5se8nHa9sZwTobHOAWvS91HYhbCZZOvhA37U5PZZPnMm61c +MTcJ6nD1Ye0+p7BTB1Ih1RaWkQ0NM5WFAEs0Jyv/chPMelEKVFSjs0mxsgbB8K9JAXZ0XUvGY+Kr +srNLI9p29jUrpXyiiBhEwREasYnYx+B+HaqgT0AlXHeYiqBf4G5OW9tMucU5S7ryjZjB2XHM0ghn +5VanaJ7JFm4KUiGDeSuJB7pVym6UO78qJuUvSJVyGP/PVNH7Cdw+rATaAz5cDQuMdKa0PS5UxKEK +pRH1+wIaB1M7IFrgfhemIajggtr8F+RQ/7c5Z2mYtIZDpNqnIRIU9iMVCUL2oCyZ6DuFWD3buyxJ +lhEyEVUSV6ZW7BE5JGyoa+Cq3ts9FEGom2qSlQGDOxl/7nuWQaNQNznlfHMqWbH32hz4pzsfm8yg +lFuHTUOT278QsWgPZruqXW+W53tvWRE9MWuzGnlWALPSVtDK0v41RTjnVmsr1pzGy81cOPDivMYw +WDREKdwhIf0H9j8qfGa/dugNdcj3obYi+HihiUHYQFRfso0H0gXSDo6gcbKDNpg0KWvarHXSVss3 +6wvudAu+J4ytJTuLv89p7KI5c9k5uXiRxs4s7Njaji00NXj2ZIrC0Dg/yBjHmM9k5S9ZfHQPHL0F +3wwmTEkTTPCdSmDooQcmDyD5LUezdOMvAAAA//8DAFBLAwQUAAYACAAAACEADdGQn7YAAAAbAQAA +JwAAAHRoZW1lL3RoZW1lL19yZWxzL3RoZW1lTWFuYWdlci54bWwucmVsc4SPTQrCMBSE94J3CG9v +07oQkSbdiNCt1AOE5DUNNj8kUeztDa4sCC6HYb6ZabuXnckTYzLeMWiqGgg66ZVxmsFtuOyOQFIW +TonZO2SwYIKObzftFWeRSyhNJiRSKC4xmHIOJ0qTnNCKVPmArjijj1bkIqOmQci70Ej3dX2g8ZsB +fMUkvWIQe9UAGZZQmv+z/TgaiWcvHxZd/lFBc9mFBSiixszgI5uqTATKW7q6xN8AAAD//wMAUEsB +Ai0AFAAGAAgAAAAhAOneD7//AAAAHAIAABMAAAAAAAAAAAAAAAAAAAAAAFtDb250ZW50X1R5cGVz +XS54bWxQSwECLQAUAAYACAAAACEApdan58AAAAA2AQAACwAAAAAAAAAAAAAAAAAwAQAAX3JlbHMv +LnJlbHNQSwECLQAUAAYACAAAACEAa3mWFoMAAACKAAAAHAAAAAAAAAAAAAAAAAAZAgAAdGhlbWUv +dGhlbWUvdGhlbWVNYW5hZ2VyLnhtbFBLAQItABQABgAIAAAAIQAw3UMpqAYAAKQbAAAWAAAAAAAA +AAAAAAAAANYCAAB0aGVtZS90aGVtZS90aGVtZTEueG1sUEsBAi0AFAAGAAgAAAAhAA3RkJ+2AAAA +GwEAACcAAAAAAAAAAAAAAAAAsgkAAHRoZW1lL3RoZW1lL19yZWxzL3RoZW1lTWFuYWdlci54bWwu +cmVsc1BLBQYAAAAABQAFAF0BAACtCgAAAAA= + +------=_NextPart_01CB1E95.4412BC30 +Content-Location: file:///C:/2A821E05/readme_files/colorschememapping.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml + +<?xml version=3D"1.0" encoding=3D"UTF-8" standalone=3D"yes"?> +<a:clrMap xmlns:a=3D"http://schemas.openxmlformats.org/drawingml/2006/main"= + bg1=3D"lt1" tx1=3D"dk1" bg2=3D"lt2" tx2=3D"dk2" accent1=3D"accent1" accent= +2=3D"accent2" accent3=3D"accent3" accent4=3D"accent4" accent5=3D"accent5" a= +ccent6=3D"accent6" hlink=3D"hlink" folHlink=3D"folHlink"/> +------=_NextPart_01CB1E95.4412BC30 +Content-Location: file:///C:/2A821E05/readme_files/filelist.xml +Content-Transfer-Encoding: quoted-printable +Content-Type: text/xml; charset="utf-8" + +<xml xmlns:o=3D"urn:schemas-microsoft-com:office:office"> + <o:MainFile HRef=3D"../readme.htm"/> + <o:File HRef=3D"themedata.thmx"/> + <o:File HRef=3D"colorschememapping.xml"/> + <o:File HRef=3D"filelist.xml"/> +</xml> +------=_NextPart_01CB1E95.4412BC30-- diff --git a/wia/ProdScan/resource.h b/wia/ProdScan/resource.h new file mode 100644 index 00000000..2c1e03cc --- /dev/null +++ b/wia/ProdScan/resource.h @@ -0,0 +1,47 @@ +#pragma once + +#define IDB_TESTIMAGE_GRAY 100 +#define IDB_TESTIMAGE_COLOR 101 +#define IDB_TEST_IMPRINTER_IMAGE 102 +#define IDB_BARCODE_SAMPLE 103 +#define IDB_PATCH_CODE_SAMPLE 104 +#define IDB_MICR_SAMPLE 105 + +#define IDS_EVENT_DEVICE_CONNECTED_NAME 200 +#define IDS_EVENT_DEVICE_DISCONNECTED_NAME 201 +#define IDS_EVENT_DEVICE_CONNECTED_DESCRIPTION 202 +#define IDS_EVENT_DEVICE_DISCONNECTED_DESCRIPTION 203 +#define IDS_CMD_SYNCHRONIZE_NAME 204 +#define IDS_CMD_SYNCHRONIZE_DESCRIPTION 205 +#define IDS_EVENT_SCAN_IMAGE_NAME 206 +#define IDS_EVENT_SCAN_IMAGE_DESCRIPTION 207 +#define IDS_CMD_BUILD_DEVICE_TREE_NAME 208 +#define IDS_CMD_BUILD_DEVICE_TREE_DESCRIPTION 209 +#define IDS_CMD_DELETE_DEVICE_TREE_NAME 210 +#define IDS_CMD_DELETE_DEVICE_TREE_DESCRIPTION 211 +#define IDS_EVENT_POWER_SUSPEND_NAME 212 +#define IDS_EVENT_POWER_RESUME_NAME 213 +#define IDS_EVENT_POWER_SUSPEND_DESCRIPTION 214 +#define IDS_EVENT_POWER_RESUME_DESCRIPTION 215 +#define IDS_EVENT_TREE_UPDATED_NAME 216 +#define IDS_EVENT_TREE_UPDATED_DESCRIPTION 217 +#define IDS_CMD_START_FEEDER_NAME 218 +#define IDS_CMD_START_FEEDER_DESCRIPTION 219 +#define IDS_CMD_STOP_FEEDER_NAME 220 +#define IDS_CMD_STOP_FEEDER_DESCRIPTION 221 +#define IDS_EVENT_DEVICE_NOT_READY_NAME 222 +#define IDS_EVENT_DEVICE_NOT_READY_DESCRIPTION 223 +#define IDS_EVENT_DEVICE_READY_NAME 224 +#define IDS_EVENT_DEVICE_READY_DESCRIPTION 225 +#define IDS_EVENT_FLATBED_LID_OPEN_NAME 226 +#define IDS_EVENT_FLATBED_LID_OPEN_DESCRIPTION 227 +#define IDS_EVENT_FLATBED_LID_CLOSED_NAME 228 +#define IDS_EVENT_FLATBED_LID_CLOSED_DESCRIPTION 229 +#define IDS_EVENT_FEEDER_LOADED_NAME 230 +#define IDS_EVENT_FEEDER_LOADED_DESCRIPTION 231 +#define IDS_EVENT_FEEDER_EMPTIED_NAME 232 +#define IDS_EVENT_FEEDER_EMPTIED_DESCRIPTION 233 +#define IDS_EVENT_COVER_OPEN_NAME 234 +#define IDS_EVENT_COVER_OPEN_DESCRIPTION 235 +#define IDS_EVENT_COVER_CLOSED_NAME 236 +#define IDS_EVENT_COVER_CLOSED_DESCRIPTION 237 diff --git a/wia/ProdScan/stdafx.h b/wia/ProdScan/stdafx.h new file mode 100644 index 00000000..f7f61586 --- /dev/null +++ b/wia/ProdScan/stdafx.h @@ -0,0 +1,87 @@ +/************************************************************************** +* +* Copyright � Microsoft Corporation +* +* File Title: stdafx.h +* +* Project: Production Scanner Driver Sample +* +* Description: precompiled header file +* +***************************************************************************/ + +#pragma once + +// +// This is the size of the buffer the driver uses to write data to the +// WIA image download stream and also to declare for legacy applications +// through WIA_IPA_BUFFER_SIZE. The buffer size chosen is 64KB (65536 bytes): +// +#define DEFAULT_BUFFER_SIZE 65536 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifndef _UNICODE +#define _UNICODE +#endif + +// +// WIA_DEBUG is needed in order to enable WIA tracing in free +// builds (see WIAS_TRACE and WIAEX_ERROR in wiamdef.h): +// +#ifndef WIA_DEBUG +#define WIA_DEBUG +#endif + +// +// Windows system headers: +// + +#include <windows.h> // Windows +#include <stdlib.h> // C standard library +#include <stdio.h> // std out +#include <coguid.h> // COM +#include <objbase.h> // COM +#include <shobjidl.h> // Shell UI Extension +#include <shlobj.h> // Shell UI Extension +#include <shlwapi.h> // Shell light weight API +#include <strsafe.h> // Safe character string APIs +#include <gdiplus.h> // GDI+ +#include <limits.h> +#include <initguid.h> + +// +// WIA driver core headers: +// +#include <sti.h> // STI defines +#include <stiusd.h> // IStiUsd interface +#include <wiamindr.h> // IWiaMinidrv interface +#include <wiadevd.h> // IWiaUIExtension interface +#include <wiamdef.h> + +// +// WIA driver headers: +// +#include "constants.h" // Constant declarations +#include "basicarray.h" // CSimpleDynamicArray class +#include "propman.h" // WIA driver property manager class +#include "capman.h" // WIA driver capability manager class +#include "wiautil.h" // WIA driver helper functions +#include "resource.h" // WIA driver resource definitions +#include "minidrv.h" // WIA driver header + +// +// Helpers for image file format translations (using GDI+): +// +using namespace Gdiplus; +#include "fileconv.h" + +// +// WIA tracing macro helpers: +// +#define WIAEX_ERROR(args) { WIAS_ERROR((g_hInst, "Error in %s (%u):", __FUNCTION__, __LINE__)); WIAS_ERROR(args); } +#define WIAEX_TRACE(args) { FAILED(hr) ? WIAS_ERROR(args) : WIAS_TRACE(args); } +#define WIAEX_TRACE_FUNC_HR { FAILED(hr) ? WIAS_ERROR((g_hInst, "%s failed, hr = 0x%08X", __FUNCTION__, hr)) : WIAS_TRACE((g_hInst, "%s succeeded, hr = 0x%08X", __FUNCTION__, hr)); } +#define WIAEX_TRACE_BEGIN { WIAS_TRACE((g_hInst, "%s..", __FUNCTION__)); } |
