summaryrefslogtreecommitdiff
path: root/wia/ProdScan/MiniDrv.cpp
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/MiniDrv.cpp
parent687b274aa38fd05c8c26e3068932121876d7f745 (diff)
Updated for "Windows 10 Anniversary Update" (Version 1607)
Diffstat (limited to 'wia/ProdScan/MiniDrv.cpp')
-rw-r--r--wia/ProdScan/MiniDrv.cpp2619
1 files changed, 2619 insertions, 0 deletions
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;
+}