You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2945 lines
81 KiB
2945 lines
81 KiB
/*******************************************************************************
|
|
*
|
|
* (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);
|
|
}
|
|
|
|
|