/******************************************************************************* * * (C) COPYRIGHT MICROSOFT CORP., 1998 * * TITLE: Device.Cpp * * VERSION: 2.0 * * AUTHOR: ReedB * * DATE: 5 Jan, 1999 * * DESCRIPTION: * Implementation of the WIA test scanner device methods. This sample WIA USD * supports push events by detecting when %windir%\temp\TESTUSD.BMP file has * been modified. This file becomes the new source of scanning data. An * event is generated the first time the device is loaded. * *******************************************************************************/ #include #include #include "testusd.h" #include "resource.h" #include "tcamprop.h" extern HINSTANCE g_hInst; // // Function prototypes, implemented in this file: // VOID FileChangeThread(LPVOID lpParameter); /**************************************************************************\ * TestUsdDevice::TestUsdDevice * * Device class constructor * * Arguments: * * None * * Return Value: * * None * * History: * * 9/11/1998 Original Version * \**************************************************************************/ TestUsdDevice::TestUsdDevice(LPUNKNOWN punkOuter): m_cRef(1), m_punkOuter(NULL), m_fValid(FALSE), m_pIStiDevControl(NULL), m_hShutdownEvent(INVALID_HANDLE_VALUE), m_hSignalEvent(INVALID_HANDLE_VALUE), m_hEventNotifyThread(NULL), m_guidLastEvent(GUID_NULL), m_pIWiaEventCallback(NULL), m_pStiDevice(NULL), m_bstrDeviceID(NULL), m_bstrRootFullItemName(NULL), m_pIDrvItemRoot(NULL) { WIAS_TRACE((g_hInst,"TestUsdDevice::TestUsdDevice")); *m_szSrcDataName = L'\0'; // // See if we are aggregated. If we are (almost always the case) save // pointer to the controlling Unknown , so subsequent calls will be // delegated. If not, set the same pointer to "this". // if (punkOuter) { m_punkOuter = punkOuter; } else { // // Cast below is needed in order to point to right virtual table // m_punkOuter = reinterpret_cast (static_cast (this)); } // // init camera search path // LPTSTR lpwszEnvString = TEXT("%CAMERA_ROOT%"); DWORD dwRet = ExpandEnvironmentStrings(lpwszEnvString, gpszPath, MAX_PATH); if ((dwRet == 0) || (dwRet == (ULONG)_tcslen(lpwszEnvString)+1)) { _tcscpy(gpszPath, TEXT("C:\\Image")); } } /**************************************************************************\ * TestUsdDevice::PrivateInitialize * * Device class private initialization * * Arguments: * * None * * Return Value: * * None * \**************************************************************************/ HRESULT TestUsdDevice::PrivateInitialize() { HRESULT hr = S_OK; __try { if(!InitializeCriticalSectionAndSpinCount(&m_csShutdown, MINLONG)) { hr = HRESULT_FROM_WIN32(::GetLastError()); WIAS_ERROR((g_hInst,"TestUsdDevice::PrivateInitialize init CritSect failed")); } } __except(EXCEPTION_EXECUTE_HANDLER) { hr = E_OUTOFMEMORY; } if(hr == S_OK) { // // Create event for syncronization of notifications shutdown. // m_hShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hShutdownEvent && (INVALID_HANDLE_VALUE != m_hShutdownEvent)) { m_fValid = TRUE; } else { hr = HRESULT_FROM_WIN32(::GetLastError()); WIAS_ERROR((g_hInst,"TestUsdDevice::PrivateInitialize, create shutdown event failed")); } } return hr; } /**************************************************************************\ * TestUsdDevice::~TestUsdDevice * * Device class destructor * * Arguments: * * None * * Return Value: * * None * * History: * * 9/11/1998 Original Version * \**************************************************************************/ TestUsdDevice::~TestUsdDevice(void) { WIAS_TRACE((g_hInst,"TestUsdDevice::~TestUsdDevice")); // // Kill notification thread if it exists. // SetNotificationHandle(NULL); // // Close event for syncronization of notifications shutdown. // if (m_hShutdownEvent && (m_hShutdownEvent != INVALID_HANDLE_VALUE)) { CloseHandle(m_hShutdownEvent); } // // Release the device control interface. // if (m_pIStiDevControl) { m_pIStiDevControl->Release(); m_pIStiDevControl = NULL; } // // WIA member destruction // // Cleanup the WIA event sink. // if (m_pIWiaEventCallback) { m_pIWiaEventCallback->Release(); } // // Free the storage for the device ID. // if (m_bstrDeviceID) { SysFreeString(m_bstrDeviceID); } // // Release the objects supporting device property storage. // if (m_bstrRootFullItemName) { SysFreeString(m_bstrRootFullItemName); } // // Free the critical section. // DeleteCriticalSection(&m_csShutdown); } /**************************************************************************\ * TestUsdDevice::GetCapabilities * * Get the device STI capabilities. * * Arguments: * * pUsdCaps - Pointer to USD capabilities data. * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::GetCapabilities(PSTI_USD_CAPS pUsdCaps) { ZeroMemory(pUsdCaps, sizeof(*pUsdCaps)); pUsdCaps->dwVersion = STI_VERSION; // // We do support device notifications, but do not requiring polling. // pUsdCaps->dwGenericCaps = STI_USD_GENCAP_NATIVE_PUSHSUPPORT; return STI_OK; } /**************************************************************************\ * TestUsdDevice::GetStatus * * Query device online and/or event status. * * Arguments: * * pDevStatus - Pointer to device status data. * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::GetStatus(PSTI_DEVICE_STATUS pDevStatus) { WIAS_TRACE((g_hInst,"TestUsdDevice::GetStatus")); // // Validate parameters. // if (!pDevStatus) { WIAS_ERROR((g_hInst,"TestUsdDevice::GetStatus, NULL parameter")); return E_INVALIDARG; } // // If we are asked, verify whether device is online. // pDevStatus->dwOnlineState = 0L; if (pDevStatus->StatusMask & STI_DEVSTATUS_ONLINE_STATE) { // // The test device is always on-line. // pDevStatus->dwOnlineState |= STI_ONLINESTATE_OPERATIONAL; } // // If we are asked, verify state of event. // pDevStatus->dwEventHandlingState &= ~STI_EVENTHANDLING_PENDING; if (pDevStatus->StatusMask & STI_DEVSTATUS_EVENTS_STATE) { // // Generate an event the first time we load. // if (m_bUsdLoadEvent) { pDevStatus->dwEventHandlingState = STI_EVENTHANDLING_PENDING; m_guidLastEvent = guidEventFirstLoaded; m_bUsdLoadEvent = FALSE; } // // event pending ??? // } return STI_OK; } /**************************************************************************\ * TestUsdDevice::DeviceReset * * Reset data file pointer to start of file. * * Arguments: * * None * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::DeviceReset(void) { WIAS_TRACE((g_hInst,"DeviceReset")); return STI_OK; } /**************************************************************************\ * TestUsdDevice::Diagnostic * * The test device always passes the diagnostic. * * Arguments: * * pBuffer - Pointer o diagnostic result data. * * Return Value: * * None * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::Diagnostic(LPSTI_DIAG pBuffer) { WIAS_TRACE((g_hInst,"TestUsdDevice::Diagnostic")); // // Initialize response buffer // pBuffer->dwStatusMask = 0; ZeroMemory(&pBuffer->sErrorInfo,sizeof(pBuffer->sErrorInfo)); pBuffer->sErrorInfo.dwGenericError = NOERROR; pBuffer->sErrorInfo.dwVendorError = 0; return STI_OK; } /**************************************************************************\ * TestUsdDevice::SetNotificationHandle * * Starts and stops the event notification thread. * * Arguments: * * hEvent - If not valid start the notification thread otherwise kill * the notification thread. * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::SetNotificationHandle(HANDLE hEvent) { WIAS_TRACE((g_hInst,"TestUsdDevice::SetNotificationHandle")); HRESULT hr = S_OK; EnterCriticalSection(&m_csShutdown); // // Are we starting or stopping the notification thread? // if (hEvent && (hEvent != INVALID_HANDLE_VALUE)) { m_hSignalEvent = hEvent; // // Initialize to no event. // m_guidLastEvent = GUID_NULL; // // Create the notification thread. // if (!m_hEventNotifyThread) { DWORD dwThread; m_hEventNotifyThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FileChangeThread, (LPVOID)this, 0, &dwThread); if (m_hEventNotifyThread) { WIAS_TRACE((g_hInst,"TestUsdDevice::SetNotificationHandle, Enabling event notification")); } else { WIAS_ERROR((g_hInst,"TestUsdDevice::SetNotificationHandle, unable to create notification thread")); hr = HRESULT_FROM_WIN32(::GetLastError()); } } else { WIAS_ERROR((g_hInst,"TestUsdDevice::SetNotificationHandle, spurious notification thread")); hr = STIERR_UNSUPPORTED; } } else { // // Disable event notifications. // SetEvent(m_hShutdownEvent); if (m_hEventNotifyThread) { WIAS_TRACE((g_hInst,"Disabling event notification")); WaitForSingleObject(m_hEventNotifyThread, 400); CloseHandle(m_hEventNotifyThread); m_hEventNotifyThread = NULL; m_guidLastEvent = GUID_NULL; // // close dlg // if (m_hDlg != NULL) { SendMessage(m_hDlg,WM_COMMAND,IDOK,0); m_hDlg = NULL; } } } LeaveCriticalSection(&m_csShutdown); return hr; } /**************************************************************************\ * TestUsdDevice::GetNotificationData * * Provides data on n event. * * Arguments: * * pBuffer - Pointer to event data. * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::GetNotificationData( LPSTINOTIFY pBuffer ) { WIAS_TRACE((g_hInst,"TestUsdDevice::GetNotificationData")); // // If we have notification ready - return it's guid // if (!IsEqualIID(m_guidLastEvent, GUID_NULL)) { pBuffer->guidNotificationCode = m_guidLastEvent; m_guidLastEvent = GUID_NULL; pBuffer->dwSize = sizeof(STINOTIFY); ZeroMemory(&pBuffer->abNotificationData, sizeof(pBuffer->abNotificationData)); // // private event // if (IsEqualIID(m_guidLastEvent, WIA_EVENT_NAME_CHANGE)) { } } else { return STIERR_NOEVENTS; } return STI_OK; } /**************************************************************************\ * TestUsdDevice::Escape * * Issue a command to the device. * * Arguments: * * EscapeFunction - Command to be issued. * pInData - Input data to be passed with command. * cbInDataSize - Size of input data. * pOutData - Output data to be passed back from command. * cbOutDataSize - Size of output data buffer. * pcbActualData - Size of output data actually written. * * Return Value: * * None * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::Escape( STI_RAW_CONTROL_CODE EscapeFunction, LPVOID pInData, DWORD cbInDataSize, LPVOID pOutData, DWORD cbOutDataSize, LPDWORD pcbActualData) { WIAS_TRACE((g_hInst,"TestUsdDevice::Escape, unsupported")); // // Write command to device if needed. // return STIERR_UNSUPPORTED; } /**************************************************************************\ * TestUsdDevice::GetLastError * * Get the last error from the device. * * Arguments: * * pdwLastDeviceError - Pointer to last error data. * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::GetLastError(LPDWORD pdwLastDeviceError) { WIAS_TRACE((g_hInst,"TestUsdDevice::GetLastError")); if (IsBadWritePtr(pdwLastDeviceError, sizeof(DWORD))) { return STIERR_INVALID_PARAM; } *pdwLastDeviceError = m_dwLastOperationError; return STI_OK; } /**************************************************************************\ * TestUsdDevice::GetLastErrorInfo * * Get extended error information from the device. * * Arguments: * * pLastErrorInfo - Pointer to extended device error data. * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::GetLastErrorInfo(STI_ERROR_INFO *pLastErrorInfo) { WIAS_TRACE((g_hInst,"TestUsdDevice::GetLastErrorInfo")); if (IsBadWritePtr(pLastErrorInfo, sizeof(STI_ERROR_INFO))) { return STIERR_INVALID_PARAM; } pLastErrorInfo->dwGenericError = m_dwLastOperationError; pLastErrorInfo->szExtendedErrorText[0] = '\0'; return STI_OK; } /**************************************************************************\ * TestUsdDevice::LockDevice * * Lock access to the device. * * Arguments: * * None * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::LockDevice(void) { return STI_OK; } /**************************************************************************\ * TestUsdDevice::UnLockDevice * * Unlock access to the device. * * Arguments: * * None * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::UnLockDevice(void) { return STI_OK; } /**************************************************************************\ * TestUsdDevice::RawReadData * * Read raw data from the device. * * Arguments: * * lpBuffer - * lpdwNumberOfBytes - * lpOverlapped - * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::RawReadData( LPVOID lpBuffer, LPDWORD lpdwNumberOfBytes, LPOVERLAPPED lpOverlapped) { WIAS_TRACE((g_hInst,"TestUsdDevice::RawReadData")); return STI_OK; } /**************************************************************************\ * TestUsdDevice::RawWriteData * * Write raw data to the device. * * Arguments: * * lpBuffer - * dwNumberOfBytes - * lpOverlapped - * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::RawWriteData( LPVOID lpBuffer, DWORD dwNumberOfBytes, LPOVERLAPPED lpOverlapped) { WIAS_TRACE((g_hInst,"TestUsdDevice::RawWriteData")); return STI_OK; } /**************************************************************************\ * TestUsdDevice::RawReadCommand * * * * Arguments: * * lpBuffer - * lpdwNumberOfBytes - * lpOverlapped - * * Return Value: * * Status * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::RawReadCommand( LPVOID lpBuffer, LPDWORD lpdwNumberOfBytes, LPOVERLAPPED lpOverlapped) { WIAS_TRACE((g_hInst,"TestUsdDevice::RawReadCommand, unsupported")); return STIERR_UNSUPPORTED; } /**************************************************************************\ * TestUsdDevice::RawWriteCommand * * * * Arguments: * * lpBuffer - * nNumberOfBytes - * lpOverlapped - * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::RawWriteCommand( LPVOID lpBuffer, DWORD nNumberOfBytes, LPOVERLAPPED lpOverlapped) { WIAS_TRACE((g_hInst,"TestUsdDevice::RawWriteCommand, unsupported")); return STIERR_UNSUPPORTED; } /**************************************************************************\ * TestUsdDevice::Initialize * * Initialize the device object. * * Arguments: * * pIStiDevControlNone - * dwStiVersion - * hParametersKey - * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ STDMETHODIMP TestUsdDevice::Initialize( PSTIDEVICECONTROL pIStiDevControl, DWORD dwStiVersion, HKEY hParametersKey) { HRESULT hr = STI_OK; UINT uiNameLen = 0; CAMERA_STATUS camStatus; WIAS_TRACE((g_hInst,"TestUsdDevice::Initialize")); if (!pIStiDevControl) { WIAS_ERROR((g_hInst,"TestUsdDevice::Initialize, invalid device control interface")); return STIERR_INVALID_PARAM; } // // Cache the device control interface. // m_pIStiDevControl = pIStiDevControl; m_pIStiDevControl->AddRef(); // // Try to open the camera only once here during Initialize // hr = CamOpenCamera(&camStatus); return (hr); } /**************************************************************************\ * TestUsdDevice::RunNotifications * * Monitor changes to the source data file parent directory. * * Arguments: * * None * * Return Value: * * Status. * * History: * * 9/11/1998 Original Version * \**************************************************************************/ VOID TestUsdDevice::RunNotifications(void) { // // start up camera event dlg // WIAS_TRACE((g_hInst,"TestUsdDevice::RunNotifications: start up event dlg")); HWND hWnd = GetDesktopWindow(); int iret = (int)DialogBoxParam( g_hInst, MAKEINTRESOURCE(IDD_EVENT_DLG), hWnd, CameraEventDlgProc, (LPARAM)this ); WIAS_TRACE((g_hInst,"TestUsdDevice::RunNotifications, iret = 0x%lx",iret)); if (iret == -1) { int err = ::GetLastError(); WIAS_TRACE((g_hInst,"TestUsdDevice::RunNotifications, dlg error = 0x%lx",err)); } } /**************************************************************************\ * FileChangeThread * * Calls RunNotifications to detect changing source data file. * * Arguments: * * lpParameter - Pointer to device object. * * Return Value: * * None * * History: * * 9/11/1998 Original Version * \**************************************************************************/ VOID FileChangeThread(LPVOID lpParameter) { WIAS_TRACE((g_hInst,"TestUsdDevice::")); TestUsdDevice *pThisDevice = (TestUsdDevice *)lpParameter; pThisDevice->RunNotifications(); }