/******************************************************************************* * * (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); }