/**************************************************************************** * * DEVICE.CPP * * Copyright (C) Microsoft Corporation 1996-1999 * All rights reserved * ***************************************************************************/ #include "Sampusd.h" #include VOID FileChangeThread( LPVOID lpParameter ); UsdSampDevice::UsdSampDevice( LPUNKNOWN punkOuter ): m_cRef(1), m_punkOuter(NULL), m_fValid(FALSE), m_pDcb(NULL), m_DeviceDataHandle(INVALID_HANDLE_VALUE), m_hSignalEvent(INVALID_HANDLE_VALUE), m_hThread(NULL), m_guidLastEvent(GUID_NULL), m_EventSignalState(TRUE) { // // See if we are aggregated. If we are ( which will be almost always the case ) // save pointer to controlling Unknown , so subsequent calls will be delegated // If not, set the same pointer to "this" . // N.b. cast below is important in order to point to right virtual table // if (punkOuter) { m_punkOuter = punkOuter; } else { m_punkOuter = reinterpret_cast (static_cast (this)); } m_hShutdownEvent = CreateEvent( NULL, // Attributes TRUE, // Manual reset FALSE, // Initial state - not set NULL ); // Anonymous if ( (INVALID_HANDLE_VALUE !=m_hShutdownEvent) && (NULL != m_hShutdownEvent)) { m_fValid = TRUE; } } UsdSampDevice::~UsdSampDevice( VOID ) { // Kill notification thread if it exists SetNotificationHandle(NULL); if (m_hShutdownEvent && m_hShutdownEvent!=INVALID_HANDLE_VALUE) { CloseHandle(m_hShutdownEvent); } if( INVALID_HANDLE_VALUE != m_DeviceDataHandle ) { CloseHandle( m_DeviceDataHandle ); } if (m_pszDeviceNameA) { delete [] m_pszDeviceNameA; m_pszDeviceNameA = NULL; } } STDMETHODIMP UsdSampDevice::GetCapabilities( PSTI_USD_CAPS pUsdCaps ) { HRESULT hres = STI_OK; ZeroMemory(pUsdCaps,sizeof(*pUsdCaps)); pUsdCaps->dwVersion = STI_VERSION; // We do support device notifications, but not reuiring polling pUsdCaps->dwGenericCaps = STI_USD_GENCAP_NATIVE_PUSHSUPPORT; return hres; } STDMETHODIMP UsdSampDevice::GetStatus( PSTI_DEVICE_STATUS pDevStatus ) { HRESULT hres = STI_OK; // // If we are asked, verify whether device is online // pDevStatus->dwOnlineState = 0L; if( pDevStatus->StatusMask & STI_DEVSTATUS_ONLINE_STATE ) { if (INVALID_HANDLE_VALUE != m_DeviceDataHandle) { // File 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 ) { // // Launch app very first time we load // if(m_EventSignalState) { pDevStatus->dwEventHandlingState = STI_EVENTHANDLING_PENDING; m_guidLastEvent = guidEventFirstLoaded; m_EventSignalState = FALSE; } if (IsChangeDetected(NULL,FALSE)) { pDevStatus->dwEventHandlingState |= STI_EVENTHANDLING_PENDING; } } return hres; } STDMETHODIMP UsdSampDevice::DeviceReset( VOID ) { HRESULT hres = STI_OK; // Reset current active device if (INVALID_HANDLE_VALUE != m_DeviceDataHandle) { ::SetFilePointer( m_DeviceDataHandle, 0, NULL, FILE_BEGIN); m_dwLastOperationError = ::GetLastError(); } hres = HRESULT_FROM_WIN32(m_dwLastOperationError); return hres; } STDMETHODIMP UsdSampDevice::Diagnostic( LPDIAG pBuffer ) { HRESULT hres = STI_OK; // Initialize response buffer pBuffer->dwStatusMask = 0; ZeroMemory(&pBuffer->sErrorInfo,sizeof(pBuffer->sErrorInfo)); pBuffer->sErrorInfo.dwGenericError = NOERROR; pBuffer->sErrorInfo.dwVendorError = 0; // This example always returns that the unit passed diagnostics return hres; } STDMETHODIMP UsdSampDevice:: SetNotificationHandle( HANDLE hEvent ) // SYNCHRONIZED { HRESULT hres = STI_OK; TAKE_CRIT_SECT t(m_cs); if (hEvent && (hEvent !=INVALID_HANDLE_VALUE)) { m_hSignalEvent = hEvent; if (m_DeviceDataHandle != INVALID_HANDLE_VALUE) { // // if we need to be asyncronous, create notification thread // m_dwAsync = 1; m_guidLastEvent = GUID_NULL; if (m_dwAsync) { if (!m_hThread) { DWORD dwThread; m_hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FileChangeThread, (LPVOID)this, 0, &dwThread); m_pDcb->WriteToErrorLog(STI_TRACE_INFORMATION, L"SampUSD::Enabling notification monitoring", NOERROR) ; } } else { hres = STIERR_UNSUPPORTED; } } else { hres = STIERR_NOT_INITIALIZED; } } else { // // Disable hardware notifications // SetEvent(m_hShutdownEvent); if ( m_hThread ) { WaitForSingleObject(m_hThread,400); CloseHandle(m_hThread); m_hThread = NULL; m_guidLastEvent = GUID_NULL; } m_pDcb->WriteToErrorLog(STI_TRACE_INFORMATION, L"SampUSD::Disabling notification monitoring", NOERROR) ; } return hres; } STDMETHODIMP UsdSampDevice::GetNotificationData( LPSTINOTIFY pBuffer ) // SYNCHRONIZED { HRESULT hres = STI_OK; TAKE_CRIT_SECT t(m_cs); // // 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); } else { hres = STIERR_NOEVENTS; } return hres; } STDMETHODIMP UsdSampDevice::Escape( STI_RAW_CONTROL_CODE EscapeFunction, LPVOID pInData, DWORD cbInDataSize, LPVOID pOutData, DWORD cbOutDataSize, LPDWORD pcbActualData ) { HRESULT hres = STI_OK; // // Write indata to device if needed. // hres = STIERR_UNSUPPORTED; return hres; } STDMETHODIMP UsdSampDevice::GetLastError( LPDWORD pdwLastDeviceError ) // SYNCHRONIZED { HRESULT hres = STI_OK; TAKE_CRIT_SECT t(m_cs); if ( IsBadWritePtr( pdwLastDeviceError,4 )) { hres = STIERR_INVALID_PARAM; } else { *pdwLastDeviceError = m_dwLastOperationError; } return hres; } STDMETHODIMP UsdSampDevice::GetLastErrorInfo(STI_ERROR_INFO *pLastErrorInfo) // SYNCHRONIZED { HRESULT hres = STI_OK; TAKE_CRIT_SECT t(m_cs); if ( IsBadWritePtr( pLastErrorInfo,4 )) { hres = STIERR_INVALID_PARAM; } else { pLastErrorInfo->dwGenericError = m_dwLastOperationError; pLastErrorInfo->szExtendedErrorText[0] = L'\0'; } return hres; } STDMETHODIMP UsdSampDevice::LockDevice( VOID ) { HRESULT hres = STI_OK; return hres; } STDMETHODIMP UsdSampDevice::UnLockDevice( VOID ) { HRESULT hres = STI_OK; return hres; } STDMETHODIMP UsdSampDevice::RawReadData( LPVOID lpBuffer, LPDWORD lpdwNumberOfBytes, LPOVERLAPPED lpOverlapped ) { HRESULT hres = STI_OK; BOOL fRet = FALSE; DWORD dwBytesReturned = 0; if (INVALID_HANDLE_VALUE != m_DeviceDataHandle) { m_dwLastOperationError = NOERROR; fRet = ::ReadFile(m_DeviceDataHandle, lpBuffer, *lpdwNumberOfBytes, lpdwNumberOfBytes, lpOverlapped); if (!fRet) { m_dwLastOperationError = ::GetLastError(); } hres = HRESULT_FROM_WIN32(m_dwLastOperationError); } else { hres = STIERR_NOT_INITIALIZED; } return hres; } STDMETHODIMP UsdSampDevice::RawWriteData( LPVOID lpBuffer, DWORD dwNumberOfBytes, LPOVERLAPPED lpOverlapped ) { HRESULT hres = STI_OK; BOOL fRet = FALSE;; DWORD dwBytesReturned = 0; if (INVALID_HANDLE_VALUE != m_DeviceDataHandle) { fRet = ::WriteFile(m_DeviceDataHandle, lpBuffer, dwNumberOfBytes, &dwBytesReturned, lpOverlapped); if (!fRet) { m_dwLastOperationError = ::GetLastError(); } hres = HRESULT_FROM_WIN32(m_dwLastOperationError); } else { hres = STIERR_NOT_INITIALIZED; } return hres; } STDMETHODIMP UsdSampDevice::RawReadCommand( LPVOID lpBuffer, LPDWORD lpdwNumberOfBytes, LPOVERLAPPED lpOverlapped ) { HRESULT hres = STIERR_UNSUPPORTED; return hres; } STDMETHODIMP UsdSampDevice::RawWriteCommand( LPVOID lpBuffer, DWORD nNumberOfBytes, LPOVERLAPPED lpOverlapped ) { HRESULT hres = STIERR_UNSUPPORTED; return hres; } STDMETHODIMP UsdSampDevice::Initialize( PSTIDEVICECONTROL pDcb, DWORD dwStiVersion, HKEY hParametersKey ) { HRESULT hres = STI_OK; UINT uiNameLen = 0; WCHAR szDeviceNameW[MAX_PATH]; if (!pDcb) { return STIERR_INVALID_PARAM; } *szDeviceNameW = L'\0'; // Check STI specification version number m_pDcb = pDcb; m_pDcb->AddRef(); // Get the name of the device port we need to open hres = m_pDcb->GetMyDevicePortName(szDeviceNameW,sizeof(szDeviceNameW)/sizeof(WCHAR)); if (!SUCCEEDED(hres) || !*szDeviceNameW) { return hres; } // Convert name to SBCS uiNameLen = WideCharToMultiByte(CP_ACP, 0, szDeviceNameW, -1, NULL, NULL, 0, 0); if (!uiNameLen) { return STIERR_INVALID_PARAM; } m_pszDeviceNameA = new CHAR[uiNameLen+1]; if (!m_pszDeviceNameA) { return STIERR_INVALID_PARAM; } WideCharToMultiByte(CP_ACP, 0, szDeviceNameW, -1, m_pszDeviceNameA, uiNameLen, 0, 0); // // Open device ourselves // m_DeviceDataHandle = CreateFileA( m_pszDeviceNameA, GENERIC_READ , // Access mask FILE_SHARE_READ | FILE_SHARE_WRITE, // Share mode NULL, // SA OPEN_EXISTING, // Create disposition FILE_ATTRIBUTE_SYSTEM, // Attributes NULL ); m_dwLastOperationError = ::GetLastError(); hres = (m_DeviceDataHandle != INVALID_HANDLE_VALUE) ? S_OK : MAKE_HRESULT(SEVERITY_ERROR,FACILITY_WIN32,m_dwLastOperationError); return hres; } VOID UsdSampDevice:: RunNotifications(VOID) { HANDLE hNotifyFileSystemChange = INVALID_HANDLE_VALUE; DWORD dwErr; CHAR szDirPath[MAX_PATH]; CHAR *pszLastSlash; // // Find name of the parent directory for out file and set up waiting on any // changes in it. // lstrcpyA(szDirPath,m_pszDeviceNameA); pszLastSlash = strrchr(szDirPath,'\\'); if (pszLastSlash) { *pszLastSlash = '\0'; } hNotifyFileSystemChange = FindFirstChangeNotificationA( szDirPath, FALSE, FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME ); if (hNotifyFileSystemChange == INVALID_HANDLE_VALUE) { dwErr = ::GetLastError(); return; } // Set initial values for time and size IsChangeDetected(NULL); // HANDLE hEvents[2] = {m_hShutdownEvent,hNotifyFileSystemChange}; BOOL fLooping = TRUE; while (fLooping) { dwErr = ::WaitForMultipleObjects(2, hEvents, FALSE, INFINITE ); switch(dwErr) { case WAIT_OBJECT_0+1: // Change detected - signal if (m_hSignalEvent !=INVALID_HANDLE_VALUE) { // Which change ? if (IsChangeDetected(&m_guidLastEvent)) { m_pDcb->WriteToErrorLog(STI_TRACE_INFORMATION, L"SampUSD::Monitored file change detected", NOERROR) ; ::SetEvent(m_hSignalEvent); } } // Go back to waiting for next file system event FindNextChangeNotification(hNotifyFileSystemChange); break; case WAIT_OBJECT_0: // Fall through default: fLooping = FALSE; } } FindCloseChangeNotification(hNotifyFileSystemChange); } BOOL UsdSampDevice:: IsChangeDetected( GUID *pguidEvent, BOOL fRefresh // TRUE ) { BOOL fRet = FALSE; LARGE_INTEGER liNewHugeSize; FILETIME ftLastWriteTime; DWORD dwError; WIN32_FILE_ATTRIBUTE_DATA sNewFileAttributes; ZeroMemory(&sNewFileAttributes,sizeof(sNewFileAttributes)); dwError = NOERROR; if ( GetFileAttributesExA(m_pszDeviceNameA,GetFileExInfoStandard, &sNewFileAttributes)) { ftLastWriteTime =sNewFileAttributes.ftLastWriteTime; liNewHugeSize.LowPart = sNewFileAttributes.nFileSizeLow; liNewHugeSize.HighPart= sNewFileAttributes.nFileSizeHigh ; } else { BY_HANDLE_FILE_INFORMATION sFileInfo; if (GetFileInformationByHandle(m_DeviceDataHandle,&sFileInfo)) { ftLastWriteTime =sFileInfo.ftLastWriteTime; liNewHugeSize.LowPart = sFileInfo.nFileSizeLow; liNewHugeSize.HighPart= sFileInfo.nFileSizeHigh ; } else { dwError = ::GetLastError(); } } if (NOERROR == dwError ) { // // First check size, because it is easy to change time without changing size // if (m_dwLastHugeSize.QuadPart != liNewHugeSize.QuadPart) { if (pguidEvent) { *pguidEvent = guidEventSizeChanged; } fRet = TRUE; } else { if (CompareFileTime(&m_ftLastWriteTime,&ftLastWriteTime) == -1 ) { if (pguidEvent) { *pguidEvent = guidEventTimeChanged; } fRet = TRUE; } else { // Nothing really changed } } m_ftLastWriteTime = ftLastWriteTime; m_dwLastHugeSize = liNewHugeSize; } return fRet; } VOID FileChangeThread( LPVOID lpParameter ) { UsdSampDevice *pThisDevice = (UsdSampDevice *)lpParameter; pThisDevice->RunNotifications(); }