summaryrefslogtreecommitdiff
path: root/wia/ProdScan
diff options
context:
space:
mode:
authorkarlf <[email protected]>2016-08-11 13:28:13 -0700
committerkarlf <[email protected]>2016-08-11 13:28:13 -0700
commit96eb96dfb613e4c745db6bd1f53a92fe7e2290fc (patch)
treead5f3ede5cbcd6b598677ce41bcf8318471bdd92 /wia/ProdScan
parent687b274aa38fd05c8c26e3068932121876d7f745 (diff)
Updated for "Windows 10 Anniversary Update" (Version 1607)
Diffstat (limited to 'wia/ProdScan')
-rw-r--r--wia/ProdScan/BasicArray.h381
-rw-r--r--wia/ProdScan/CapMan.cpp515
-rw-r--r--wia/ProdScan/CapMan.h81
-rw-r--r--wia/ProdScan/Constants.h230
-rw-r--r--wia/ProdScan/Events.cpp180
-rw-r--r--wia/ProdScan/FileConv.cpp730
-rw-r--r--wia/ProdScan/FileConv.h37
-rw-r--r--wia/ProdScan/InitProp.cpp3134
-rw-r--r--wia/ProdScan/MiniDrv.cpp2619
-rw-r--r--wia/ProdScan/MiniDrv.h611
-rw-r--r--wia/ProdScan/ProdScan.def9
-rw-r--r--wia/ProdScan/ProdScan.inf78
-rw-r--r--wia/ProdScan/ProdScan.rc103
-rw-r--r--wia/ProdScan/ProdScan.vcxproj229
-rw-r--r--wia/ProdScan/ProdScan.vcxproj.Filters60
-rw-r--r--wia/ProdScan/PropMan.cpp1499
-rw-r--r--wia/ProdScan/PropMan.h205
-rw-r--r--wia/ProdScan/Res/Barcodes.xml36
-rw-r--r--wia/ProdScan/Res/Micr.xml17
-rw-r--r--wia/ProdScan/Res/PatchCod.xml14
-rw-r--r--wia/ProdScan/Res/Printer.bmpbin0 -> 38462 bytes
-rw-r--r--wia/ProdScan/Res/TestGray.jpgbin0 -> 1044041 bytes
-rw-r--r--wia/ProdScan/Res/TestRGB.jpgbin0 -> 1067155 bytes
-rw-r--r--wia/ProdScan/ScanJobs.cpp2531
-rw-r--r--wia/ProdScan/Server.cpp476
-rw-r--r--wia/ProdScan/StiUSD.cpp821
-rw-r--r--wia/ProdScan/Validate.cpp1994
-rw-r--r--wia/ProdScan/WiaUtil.cpp406
-rw-r--r--wia/ProdScan/WiaUtil.h59
-rw-r--r--wia/ProdScan/readme.mht956
-rw-r--r--wia/ProdScan/resource.h47
-rw-r--r--wia/ProdScan/stdafx.h87
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
new file mode 100644
index 00000000..6371a918
--- /dev/null
+++ b/wia/ProdScan/Res/Printer.bmp
Binary files differ
diff --git a/wia/ProdScan/Res/TestGray.jpg b/wia/ProdScan/Res/TestGray.jpg
new file mode 100644
index 00000000..02bd768d
--- /dev/null
+++ b/wia/ProdScan/Res/TestGray.jpg
Binary files differ
diff --git a/wia/ProdScan/Res/TestRGB.jpg b/wia/ProdScan/Res/TestRGB.jpg
new file mode 100644
index 00000000..dab44312
--- /dev/null
+++ b/wia/ProdScan/Res/TestRGB.jpg
Binary files differ
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"&#45;-"/>
+ <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"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</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"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</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"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</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"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</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"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</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"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</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"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</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"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+</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>&nbsp;</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'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </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'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </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'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </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__)); }