|
|
/*******************************************************************************
* * (C) COPYRIGHT MICROSOFT CORP., 1998 * * TITLE: WiaTrans.Cpp * * VERSION: 2.0 * * AUTHOR: ReedB * * DATE: 7 Apr, 1998 * * DESCRIPTION: * Implementation of IBandedTransfer interface for the WIA device class driver. * *******************************************************************************/ #include "precomp.h"
#include "stiexe.h"
#include "wiamindr.h"
#include "wiapsc.h"
#include "helpers.h"
#include "ienumwfi.h"
#include "devmgr.h"
//
// Until transfers are re-written to use N buffers, we always use 2.
//
#define WIA_NUM_TRANS_BUFFERS 2
/**************************************************************************\
* DataThreadProc * * Use separate thread to call clients * * Arguments: * * pInfo - parameters for callback * * Return Value: * * Status * * History: * * 11/19/1998 Original Version * \**************************************************************************/
DWORD WINAPI DataThreadProc(LPVOID lpParameter) { DBG_FN(::DataThreadProc);
HRESULT hr;
PWIA_DATA_THREAD_INFO pInfo = (PWIA_DATA_THREAD_INFO)lpParameter;
hr = CoInitializeEx(0,COINIT_MULTITHREADED);
if (FAILED(hr)) { DBG_ERR(("Thread callback, CoInitializeEx failed (0x%X)", hr)); pInfo->hr = E_FAIL; return hr; }
pInfo->hr = S_OK;
do {
//
// wait for message from MiniDrvCallback to call client
//
DWORD dwRet = WaitForSingleObject(pInfo->hEventStart,INFINITE);
//
// check termination
//
if (pInfo->bTerminateThread) { break; }
//
// valid wait code
//
if (dwRet == WAIT_OBJECT_0) {
//
// 64bit fix. XP client code:
// pInfo->hr = pInfo->pIDataCallback->BandedDataCallback(
// pInfo->lReason,
// pInfo->lStatus,
// pInfo->lPercentComplete,
// pInfo->lOffset,
// pInfo->lLength,
// pInfo->lClientAddress,
// pInfo->lMarshalLength,
// pInfo->pBuffer);
//
// We set ClientAddress to NULL to force COM marshalling.
// That way we avoid using the shared memory window we
// set up. It is the shared memory window that
// messes things up for us, since only a 32bit value
// was being used to store the shared buffer pointer
// in the Client's address space.
//
pInfo->hr = pInfo->pIDataCallback->BandedDataCallback( pInfo->lReason, pInfo->lStatus, pInfo->lPercentComplete, pInfo->lOffset, pInfo->lLength, 0, pInfo->lMarshalLength, pInfo->pBuffer);
if (FAILED(pInfo->hr)) { DBG_ERR(("DataThreadProc,BandedDataCallback failed (0x%X)", hr)); }
} else { DBG_ERR(("Thread callback, WiatForSingleObject failed")); pInfo->hr = E_FAIL; break; }
SetEvent(pInfo->hEventComplete);
} while (TRUE);
CoUninitialize(); return hr; }
/*******************************************************************************
* * QueryInterface * AddRef * Release * Constructor/Destructor * Initialize * * DESCRIPTION: * COM methods for CWiaMiniDrvCallBack. This class is used by itGetImage * to respond to mini driver callbacks during image transfers. * *******************************************************************************/
HRESULT _stdcall CWiaMiniDrvCallBack::QueryInterface(const IID& iid, void** ppv) { *ppv = NULL;
if (iid == IID_IUnknown || iid == IID_IWiaMiniDrvCallBack) { *ppv = (IWiaMiniDrvCallBack*) this; } else { return E_NOINTERFACE; }
AddRef(); return S_OK; }
ULONG _stdcall CWiaMiniDrvCallBack::AddRef() { InterlockedIncrement((long*) &m_cRef); return m_cRef; }
ULONG _stdcall CWiaMiniDrvCallBack::Release() { ULONG ulRefCount = m_cRef - 1;
if (InterlockedDecrement((long*) &m_cRef) == 0) { delete this; return 0; } return ulRefCount; }
/**************************************************************************\
* CWiaMiniDrvCallBack::CWiaMiniDrvCallBack * * Setup data callback control and thread * * Arguments: * * * * Return Value: * * Status * * History: * * 4/9/1999 Original Version * \**************************************************************************/
CWiaMiniDrvCallBack::CWiaMiniDrvCallBack() { m_cRef = 0; m_hThread = NULL; m_ThreadInfo.hEventStart = NULL; m_ThreadInfo.hEventComplete = NULL; };
/**************************************************************************\
* CWiaMiniDrvCallBack::~CWiaMiniDrvCallBack * * free callback thread and event * * Arguments: * * * * Return Value: * * Status * * History: * * 4/9/1999 Original Version * \**************************************************************************/
CWiaMiniDrvCallBack::~CWiaMiniDrvCallBack() { DBG_FN(CWiaMiniDrvCallBack::~CWiaMiniDrvCallBack); //
// terminate thread and delete event
//
if (m_ThreadInfo.hEventStart) {
if (m_hThread) {
m_ThreadInfo.bTerminateThread = TRUE;
SetEvent(m_ThreadInfo.hEventStart);
//
// wait for thread to terminate
//
WaitForSingleObject(m_hThread,10000); CloseHandle(m_hThread); m_hThread = NULL; }
CloseHandle(m_ThreadInfo.hEventStart);
if (m_ThreadInfo.hEventComplete) { CloseHandle(m_ThreadInfo.hEventComplete); }
//
// force kill thread?
//
} }
/**************************************************************************\
* CWiaMiniDrvCallBack::Initialize * * Set up callback class * * Arguments: * * pmdtc - context information for this callback * pIUnknown - interface pointer back to client * * Return Value: * * Status * * History: * * 11/12/1998 Original Version * \**************************************************************************/
HRESULT CWiaMiniDrvCallBack::Initialize( PMINIDRV_TRANSFER_CONTEXT pmdtc, IWiaDataCallback *pIWiaDataCallback) { DBG_FN(CWiaMiniDrvCallback::Initialize); ASSERT(pmdtc != NULL);
if(pmdtc == NULL) { return E_INVALIDARG; }
//
// init callback params
//
m_mdtc = *pmdtc;
//
// create thread communications event, auto reset
//
// hEventStart is signaled when the MiniDrvCallback routine wishes the
// thread to begin a new callback
//
// hEventComplete is signaled when thread is ready to accept another
// callback
//
m_ThreadInfo.pIDataCallback = pIWiaDataCallback;
m_ThreadInfo.hEventStart = CreateEvent(NULL,FALSE,FALSE,NULL); m_ThreadInfo.hEventComplete = CreateEvent(NULL,FALSE,TRUE,NULL);
if ((m_ThreadInfo.hEventStart == NULL) || ((m_ThreadInfo.hEventComplete == NULL))) { DBG_ERR(("CWiaMiniDrvCallBack::Initialize, failed to create event")); return E_FAIL; }
//
// create callback thread
//
m_ThreadInfo.bTerminateThread = FALSE;
m_hThread = CreateThread(NULL,0,DataThreadProc,&m_ThreadInfo,0,&m_dwThreadID);
if (m_hThread == NULL) { DBG_ERR(("CWiaMiniDrvCallBack::Initialize, failed to create thread")); return E_FAIL; }
//
// init first thread return
//
m_ThreadInfo.hr = S_OK; return S_OK; }
/**************************************************************************\
* CWiaMiniDrvCallBack::MiniDrvCallback * * This callback is used by itGetImage to respond to mini driver callbacks * during image transfers. * * Arguments: * * lReason - message to application * lStatus - status flags * lPercentComplete - operation percent complete * lOffset - buffer offset for data operation * lLength - length of this buffer operation * pmdtc - pointer to the mini driver context. * lReserved - reserved * * Return Value: * * Status * * History: * * 11/12/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaMiniDrvCallBack::MiniDrvCallback( LONG lReason, LONG lStatus, LONG lPercentComplete, LONG lOffset, LONG lLength, PMINIDRV_TRANSFER_CONTEXT pmdtc, LONG lReserved) { DBG_FN(CMiniDrvCallback::MiniDrvCallback); HRESULT hr = S_OK;
//
// verify driver hasn't changed active buffer
//
LONG ActiveBuffer = 0; PBYTE pBuffer = NULL; LONG lMarshalLength = 0; BOOL bOOBData = FALSE;
//
// If the application didn't provide a callback, then nothing left to do.
//
if (!m_ThreadInfo.pIDataCallback) { return S_OK; }
if ((lReason == IT_MSG_DATA) || (lReason == IT_MSG_DATA_HEADER) || (lReason == IT_MSG_NEW_PAGE)) {
if (!pmdtc) { DBG_ERR(("MiniDrvCallback::MiniDrvCallback, transfer context is NULL!")); return E_POINTER; }
if (pmdtc == NULL) { DBG_ERR(("NULL Minidriver context handed to us by driver!")); return E_POINTER; } if (pmdtc->lActiveBuffer != m_mdtc.lActiveBuffer) { DBG_TRC(("MiniDrvCallback, Active Buffers have been changed. This OK if the driver meant to do this. Possible repurcussion will be exception thrown on proxy/stub if buffer or length is incorrect")); } if (pmdtc->pTransferBuffer != m_mdtc.pTransferBuffer) { DBG_TRC(("MiniDrvCallback, Transfer Buffers have been changed. This OK if the driver meant to do this. Possible repurcussion will be exception thrown on proxy/stub if buffer or length is incorrect")); }
//
// get currently active buffer from driver, use member function
// for all other information
//
// for mapped case, no buffer to copy
//
// for remote case, must copy buffer
//
ActiveBuffer = pmdtc->lActiveBuffer;
if (m_mdtc.lClientAddress == 0) { pBuffer = pmdtc->pTransferBuffer; lMarshalLength = lLength; } } else if ((lReason == IT_MSG_FILE_PREVIEW_DATA) || (lReason == IT_MSG_FILE_PREVIEW_DATA_HEADER)) {
//
// This is an OOB Data message, so mark bOOBData as TRUE
//
bOOBData = TRUE;
//
// NOTE: OOBData is stored in the mini driver transfer context's
// pBaseBuffer member. So, if pBaseBuffer is non-zero, then some
// OOBData is being sent, so set pBuffer and the MarshalLength.
//
if (pmdtc->pBaseBuffer) { pBuffer = pmdtc->pBaseBuffer; lMarshalLength = lLength; } }
//
// Check whether we are using single or double buffering for
// banded data callbacks
//
if ((m_mdtc.lNumBuffers == 1 && m_mdtc.bTransferDataCB) || bOOBData) {
//
// NOTE: this section is a hack to get around the fact that
// the transfer was hard-coded to use double buffering. This hack
// fixes the case when an App. specifies not to use double buffering.
// This whole data transfer section should be re-written to handle
// N buffers at some later stage.
//
//
// 64bit fix. XP client code:
// hr = m_ThreadInfo.pIDataCallback->BandedDataCallback(lReason,
// lStatus,
// lPercentComplete,
// lOffset,
// lLength,
// m_mdtc.lClientAddress,
// lMarshalLength,
// pBuffer);
//
// We set ClientAddress to NULL to force COM marshalling.
// That way we avoid using the shared memory window we
// set up. It is the shared memory window that
// messes things up for us, since only a 32bit value
// was being used to store the shared buffer pointer
// in the Client's address space.
//
hr = m_ThreadInfo.pIDataCallback->BandedDataCallback( lReason, lStatus, lPercentComplete, lOffset, lLength, 0, lMarshalLength, pBuffer); if(FAILED(hr)) { DBG_ERR(("MiniDrvCallback, BandedDataCallback returned 0x%lx", hr)); }
} else { //
// wait for CB thread to be ready, check old status
//
DWORD dwRet = WaitForSingleObject(m_ThreadInfo.hEventComplete, 30000);
if (dwRet == WAIT_TIMEOUT) { DBG_ERR(("MiniDrvCallback, callback timeout, cancel data transfer")); hr = S_FALSE;
} else if (dwRet == WAIT_OBJECT_0) {
hr = m_ThreadInfo.hr;
} else { DBG_ERR(("MiniDrvCallback, error in callback wait, ret = 0x%lx",dwRet)); hr = E_FAIL; }
//
// error messages
//
if (hr == S_FALSE) {
DBG_WRN(("MiniDrvCallback, client canceled scan (0x%X)", hr));
//
// set the start event so that DataThreadProc will still be able to
// send IT_MSG_TERMINATION etc. to client.
//
SetEvent(m_ThreadInfo.hEventStart); } else if (hr == S_OK) {
//
// If this is a IT_MSG_TERMINATION message, call it directly
//
if (lReason == IT_MSG_TERMINATION) { //
// 64bit fix. XP client code:
// hr = m_ThreadInfo.pIDataCallback->BandedDataCallback(lReason,
// lStatus,
// lPercentComplete,
// lOffset,
// lLength,
// m_mdtc.lClientAddress,
// lMarshalLength,
// pBuffer);
//
// We set ClientAddress to NULL to force COM marshalling.
// That way we avoid using the shared memory window we
// set up. It is the shared memory window that
// messes things up for us, since only a 32bit value
// was being used to store the shared buffer pointer
// in the Client's address space.
//
hr = m_ThreadInfo.pIDataCallback->BandedDataCallback( lReason, lStatus, lPercentComplete, lOffset, lLength, 0, lMarshalLength, pBuffer); } else {
//
// send new request to callback thread
//
m_ThreadInfo.lReason = lReason; m_ThreadInfo.lStatus = lStatus; m_ThreadInfo.lPercentComplete = lPercentComplete; m_ThreadInfo.lOffset = lOffset; m_ThreadInfo.lLength = lLength;
//
// if remote, client address is 0
//
if (m_mdtc.lClientAddress == 0) { m_ThreadInfo.lClientAddress = 0; } else { m_ThreadInfo.lClientAddress = m_mdtc.lClientAddress + m_mdtc.lActiveBuffer * m_mdtc.lBufferSize; }
m_ThreadInfo.lMarshalLength = lMarshalLength; m_ThreadInfo.pBuffer = pBuffer;
//
// kick off callback thread
//
SetEvent(m_ThreadInfo.hEventStart);
//
// switch to next transfer buffers
//
if ((lReason == IT_MSG_DATA) || (lReason == IT_MSG_DATA_HEADER) || (lReason == IT_MSG_NEW_PAGE)) {
//
// use next buffer
//
pmdtc->lActiveBuffer++;
if (pmdtc->lActiveBuffer >= m_mdtc.lNumBuffers) { pmdtc->lActiveBuffer = 0; }
m_mdtc.lActiveBuffer = pmdtc->lActiveBuffer;
//
// calc new tran buffer
//
m_mdtc.pTransferBuffer = m_mdtc.pBaseBuffer + m_mdtc.lActiveBuffer * m_mdtc.lBufferSize;
pmdtc->pTransferBuffer = m_mdtc.pTransferBuffer; } } } }
return hr; }
/**************************************************************************\
* CWiaItem::idtGetBandedData * * Use shared memory window and data callbacks to transfer image to * client * * Arguments: * * pWiaDataTransInfo - sharded buffer information * pIWiaDataCallback - client callback interface * * Return Value: * * Status * * History: * * 11/6/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::idtGetBandedData( PWIA_DATA_TRANSFER_INFO pWiaDataTransInfo, IWiaDataCallback *pIWiaDataCallback) { DBG_FN(CWiaItem::idtGetBandedData); HRESULT hr;
//
// Check whether item properties have been initialized
//
if (!m_bInitialized) {
hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::idtGetBandedData, InitLazyProps failed")); return hr; } }
return CommonGetData(NULL, pWiaDataTransInfo, pIWiaDataCallback); }
/**************************************************************************\
* CWiaItem::idtGetData * * Uses normal IDATAOBJECT transfer mechanisms but provides callback * status for the transfer * * Arguments: * * pstm - data storage * pIWiaDataCallback - optional callback routine * * Return Value: * * Status * * History: * * 10/28/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::idtGetData( LPSTGMEDIUM pMedium, IWiaDataCallback *pIWiaDataCallback) { DBG_FN(CWiaItem::idtGetData); HRESULT hr; WIA_DATA_TRANSFER_INFO WiaDataTransInfo;
memset(&WiaDataTransInfo, 0, sizeof(WiaDataTransInfo));
//
// Fill out the necessary transfer info. to be used for OOB Data.
//
WiaDataTransInfo.ulSize = sizeof(WiaDataTransInfo); WiaDataTransInfo.bDoubleBuffer = FALSE; WiaDataTransInfo.ulReserved3 = 1;
//
// Check whether item properties have been initialized
//
if (!m_bInitialized) {
hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::idtGetData, InitLazyProps failed")); return hr; } }
return CommonGetData(pMedium, &WiaDataTransInfo, pIWiaDataCallback); }
/**************************************************************************\
* CWiaItem::idtAllocateTransferBuffer * * Allocate a transfer buffer for the banded transfer methods. * * Arguments: * * pWiaDataTransInfo - buffer information * * Return Value: * * Status * * History: * * 11/12/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::idtAllocateTransferBuffer( PWIA_DATA_TRANSFER_INFO pWiaDataTransInfo) { DBG_FN(CWiaItem::idtAllocTransferBuffer);
LONG lSize = m_dcbInfo.ulBufferSize; /*pWiaDataTransInfo->ulBufferSize;*/ HANDLE hSection = (HANDLE)m_dcbInfo.pMappingHandle; /*pWiaDataTransInfo->ulSection;*/ ULONG ulProcessID = m_dcbInfo.ulClientProcessId; /*pWiaDataTransInfo->ulReserved2;*/ LONG lNumBuffers = pWiaDataTransInfo->ulReserved3;
//
// NOTE: This will be a problem in 64-bit!! We do this here because
// padtc->ulReserved1 is packed into a MiniDrvTransferContext later, which
// uses a 32-bit ULONG for the client address.
//
//
// 64bit fix. XP client code:
// pWiaDataTransInfo->ulReserved1 = m_dcbInfo.pTransferBuffer;
//
// We set this to NULL to force COM marshalling.
// That way we avoid using the shared memory window we
// set up. It is the shared memory window that
// messes things up for us, since only a 32bit value
// was being used to store the shared buffer pointer
// in the Client's address space.
//
pWiaDataTransInfo->ulReserved1 = 0;
//
// Corresponding driver item must be valid.
//
HRESULT hr = ValidateWiaDrvItemAccess(m_pWiaDrvItem); if (FAILED(hr)) { DBG_ERR(("CWiaItem::idtAllocateTransferBuffer, ValidateWiaDrvItemAccess failed")); return hr; }
//
// exclusive access for entire transfer
//
//
// if section is NULL, alloc buffer
//
if (hSection == 0) { m_pBandBuffer = (PBYTE)LocalAlloc(0,lSize);
if (m_pBandBuffer == NULL) { DBG_ERR(("CWiaItem::idtAllocateTransferBuffer failed mem alloc")); return E_OUTOFMEMORY; }
//
// Use m_lBandBufferLength = lSize / lNumBuffers if we want
// the ulBufferSize to be the entire size instead of the
// chunk size.
//
m_lBandBufferLength = lSize / lNumBuffers; m_bMapSection = FALSE; return S_OK; }
//
// map client's section
//
HANDLE TokenHandle;
//
// Check for open token.
//
if (OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &TokenHandle)) { DBG_ERR(("itAllocateTransferBuffer, Open token on entry, last error: %d", GetLastError())); CloseHandle(TokenHandle); }
//
// Do we need max sector size?
//
if (lSize > 0) {
//
// transfer buffer for this device must not already exist
//
if (m_hBandSection == NULL) {
//
// duplicate hSection handle
//
HANDLE hClientProcess = NULL; HANDLE hServerProcess = GetCurrentProcess();
hClientProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, ulProcessID);
if (hClientProcess) {
BOOL bRet = DuplicateHandle(hClientProcess, hSection, hServerProcess, &m_hBandSection, 0, FALSE, DUPLICATE_SAME_ACCESS);
CloseHandle(hClientProcess);
if (m_hBandSection != NULL) { hr = S_OK; m_pBandBuffer = (PBYTE) MapViewOfFileEx(m_hBandSection, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, lSize, NULL);
//
// Use m_lBandBufferLength = lSize / lNumBuffers if we want
// the ulBufferSize to be the entire size instead of the
// chunk size.
//
m_lBandBufferLength = lSize / lNumBuffers;
if (m_pBandBuffer == NULL) { DBG_ERR(("CWiaItem::itAllocateTransferBuffer, failed MapViewOfFileEx")); CloseHandle(m_hBandSection); m_hBandSection = NULL; hr = E_OUTOFMEMORY; } else { m_bMapSection = TRUE; } } else { DBG_ERR(("CWiaItem::itAllocateTransferBuffer, failed DuplicateHandle")); hr = E_OUTOFMEMORY; } } else { LONG lRet = GetLastError(); DBG_ERR(("CWiaItem::itAllocateTransferBuffer, OpenProcess failed, GetLastError = 0x%X", lRet));
hr = HRESULT_FROM_WIN32(lRet); }
if (OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &TokenHandle)) { DBG_ERR(("itAllocateTransferBufferEx, Open token after revert, last error: %d", GetLastError())); CloseHandle(TokenHandle); } } else { DBG_ERR(("CWiaItem::itAllocateTransferBuffer failed , buffer already allocated")); hr = E_INVALIDARG; } } else { hr = E_INVALIDARG; } return (hr); }
/**************************************************************************\
* CWiaItem::idtFreeTransferBufferEx * * Free a transfer buffer allocated by idtAllocateTransferBuffer. * * Arguments: * * None * * Return Value: * * Status * * History: * * 10/28/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::idtFreeTransferBufferEx(void) { DBG_FN(CWiaItem::idtFreeTransferBufferEx); if (m_pBandBuffer != NULL) {
if (m_bMapSection) { UnmapViewOfFile(m_pBandBuffer); } else { LocalFree(m_pBandBuffer); } m_pBandBuffer = NULL; }
if (m_hBandSection != NULL) { CloseHandle(m_hBandSection); m_hBandSection = NULL; }
m_lBandBufferLength = 0; return S_OK; }
/**************************************************************************\
* CWiaItem::idtQueryGetData * * find out if the tymed/format pair is supported * * Arguments: * * pwfi - format and tymed info * * Return Value: * * Status * * History: * * 11/17/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::idtQueryGetData(WIA_FORMAT_INFO *pwfi) { DBG_FN(CWiaItem::idtQueryGetData);
//
// Do parameter validation
//
if (!pwfi) { DBG_ERR(("CWiaItem::idtQueryGetData, WIA_FORMAT_INFO arg is NULL!")); return E_INVALIDARG; }
if (IsBadReadPtr(pwfi, sizeof(WIA_FORMAT_INFO))) { DBG_ERR(("CWiaItem::idtQueryGetData, WIA_FORMAT_INFO arg is a bad read pointer!")); return E_INVALIDARG; }
//
// Corresponding driver item must be valid.
//
HRESULT hr = ValidateWiaDrvItemAccess(m_pWiaDrvItem); if (FAILED(hr)) { return hr; }
//
// Check whether item properties have been initialized
//
if (!m_bInitialized) {
hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::idtQueryGetData, InitLazyProps failed")); return hr; } }
//
// A tymed must be provided.
//
if (pwfi->lTymed == TYMED_NULL) { return DV_E_TYMED; }
LONG lnumFormatInfo; WIA_FORMAT_INFO *pwfiDriver;
//
// Call the mini driver to see if this format is supported.
//
{ LOCK_WIA_DEVICE _LWD(this, &hr);
if(SUCCEEDED(hr)) { hr = m_pActiveDevice->m_DrvWrapper.WIA_drvGetWiaFormatInfo((BYTE*)this, 0, &lnumFormatInfo, &pwfiDriver, &m_lLastDevErrVal); } }
if (SUCCEEDED(hr)) {
//
// Make sure we can read the array that was given to us.
//
if (IsBadReadPtr(pwfiDriver, sizeof(WIA_FORMAT_INFO) * lnumFormatInfo)) { DBG_ERR(("CWiaItem::idtQueryGetData, Bad pointer from driver (array of WIA_FORMAT_INFO)")); return E_FAIL; }
//
// Look for the requested Tymed/Format pair. Return S_OK if found.
//
for (LONG lIndex = 0; lIndex < lnumFormatInfo; lIndex++) { if ((IsEqualGUID(pwfiDriver[lIndex].guidFormatID, pwfi->guidFormatID)) && (pwfiDriver[lIndex].lTymed == pwfi->lTymed)) {
return S_OK; } }
DBG_ERR(("CWiaItem::idtQueryGetData, Tymed and Format pair not supported"));
hr = E_INVALIDARG; }
return hr; }
/**************************************************************************\
* CWiaItem::idtEnumWIA_FORMAT_INFO * * Format enumeration for the banded transfer methods. * * Arguments: * * dwDir - Data transfer direction flag. * ppIEnum - Pointer to returned enumerator. * * Return Value: * * Status * * History: * * 11/17/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::idtEnumWIA_FORMAT_INFO( IEnumWIA_FORMAT_INFO **ppIEnum) { DBG_FN(CWiaItem::idtEnumWIA_FORMAT_INFO); HRESULT hr = E_FAIL;
if (!ppIEnum) { return E_POINTER; }
*ppIEnum = NULL;
//
// Check whether item properties have been initialized
//
if (!m_bInitialized) {
hr = InitLazyProps(); if (FAILED(hr)) { DBG_ERR(("CWiaItem::idtEnumWIA_FORMAT_INFO, InitLazyProps failed")); return hr; } }
CEnumWiaFormatInfo *pIEnum; pIEnum = new CEnumWiaFormatInfo();
if (pIEnum == NULL) { return E_OUTOFMEMORY; }
hr = pIEnum->Initialize(this); if (SUCCEEDED(hr)) { pIEnum->AddRef(); *ppIEnum = pIEnum; } else { delete pIEnum; pIEnum = NULL; } return hr; }
/**************************************************************************\
* CWiaItem::idtGetExtendedTransferInfo * * Returns extended transfer information such as optimal buffer size for * the transfer, number of buffers server will use etc. * * Arguments: * * pExtendedTransferInfo - Pointer to a structure which will hold the * transfer info on return. * * Return Value: * * Status * * History: * * 01/23/2000 Original Version * \**************************************************************************/
HRESULT CWiaItem::idtGetExtendedTransferInfo( PWIA_EXTENDED_TRANSFER_INFO pExtendedTransferInfo) { DBG_FN(CWiaItem::idtGetExtendedTransferInfo); HRESULT hr = S_OK;
//
// Clear the structure and set the size
//
memset(pExtendedTransferInfo, 0, sizeof(*pExtendedTransferInfo)); pExtendedTransferInfo->ulSize = sizeof(*pExtendedTransferInfo);
//
// Set the number of buffers. This number is the number of buffers
// that the server will use during callback data transfers. Each buffer
// will be WIA_DATA_TRANSFER_INFO->ulBufferSize large specified in the
// call to idtGetBandedData.
//
pExtendedTransferInfo->ulNumBuffers = WIA_NUM_TRANS_BUFFERS;
//
// Set the buffer size values. The assumption is that the
// WIA_IPA_BUFFER_SIZE valid values will be set as follows:
// min - will specify the minimum value for buffer size
// max - will specify the maxium buffer size
// nom - will specify the optimal buffer size
//
hr = GetBufferValues(this, pExtendedTransferInfo); if (FAILED(hr)) { DBG_ERR(("CWiaItem::idtGetExtendedTransferInfo, Failed to get buffer size information!")); }
return hr; }
/**************************************************************************\
* AllocBufferFile * * Open file for data transfer. If cbItemSize == 0, just create * a file, don't memory map. * * Arguments: * * pstm - in/out stream * cbItemSize - size of image, 0 means driver doesn't know size. * phBuffer - file handle * ppImage - buffer pointer * * Return Value: * * Status * * History: * * 4/6/1999 Original Version * \**************************************************************************/
HRESULT AllocBufferFile( IN OUT STGMEDIUM* pstm, IN LONG cbItemSize, OUT HANDLE* phBuffer, OUT BYTE** ppImage) { DBG_FN(::AllocBufferFile); HRESULT hr = S_OK; USES_CONVERSION;
*phBuffer = NULL; *ppImage = NULL;
pstm->pUnkForRelease = NULL;
//
// NOTE: This file should already have been created on the proxy side.
// We only want to open it here. This is so the file is created
// with the client credentials, with the client as owner.
//
*phBuffer = CreateFile(W2T(pstm->lpszFileName), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | SECURITY_ANONYMOUS | SECURITY_SQOS_PRESENT, NULL); if (*phBuffer == INVALID_HANDLE_VALUE) { LONG lRet = GetLastError(); DBG_ERR(("AllocBufferFile, CreateFile on %S failed, GetLastError = 0x%X", pstm->lpszFileName, lRet));
hr = HRESULT_FROM_WIN32(lRet); } else if (GetFileType(*phBuffer) != FILE_TYPE_DISK) { CloseHandle(*phBuffer); *phBuffer = INVALID_HANDLE_VALUE; DBG_ERR(("AllocBufferFile, WIA will only transfer to files of type FILE_TYPE_DISK")); hr = E_INVALIDARG; }
//
// If file size is 0, mini driver can't determine size yet. Just create
// file. If size is not 0, then memory map the file
//
if ((cbItemSize != 0) && SUCCEEDED(hr)) {
HANDLE hMapped = CreateFileMapping(*phBuffer, NULL, PAGE_READWRITE, 0, cbItemSize, NULL); if (hMapped) { *ppImage = (PBYTE) MapViewOfFileEx(hMapped, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, cbItemSize, NULL); }
//
// hMapped is not needed any more in our code, so close the user mode
// handle. The Section will be destroyed when UnMapViewOfFileEx called.
//
CloseHandle(hMapped);
if (!ppImage) { DBG_ERR(("AllocBufferFile, unable to map file, size: %d", cbItemSize)); CloseHandle(*phBuffer); *phBuffer = INVALID_HANDLE_VALUE;
hr = E_OUTOFMEMORY; } }
if (FAILED(hr)) { CoTaskMemFree(pstm->lpszFileName); pstm->lpszFileName = NULL; }
return hr; }
/**************************************************************************\
* CloseBufferFile * * Close file/mapping. NOTE: Don't use tymed from STGMEDIUM!!! * * Arguments: * * tymed - media type - will be TYMED_FILE or TYMED_MULTIPAGE_FILE * pstm - stream * pBuffer - memory mapped buffer * hImage - File Handle * hrTransfer - Indicated whether data transfer is successful, if not, * delete the temporary file when TYMED_FILE is used or * memory buffer when tyme_hglobal is used. * * Return Value: * * Status * * History: * * 4/6/1999 Original Version * \**************************************************************************/
void CloseBufferFile( LONG lTymed, STGMEDIUM *pstm, PBYTE pBuffer, HANDLE hImage, HRESULT hrTransfer) { DBG_FN(::CloseBufferFile); if (pBuffer) { UnmapViewOfFile(pBuffer); }
if (hImage != INVALID_HANDLE_VALUE) { CloseHandle(hImage); }
if(lTymed == TYMED_MULTIPAGE_FILE) {
if(hrTransfer == WIA_ERROR_PAPER_JAM || hrTransfer == WIA_ERROR_PAPER_EMPTY || hrTransfer == WIA_ERROR_PAPER_PROBLEM) { // any of these are good reason not to delete the file
return ; } }
if (hrTransfer != S_OK) {
#ifdef UNICODE
DeleteFile(pstm->lpszFileName); #else
char szFileName[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, pstm->lpszFileName, -1, szFileName, sizeof(szFileName), NULL, NULL); DeleteFile(szFileName); #endif
} }
/**************************************************************************\
* PrepCallback * * Prepares a callback for use during data transfer. * * Arguments: * * pIWiaDataCallback - optional callback routine * pmdtc - pointer to mini driver data transfer context * ppIcb - pointer to returned mini driver callback interface. * * Return Value: * * Status * * History: * * 10/28/1998 Original Version * \**************************************************************************/
HRESULT _stdcall PrepCallback( IWiaDataCallback *pIWiaDataCallback, PMINIDRV_TRANSFER_CONTEXT pmdtc, IWiaMiniDrvCallBack **ppIcb) { DBG_FN(::PrepCallback);
*ppIcb = NULL; pmdtc->pIWiaMiniDrvCallBack = NULL;
//
// Always create the callback object so drivers don't have to deal
// with NULLs
//
//if (pIWiaDataCallback) {
HRESULT hr;
CWiaMiniDrvCallBack *pCMiniDrvCB = new CWiaMiniDrvCallBack();
if (pCMiniDrvCB) {
hr = pCMiniDrvCB->Initialize(pmdtc, pIWiaDataCallback); if (SUCCEEDED(hr)) {
hr = pCMiniDrvCB->QueryInterface(IID_IWiaMiniDrvCallBack, (void **)ppIcb); if (SUCCEEDED(hr)) { pmdtc->pIWiaMiniDrvCallBack = *ppIcb; } else { DBG_ERR(("PrepCallback, failed QI of pCMiniDrvCB")); } } else { delete pCMiniDrvCB; } } else { DBG_ERR(("PrepCallback, failed memory alloc of pCMiniDrvCB")); hr = E_OUTOFMEMORY; } return hr; //}
//else {
// return S_FALSE;
//}
}
/**************************************************************************\
* CWiaItem::GetData * * Handles TYMED_FILE specific portion of the data transfer. * * Arguments: * * lDataSize - size of image data, zero if mini driver doesn't know. * pstm - data storage * pIWiaDataCallback - optional callback routine * pmdtc - pointer to mini driver data transfer context * * Return Value: * * Status * * History: * * 10/28/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::GetData( STGMEDIUM *pstm, IWiaDataCallback *pIWiaDataCallback, PMINIDRV_TRANSFER_CONTEXT pmdtc) { DBG_FN(CWiaItem::GetData);
if(pstm->tymed != TYMED_FILE && pstm->tymed != TYMED_MULTIPAGE_FILE) { DBG_ERR(("Invalid tymed on storage medium passed to GetData() : %d", pstm->tymed)); return HRESULT_FROM_WIN32(E_INVALIDARG); }
//
// Allocate file for transfer. If the mini driver knows the size,
// lDataSize != 0, the file will be memory mapped.
//
HANDLE hImage; PBYTE pImage;
HRESULT hr = AllocBufferFile(pstm, pmdtc->lItemSize, &hImage, &pImage);
if (SUCCEEDED(hr)) {
//
// Fill in the mini driver transfer context.
//
if (pImage) { pmdtc->lBufferSize = pmdtc->lItemSize; pmdtc->pTransferBuffer = pImage; pmdtc->pBaseBuffer = pImage; }
pmdtc->hFile = (LONG_PTR)hImage; pmdtc->lNumBuffers = 1; pmdtc->bTransferDataCB = FALSE;
//
// Prepare the IWiaMiniDrvCallBack for status messages only.
// Mini driver can write to structure so save an interface
// ptr for release.
//
IWiaMiniDrvCallBack *pIcb;
hr = PrepCallback(pIWiaDataCallback, pmdtc, &pIcb);
if (SUCCEEDED(hr)) {
hr = SendOOBDataHeader(0, pmdtc); if (SUCCEEDED(hr)) {
//
// Call the device mini driver to accquire the device item data.
//
hr = AcquireMiniDrvItemData(pmdtc); } else { DBG_ERR(("GetData, SendOOBDataHeader failed...")); }
}
//
// Release the Mini Driver Callback, if any.
//
if (pIcb) {
//
// send the termination message
//
pIcb->MiniDrvCallback(IT_MSG_TERMINATION, IT_STATUS_TRANSFER_TO_CLIENT, 0, 0, 0, pmdtc, 0); pIcb->Release(); }
CloseBufferFile(pmdtc->tymed, pstm, pImage, hImage, hr); } else { hr = STG_E_MEDIUMFULL; } return hr; }
/**************************************************************************\
* CWiaItem::GetDataBanded * * Handles TYMED_CALLBACK specific portion of the data transfer. * * Arguments: * * lDataSize - size of image data, zero if mini driver doesn't know. * padtc - pointer to application data transfer context * pIWiaDataCallback - callback routine * pmdtc - pointer to mini driver data transfer context * * Return Value: * * Status * * History: * * 10/28/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::GetDataBanded( PWIA_DATA_TRANSFER_INFO padtc, IWiaDataCallback *pIWiaDataCallback, PMINIDRV_TRANSFER_CONTEXT pmdtc) { DBG_FN(::GetDataBanded);
HRESULT hr = E_FAIL;
//
// A callback must be supplied.
//
if (!pIWiaDataCallback) { DBG_ERR(("GetDataBanded, NULL input pointers")); return E_INVALIDARG; }
//
// allocate transfer buffer
//
hr = idtAllocateTransferBuffer(padtc);
if (hr != S_OK) { DBG_ERR(("GetDataBanded, idtAllocateTransferBuffer failed")); return hr; }
//
// Fill in the mini driver transfer context.
//
pmdtc->lBufferSize = m_lBandBufferLength; pmdtc->lNumBuffers = padtc->ulReserved3; pmdtc->pBaseBuffer = m_pBandBuffer; pmdtc->pTransferBuffer = m_pBandBuffer; //
// 64bit fix. XP client code:
// pmdtc->lClientAddress = padtc->ulReserved1;
//
// We set this to NULL to force COM marshalling.
// That way we avoid using the shared memory window we
// set up. It is the shared memory window that
// messes things up for us, since only a 32bit value
// was being used to store the shared buffer pointer
// in the Client's address space.
//
pmdtc->lClientAddress = NULL; pmdtc->bTransferDataCB = TRUE;
//
// Setup the mini driver callback. Mini driver can write to
// structure so save an interface ptr for release.
//
IWiaMiniDrvCallBack *pIcb;
hr = PrepCallback(pIWiaDataCallback, pmdtc, &pIcb);
if (hr == S_OK) {
//
// transfer data header to client
//
hr = SendDataHeader(pmdtc->lItemSize, pmdtc);
//
// data transfer may have been canceled by client
//
if (hr == S_OK) {
//
// Call the device mini driver to accquire the device item data.
//
hr = AcquireMiniDrvItemData(pmdtc); }
//
// terminate data transfer even if transfer is cancelled
//
pIcb->MiniDrvCallback(IT_MSG_TERMINATION, IT_STATUS_TRANSFER_TO_CLIENT, 0, 0, 0, pmdtc, 0); //
// Release the call back.
//
pIcb->Release(); } else { DBG_ERR(("CWiaItem::GetDataBanded, PrepCallback failed")); }
//
// free mapped transfer buffer
//
idtFreeTransferBufferEx(); return hr; }
/**************************************************************************\
* CWiaItem::CommonGetData * * Helper function used by both idtGetData and idtGetBandedData. * * Arguments: * * pstm - data storage * padtc - pointer to application data transfer context * pIWiaDataCallback - optional callback routine * * Return Value: * * Status * * History: * * 10/28/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::CommonGetData( STGMEDIUM *pstm, PWIA_DATA_TRANSFER_INFO padtc, IWiaDataCallback *pIWiaDataCallback) { DBG_FN(CWiaItem::CommonGetData);
//
// Corresponding driver item must be valid to talk with hardware.
//
HRESULT hr = ValidateWiaDrvItemAccess(m_pWiaDrvItem); if (FAILED(hr)) { return hr; }
//
// Data transfers are only allowed on items that are type Transfer.
// Fix: For now, look for either file or transfer.
//
LONG lFlags = 0;
GetItemType(&lFlags); if (!((lFlags & WiaItemTypeTransfer) || (lFlags & WiaItemTypeFile))) { DBG_ERR(("CWiaItem::CommonGetData, Item is not of File type")); return E_INVALIDARG; }
//
// Setup the minidriver transfer context. Fill in transfer context
// members which derive from item properties.
//
MINIDRV_TRANSFER_CONTEXT mdtc;
hr = InitMiniDrvContext(this, &mdtc); if (FAILED(hr)) { return hr; }
//
// Verify the device supports the requested format/media type.
//
WIA_FORMAT_INFO wfi;
wfi.lTymed = mdtc.tymed; wfi.guidFormatID = mdtc.guidFormatID;
hr = idtQueryGetData(&wfi); if (hr != S_OK) { DBG_ERR(("CWiaItem::CommonGetData, idtQueryGetData failed, format not supported")); return hr; }
//
// lock device
//
if(SUCCEEDED(hr)) {
LOCK_WIA_DEVICE _LWD(this, &hr);
if(SUCCEEDED(hr)) {
//
// Call the device mini driver to set the device item properties
// to the device some device may update mini driver context.
//
hr = SetMiniDrvItemProperties(&mdtc);
if (SUCCEEDED(hr)) {
if (pstm) {
//
// Do a file based transfer.
//
hr = GetData(pstm, pIWiaDataCallback, &mdtc); } else {
//
// Do a callback based transfer.
//
hr = GetDataBanded(padtc, pIWiaDataCallback, &mdtc);
} }
} }
return hr; }
/**************************************************************************\
* CWiaItem::SendDataHeader * * call client with total transfer size * * Arguments: * * pmdtc - destination information * * Return Value: * * Status * * History: * * 11/6/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::SendDataHeader( LONG lDataSize, MINIDRV_TRANSFER_CONTEXT *pmdtc) { DBG_FN(CWiaItem::SendDataHeader); HRESULT hr = S_OK; BYTE *pSavedPtr;
ASSERT(pmdtc != NULL); ASSERT(pmdtc->tymed == TYMED_CALLBACK || pmdtc->tymed == TYMED_MULTIPAGE_CALLBACK); ASSERT(pmdtc->pIWiaMiniDrvCallBack != NULL);
//
// All formats must first send a WIA_DATA_CALLBACK_HEADER
//
WIA_DATA_CALLBACK_HEADER wiaHeader;
wiaHeader.lSize = sizeof(WIA_DATA_CALLBACK_HEADER); wiaHeader.guidFormatID = pmdtc->guidFormatID; wiaHeader.lBufferSize = lDataSize; wiaHeader.lPageCount = 0;
pSavedPtr = pmdtc->pTransferBuffer; pmdtc->pTransferBuffer = (BYTE *) &wiaHeader;
//
// note: the data transfer cbOffset element is not changed by
// sending the data transfer header (pcbWritten not changed)
//
hr = pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback(IT_MSG_DATA_HEADER, IT_STATUS_TRANSFER_TO_CLIENT, 0, 0, wiaHeader.lSize, pmdtc, 0); pmdtc->pTransferBuffer = pSavedPtr; return hr; }
HRESULT _stdcall CWiaItem::SendOOBDataHeader( LONG lDataSize, MINIDRV_TRANSFER_CONTEXT *pmdtc) { DBG_FN(CWiaItem::SendOOBDataHeader); HRESULT hr = S_OK;
ASSERT(pmdtc != NULL); ASSERT(pmdtc->tymed == TYMED_FILE || pmdtc->tymed == TYMED_MULTIPAGE_FILE);
if (pmdtc->pIWiaMiniDrvCallBack == NULL) { return S_OK; }
//
// All formats must first send a WIA_DATA_CALLBACK_HEADER
//
WIA_DATA_CALLBACK_HEADER wiaHeader;
wiaHeader.lSize = sizeof(WIA_DATA_CALLBACK_HEADER); wiaHeader.guidFormatID = pmdtc->guidFormatID; wiaHeader.lBufferSize = lDataSize; wiaHeader.lPageCount = 0;
pmdtc->pBaseBuffer = (BYTE*)&wiaHeader;
//
// note: the data transfer cbOffset element is not changed by
// sending the data transfer header (pcbWritten not changed)
//
hr = pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback(IT_MSG_FILE_PREVIEW_DATA_HEADER, IT_STATUS_TRANSFER_TO_CLIENT, 0, 0, wiaHeader.lSize, pmdtc, 0); if(FAILED(hr)) { DBG_ERR(("CWiaItem::SendOOBDataHeader failed with %x", hr)); } return hr; }
/**************************************************************************\
* CWiaItem::SendEndOfPage * * Call client with total page count. * * Arguments: * * lPageCount - Zero based count of total pages. * pmdtc - Pointer to mini driver transfer context. * * Return Value: * * Status * * History: * * 11/6/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::SendEndOfPage( LONG lPageCount, MINIDRV_TRANSFER_CONTEXT *pmdtc) { DBG_FN(CWiaItem::SendEndOfPage); HRESULT hr = S_OK; PBYTE pSavedPtr;
ASSERT(pmdtc != NULL); ASSERT(pmdtc->pIWiaMiniDrvCallBack != NULL);
//
// Set up the header for page count.
//
WIA_DATA_CALLBACK_HEADER wiaHeader;
wiaHeader.lSize = sizeof(WIA_DATA_CALLBACK_HEADER); wiaHeader.guidFormatID = pmdtc->guidFormatID; wiaHeader.lBufferSize = 0; wiaHeader.lPageCount = lPageCount;
pSavedPtr = pmdtc->pTransferBuffer; pmdtc->pTransferBuffer = (BYTE *) &wiaHeader; //
// note: the data transfer cbOffset element is not changed by
// sending the data transfer header (pcbWritten not changed)
//
hr = pmdtc->pIWiaMiniDrvCallBack->MiniDrvCallback(IT_MSG_NEW_PAGE, IT_STATUS_TRANSFER_TO_CLIENT, 0, 0, wiaHeader.lSize, pmdtc, 0);
pmdtc->pTransferBuffer = pSavedPtr; return hr; }
/**************************************************************************\
* CWiaItem::AcquireMiniDrvItemData * * Call mini driver to capture item data. * * Arguments: * * pmdtc - transfer data context * * Return Value: * * Status * * History: * * 11/17/1998 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::AcquireMiniDrvItemData( PMINIDRV_TRANSFER_CONTEXT pmdtc) { DBG_FN(CWiaItem::AcquireMiniDrvItemData);
//
// Set flag to indicate if class driver allocated the
// data transfer buffer or not.
//
if (pmdtc->pTransferBuffer) { pmdtc->bClassDrvAllocBuf = TRUE; } else { pmdtc->bClassDrvAllocBuf = FALSE; }
HRESULT hr = S_OK;
LONG lFlags = 0;
InterlockedIncrement(&g_NumberOfActiveTransfers);
hr = m_pActiveDevice->m_DrvWrapper.WIA_drvAcquireItemData((BYTE*)this, lFlags, pmdtc, &m_lLastDevErrVal); InterlockedDecrement(&g_NumberOfActiveTransfers);
return hr; }
/*******************************************************************************
* * SetMiniDrvItemProperties * * DESCRIPTION: * Call the device mini driver to set the device item properties to the device. * * PARAMETERS: * *******************************************************************************/
HRESULT _stdcall CWiaItem::SetMiniDrvItemProperties( PMINIDRV_TRANSFER_CONTEXT pmdtc) { DBG_FN(CWiaItem::SetMiniDrvItemProperties); HRESULT hr = S_OK; LONG lFlags = 0;
InterlockedIncrement(&g_NumberOfActiveTransfers);
hr = m_pActiveDevice->m_DrvWrapper.WIA_drvWriteItemProperties((BYTE*)this, lFlags, pmdtc, &m_lLastDevErrVal); InterlockedDecrement(&g_NumberOfActiveTransfers);
return hr; }
/**************************************************************************\
* CWiaItem::SetCallbackBufferInfo * * * Arguments: * * * Return Value: * * Status * * History: * * 07/21/2000 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::SetCallbackBufferInfo(WIA_DATA_CB_BUF_INFO DataCBBufInfo) { //
// Store the data callback information
//
m_dcbInfo.ulSize = DataCBBufInfo.ulSize; m_dcbInfo.pMappingHandle = DataCBBufInfo.pMappingHandle; m_dcbInfo.pTransferBuffer = DataCBBufInfo.pTransferBuffer; m_dcbInfo.ulBufferSize = DataCBBufInfo.ulBufferSize; m_dcbInfo.ulClientProcessId = DataCBBufInfo.ulClientProcessId;
return S_OK; }
/**************************************************************************\
* CWiaItem::SetCallbackBufferInfo * * * Arguments: * * * Return Value: * * Status * * History: * * 07/21/2000 Original Version * \**************************************************************************/
HRESULT _stdcall CWiaItem::GetCallbackBufferInfo(WIA_DATA_CB_BUF_INFO *pDataCBBufInfo) { //
// Return the data callback information
//
if (IsBadWritePtr(pDataCBBufInfo, sizeof(WIA_DATA_CB_BUF_INFO))) { DBG_ERR(("CWiaItem::GetCallbackBufferInfo, parameter is a bad write pointer")); return E_INVALIDARG; }
pDataCBBufInfo->ulSize = m_dcbInfo.ulSize; pDataCBBufInfo->pMappingHandle = m_dcbInfo.pMappingHandle; pDataCBBufInfo->pTransferBuffer = m_dcbInfo.pTransferBuffer; pDataCBBufInfo->ulBufferSize = m_dcbInfo.ulBufferSize; pDataCBBufInfo->ulClientProcessId = m_dcbInfo.ulClientProcessId;
return S_OK; }
class CWiaRemoteTransfer : public IWiaMiniDrvCallBack { ULONG m_cRef;
LONG m_lMessage; LONG m_lStatus; LONG m_lPercentComplete; LONG m_lOffset; LONG m_lTransferOffset; LONG m_lLength; BYTE *m_pBuffer;
BOOL m_bTransferCancelled; BOOL m_bDeviceLocked; HRESULT m_savedHr; HANDLE m_hThread; HANDLE m_hMessagePickedUp; HANDLE m_hMessageAvailable; LONG m_MessageWaitTimeout;
public: STGMEDIUM m_Medium; WCHAR m_szTransferFile[MAX_PATH]; MINIDRV_TRANSFER_CONTEXT m_mdtc; CWiaItem *m_pWiaItem;
CWiaRemoteTransfer() : m_cRef(1), m_pBuffer(0), m_bTransferCancelled(FALSE), m_bDeviceLocked(FALSE), m_hThread(0), m_savedHr(S_OK), m_hMessagePickedUp(NULL), m_hMessageAvailable(NULL), m_MessageWaitTimeout(30000L) { ZeroMemory(&m_mdtc, sizeof(m_mdtc)); }
~CWiaRemoteTransfer();
// IWiaMiniDrvCallback messages
HRESULT __stdcall QueryInterface(REFIID riid, LPVOID * ppv); ULONG __stdcall AddRef() { return InterlockedIncrement((LPLONG)&m_cRef); }
ULONG __stdcall Release();
HRESULT __stdcall MiniDrvCallback( LONG lReason, LONG lStatus, LONG lPercentComplete, LONG lOffset, LONG lLength, PMINIDRV_TRANSFER_CONTEXT pmdtc, LONG lReserved);
HRESULT Init(CWiaItem *pItem, LPSTGMEDIUM pMedium); HRESULT ThreadInit();
HRESULT GetWiaMessage( ULONG nNumberOfBytesToRead, ULONG *pNumberOfBytesRead, BYTE *pBuffer, LONG *pOffset, LONG *pMessage, LONG *pStatus, LONG *pPercentComplete);
void SaveHR(HRESULT hr) { m_savedHr = hr; }
void CancelTransfer() { m_bTransferCancelled = TRUE; } };
CWiaRemoteTransfer::~CWiaRemoteTransfer() { if(m_hMessagePickedUp) { SetEvent(m_hMessagePickedUp); } if(m_hThread) { WaitForSingleObject(m_hThread, INFINITE); CloseHandle(m_hThread); }
if(m_hMessagePickedUp) { CloseHandle(m_hMessagePickedUp); m_hMessagePickedUp = NULL; }
if(m_hMessageAvailable) { CloseHandle(m_hMessageAvailable); m_hMessageAvailable = NULL; } HANDLE hFile = (HANDLE) m_mdtc.hFile;
if(hFile != INVALID_HANDLE_VALUE && hFile != NULL) { if(m_mdtc.pTransferBuffer) { UnmapViewOfFile(m_mdtc.pTransferBuffer); } CloseHandle(hFile); m_mdtc.hFile = NULL; }
//
// If this was a Callback transfer and we allocated the buffer, we should
// free it now.
//
if((m_mdtc.bTransferDataCB == TRUE) && (m_mdtc.bClassDrvAllocBuf == TRUE)) { if (m_mdtc.pBaseBuffer) { LocalFree(m_mdtc.pBaseBuffer); m_mdtc.pBaseBuffer = NULL; } } if(m_bDeviceLocked) { UnLockWiaDevice(m_pWiaItem); } }
HRESULT __stdcall CWiaRemoteTransfer::QueryInterface(REFIID riid, LPVOID * ppv) { *ppv = NULL;
if (riid == IID_IUnknown || riid == IID_IWiaMiniDrvCallBack) { *ppv = (IWiaMiniDrvCallBack*) this; } else { return E_NOINTERFACE; }
AddRef(); return S_OK; }
ULONG __stdcall CWiaRemoteTransfer::Release() { ULONG lresult;
lresult = InterlockedDecrement((LPLONG)&m_cRef); if(lresult == 0) { delete this; } return lresult; }
void _stdcall CleanupRemoteTransfer(CWiaRemoteTransfer *p) {
p->CancelTransfer(); delete p; }
DWORD WINAPI RemoteTransferDriverThread(LPVOID param) { HRESULT hr; CWiaRemoteTransfer *pTransferObject = (CWiaRemoteTransfer *)param; WIA_DATA_CALLBACK_HEADER Header; LONG lFlags = 0; LONG lDevErrVal = 0; BYTE *pSavedPointer;
hr = CoInitializeEx(0,COINIT_MULTITHREADED);
if (FAILED(hr)) { pTransferObject->SaveHR(hr); DBG_ERR(("Thread callback, CoInitializeEx failed (0x%X)", hr)); goto Cleanup; }
hr = pTransferObject->ThreadInit(); if(FAILED(hr)) { pTransferObject->SaveHR(hr); DBG_ERR(("Thread callback, ThreadInit() failed (0x%X)", hr)); goto Cleanup; }
//
// For callback transfers, fill out WIA_DATA_CALLBACK_HEADER
// structure in the transfer buffer
// structure in the transfer buffer.
// For file transfers, send the OOB data header. CLients that
// dont understand OOB data will ignore it.
//
if(pTransferObject->m_mdtc.bTransferDataCB) {
Header.lSize = sizeof(Header); Header.guidFormatID = pTransferObject->m_mdtc.guidFormatID; Header.lBufferSize = pTransferObject->m_mdtc.lItemSize; Header.lPageCount = 0;
//
// Save transfer buffer pointer and prepare to transfer our header
//
pSavedPointer = pTransferObject->m_mdtc.pTransferBuffer; pTransferObject->m_mdtc.pTransferBuffer = (BYTE *)&Header;
//
// Let client app to pick up IT_MSG_DATA_HEADER
//
hr = pTransferObject->MiniDrvCallback(IT_MSG_DATA_HEADER, IT_STATUS_TRANSFER_TO_CLIENT, 0, 0, Header.lSize, &pTransferObject->m_mdtc, 0); //
// Restore transfer buffer pointer
//
pTransferObject->m_mdtc.pTransferBuffer = pSavedPointer;
if(hr != S_OK) { pTransferObject->SaveHR(hr); DBG_ERR(("CWiaRemoteTransfer::Start() MinDrvCallback failed (0x%X)", hr)); goto Cleanup; } } else { Header.lSize = sizeof(Header); Header.guidFormatID = pTransferObject->m_mdtc.guidFormatID; Header.lBufferSize = 0; // Force clients to allocate as needed since we don't know the size
Header.lPageCount = 0;
//
// Save transfer buffer pointer and prepare to transfer our header
//
pSavedPointer = pTransferObject->m_mdtc.pTransferBuffer; pTransferObject->m_mdtc.pTransferBuffer = (BYTE *)&Header;
//
// Let client app to pick up IT_MSG_FILE_PREVIEW_DATA_HEADER
//
hr = pTransferObject->MiniDrvCallback(IT_MSG_FILE_PREVIEW_DATA_HEADER, IT_STATUS_TRANSFER_TO_CLIENT, 0, 0, Header.lSize, &pTransferObject->m_mdtc, 0); //
// Restore transfer buffer pointer
//
pTransferObject->m_mdtc.pTransferBuffer = pSavedPointer;
if(hr != S_OK) { pTransferObject->SaveHR(hr); DBG_ERR(("CWiaRemoteTransfer::Start() MinDrvCallback failed (0x%X)", hr)); goto Cleanup; } }
InterlockedIncrement(&g_NumberOfActiveTransfers);
// Call into mini-driver and don't return until transfer is complete
hr = pTransferObject->m_pWiaItem->m_pActiveDevice->m_DrvWrapper.WIA_drvAcquireItemData( (BYTE *) pTransferObject->m_pWiaItem, lFlags, &pTransferObject->m_mdtc, &lDevErrVal); InterlockedDecrement(&g_NumberOfActiveTransfers);
if(FAILED(hr)) { pTransferObject->SaveHR(hr); DBG_ERR(("RemoteTransferDriverThread, drvAcquireItemData failed (Minidriver Error %d)", lDevErrVal)); }
Cleanup: //
// Make sure IT_MSG_TERMINATION is send in any case
//
hr = pTransferObject->MiniDrvCallback(IT_MSG_TERMINATION, IT_STATUS_TRANSFER_TO_CLIENT, 0, 0, 0, &pTransferObject->m_mdtc, 0); if(hr != S_OK) { DBG_ERR(("RemoteTransferDriverThread MinDrvCallback failed (0x%X)", hr)); }
CoUninitialize(); return 0; }
HRESULT CWiaRemoteTransfer::MiniDrvCallback( LONG lReason, LONG lStatus, LONG lPercentComplete, LONG lOffset, LONG lLength, PMINIDRV_TRANSFER_CONTEXT pmdtc, LONG lReserved) { HRESULT hr = S_OK;
//
// Copy message content
//
m_lMessage = lReason; m_lStatus = lStatus; m_lPercentComplete = lPercentComplete; m_lOffset = lOffset; m_lTransferOffset = 0; m_lLength = lLength;
// Make sure we reference the correct buffer. Out-of-band-data (or "File Preview" data)
// comes from the MiniDriver Transfer Context's pBaseBuffer pointer, while all other
// data comes from its pTransferBuffer.
if (pmdtc) { if (m_lMessage == IT_MSG_FILE_PREVIEW_DATA) { m_pBuffer = pmdtc->pBaseBuffer; } else { m_pBuffer = pmdtc->pTransferBuffer; } } else { m_pBuffer = NULL; }
//
// Let the incoming thread from remote app to pick up the message
//
SetEvent(m_hMessageAvailable);
//
// Wait until all the data message is picked up by app
//
if(WaitForSingleObject(m_hMessagePickedUp, m_MessageWaitTimeout) != WAIT_OBJECT_0) { DBG_ERR(("CWiaRemoteTransfer::MiniDrvCallback timed out")); // avoid long waits if message timed out once
m_MessageWaitTimeout = 1; hr = S_FALSE; } ResetEvent(m_hMessagePickedUp);
if(m_bTransferCancelled) { hr = S_FALSE; } return hr; }
HRESULT CWiaRemoteTransfer::Init(CWiaItem *pItem, LPSTGMEDIUM pMedium) { HRESULT hr = S_OK; DWORD dwThread; m_pWiaItem = pItem; m_Medium = *pMedium; m_MessageWaitTimeout = 30000L; if(pMedium->tymed == TYMED_FILE) { m_Medium.lpszFileName = m_szTransferFile; wcsncpy(m_szTransferFile, pMedium->lpszFileName, MAX_PATH - 1); m_szTransferFile[MAX_PATH - 1] = 0; }
//
// Create events. No messages are posted yet.
//
m_hMessageAvailable = CreateEvent(NULL, TRUE, FALSE, NULL); m_hMessagePickedUp = CreateEvent(NULL, TRUE, FALSE, NULL);
if(!m_hMessageAvailable || !m_hMessagePickedUp) { LONG lRet = GetLastError(); hr = HRESULT_FROM_WIN32(lRet); DBG_ERR(("CWiaRemoteTransfer::Init, CreateEvent failed, GetLastError() = 0x%X", lRet)); goto Cleanup; }
//
// Create driver thread
//
m_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) RemoteTransferDriverThread, (LPVOID) this, 0, &dwThread); if(!m_hThread) { LONG lRet = GetLastError(); hr = HRESULT_FROM_WIN32(lRet); DBG_ERR(("CWiaRemoteTransfer::Init, CreateThread failed, GetLastError() = 0x%X", lRet)); goto Cleanup; }
Cleanup: return hr; }
HRESULT CWiaRemoteTransfer::ThreadInit() { HRESULT hr = S_OK; WIA_EXTENDED_TRANSFER_INFO exti = { 0 }; DWORD dwWidthInBytes;
if(m_Medium.tymed == TYMED_FILE) { m_mdtc.lBufferSize = 0;
HANDLE hImage; PBYTE pImage;
hr = AllocBufferFile(&m_Medium, m_mdtc.lItemSize, &hImage, &pImage);
if (FAILED(hr)) { DBG_ERR(("CWiaRemoteTransfer::ThreadInit, AllocateBufferFile failed, hr = 0x%X", hr)); goto Cleanup; }
//
// Fill in the mini driver transfer context.
//
if (pImage) { m_mdtc.lBufferSize = m_mdtc.lItemSize; m_mdtc.pTransferBuffer = pImage; m_mdtc.pBaseBuffer = pImage; m_mdtc.bClassDrvAllocBuf = TRUE; } else { m_mdtc.bClassDrvAllocBuf = FALSE; }
m_mdtc.hFile = (LONG_PTR)hImage; m_mdtc.lNumBuffers = 1; m_mdtc.bTransferDataCB = FALSE;
} else { // callback case
hr = GetBufferValues(m_pWiaItem, &exti); if(FAILED(hr)) { DBG_ERR(("CWiaRemoteTransfer::ThreadInit, GetBufferValues() failed, hr = %X", hr)); goto Cleanup; }
m_mdtc.lBufferSize = exti.ulMinBufferSize; m_mdtc.lNumBuffers = 1;
if(m_mdtc.lBufferSize) { m_mdtc.pBaseBuffer = (BYTE *) LocalAlloc(LPTR, m_mdtc.lBufferSize); if(m_mdtc.pBaseBuffer == NULL) { hr = E_OUTOFMEMORY; DBG_ERR(("CWiaRemoteTransfer::ThreadInit, failed to allocate %ld bytes", m_mdtc.lBufferSize)); goto Cleanup; } m_mdtc.pTransferBuffer = m_mdtc.pBaseBuffer; m_mdtc.bClassDrvAllocBuf = TRUE; } else { m_mdtc.bClassDrvAllocBuf = FALSE; } m_mdtc.lClientAddress = NULL; m_mdtc.bTransferDataCB = TRUE; }
hr = QueryInterface(IID_IWiaMiniDrvCallBack, (void **)&m_mdtc.pIWiaMiniDrvCallBack); if(hr != S_OK) { DBG_ERR(("CWiaRemoteTransfer::ThreadInit this::QI(IWiaMiniDrvCallback) failed (0x%X)", hr)); goto Cleanup; }
hr = LockWiaDevice(m_pWiaItem); if(FAILED(hr)) { DBG_ERR(("CWiaRemoteTransfer::ThreadInit LockWiaDevice failed (0x%X)", hr)); goto Cleanup; }
m_bDeviceLocked = TRUE;
//
// Call the device mini driver to set the device item properties
// to the device some other device may update mini driver context.
//
hr = m_pWiaItem->SetMiniDrvItemProperties(&m_mdtc); if(FAILED(hr)) { DBG_ERR(("CWiaRemoteTransfer::ThreadInit, SetMiniDrvItemProperties failed (0x%X)", hr)); goto Cleanup; }
Cleanup: return hr; }
HRESULT CWiaRemoteTransfer::GetWiaMessage( ULONG nNumberOfBytesToRead, ULONG *pNumberOfBytesRead, BYTE *pBuffer, LONG *pOffset, LONG *pMessage, LONG *pStatus, LONG *pPercentComplete) { HRESULT hr = S_OK;
//
// Wait until data is available
//
if(WaitForSingleObject(m_hMessageAvailable, INFINITE) != WAIT_OBJECT_0) { DBG_ERR(("CWiaRemoteTransfer::GetMessage() timed out")); hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; }
//
// Copy message data to client space
//
*pMessage = m_lMessage; *pStatus = m_lStatus; *pPercentComplete = m_lPercentComplete; *pOffset = m_lOffset + m_lTransferOffset;
//
// Copy data bytes
//
*pNumberOfBytesRead = min(nNumberOfBytesToRead, (ULONG)m_lLength); if(*pNumberOfBytesRead) {
// adjust image size, if necessary
if(m_mdtc.lImageSize) { *pPercentComplete = MulDiv(*pOffset + *pNumberOfBytesRead, 100, m_mdtc.lImageSize); } if(m_pBuffer && pBuffer) { __try { memcpy(pBuffer, m_pBuffer + m_lTransferOffset, *pNumberOfBytesRead); m_lLength -= *pNumberOfBytesRead; m_lTransferOffset += *pNumberOfBytesRead; } __except(EXCEPTION_EXECUTE_HANDLER) { DBG_ERR(("CWiaRemoteTransfer::GetMessage caught exception copying data")); } } }
//
// If this was a data message and some data still left,
// we need to keep entry open and don't release driver
//
if(m_lLength != 0 && (m_lMessage == IT_MSG_DATA || m_lMessage == IT_MSG_FILE_PREVIEW_DATA)) {
//
// keep hMessageAvailable set and hMessagePickedUp reset
// so repeat calls from app would work and
// the driver thread would be blocked
//
} else {
//
// It was not a data message or all data was consumed --
// prevent reentry and release the driver thread
//
ResetEvent(m_hMessageAvailable); SetEvent(m_hMessagePickedUp);
}
hr = m_savedHr;
Cleanup: return hr; }
/**************************************************************************\
* CWiaItem::idtStartRemoteDataTransfer * * * * Arguments: * * * * Return Value: * * Status: * * History: * * * \**************************************************************************/ HRESULT _stdcall CWiaItem::idtStartRemoteDataTransfer(LPSTGMEDIUM pMedium) { HRESULT hr = S_OK; LONG lFlags = 0; LONG lDevErrVal = 0;
//
// Prepare remote transfer object
//
CWiaRemoteTransfer *pRemoteTransfer = new CWiaRemoteTransfer(); if(!pRemoteTransfer) { hr = E_OUTOFMEMORY; DBG_ERR(("CWiaItem::idtStartRemoteDataTransfer, new CWiaRemoteTransfer() failed")); goto Cleanup; }
//
// Acquire remote transfer lock
//
if(InterlockedCompareExchangePointer((PVOID *)&m_pRemoteTransfer, pRemoteTransfer, NULL) != NULL) { hr = E_FAIL; DBG_ERR(("CWiaItem::idtStartRemoteDataTransfer, transfer already in progress")); goto Cleanup; }
//
// Check whether item properties have been initialized
//
if(!m_bInitialized) { hr = InitLazyProps(); if(hr != S_OK) { DBG_ERR(("CWiaItem::idtStartRemoteDataTransfer, InitLazyProps() failed (0x%X)", hr)); goto Cleanup; } }
//
// Corresponding driver item must be valid to talk with hardware.
//
hr = ValidateWiaDrvItemAccess(m_pWiaDrvItem); if (hr != S_OK) { DBG_ERR(("CWiaItem::idtStartRemoteDataTransfer, ValidateWiaDrvItemAccess() failed (0x%X)", hr)); goto Cleanup; }
//
// Data transfers are only allowed on items that are type Transfer.
// Fix: For now, look for either file or transfer.
//
GetItemType(&lFlags); if (!(lFlags & WiaItemTypeTransfer) && !(lFlags & WiaItemTypeFile)) { hr = E_INVALIDARG; DBG_ERR(("CWiaItem::idtStartRemoteDataTransfer, Item is not of File type")); goto Cleanup; }
//
// Setup the minidriver transfer context. Fill in transfer context
// members which derive from item properties.
//
hr = InitMiniDrvContext(this, &pRemoteTransfer->m_mdtc); if (FAILED(hr)) { DBG_ERR(("CWiaItem::idtStartRemoteDataTransfer, InitMiniDrvContext() failed (0x%X)", hr)); goto Cleanup; }
//
// Verify the device supports the requested format/media type.
//
WIA_FORMAT_INFO wfi;
wfi.lTymed = pRemoteTransfer->m_mdtc.tymed; wfi.guidFormatID = pRemoteTransfer->m_mdtc.guidFormatID;
hr = idtQueryGetData(&wfi); if (hr != S_OK) { DBG_ERR(("CWiaItem::idtStartRemoteDataTransfer, idtQueryGetData failed, format not supported (0x%X)", hr)); goto Cleanup; }
//
// Initiate transfer, wait for it to start or fail
//
hr = m_pRemoteTransfer->Init(this, pMedium); if(hr != S_OK) { DBG_ERR(("CWiaItem::idtStartRemoteDataTransfer, CWiaRemoteTransfer::Start() failed (0x%X)", hr)); goto Cleanup; }
Cleanup:
if(hr != S_OK && pRemoteTransfer) {
//
// If we managed to acquire transfer lock, release it
//
InterlockedCompareExchangePointer((PVOID *)&m_pRemoteTransfer, NULL, pRemoteTransfer); delete pRemoteTransfer;
}
return hr; }
/**************************************************************************\
* CWiaItem::idtStopRemoteDataTransfer * * * * Arguments: * * * * Return Value: * * Status: * * History: * * * \**************************************************************************/ HRESULT _stdcall CWiaItem::idtStopRemoteDataTransfer() { HRESULT hr = S_OK; //
// we are garanteed to have device lock at this point
// (we are here only if idtStartRemoteTransfer succeded)
//
CWiaRemoteTransfer *pTransfer = (CWiaRemoteTransfer *)InterlockedExchangePointer((PVOID *)&m_pRemoteTransfer, NULL); delete pTransfer;
return hr; }
HRESULT _stdcall CWiaItem::idtCancelRemoteDataTransfer() { HRESULT hr = S_OK;
m_pRemoteTransfer->CancelTransfer();
return hr; }
/**************************************************************************\
* CWiaItem::idtRemoteDataTransfer * * * * Arguments: * * * * Return Value: * * Status: * * History: * * * \**************************************************************************/ HRESULT _stdcall CWiaItem::idtRemoteDataTransfer( ULONG nNumberOfBytesToRead, ULONG *pNumberOfBytesRead, BYTE *pBuffer, LONG *pOffset, LONG *pMessage, LONG *pStatus, LONG *pPercentComplete) { //
// Let the remote transfer object wait until the message is ready and
// return the message
//
return m_pRemoteTransfer->GetWiaMessage(nNumberOfBytesToRead, pNumberOfBytesRead, pBuffer, pOffset, pMessage, pStatus, pPercentComplete); }
|