Leaked source code of windows server 2003
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.
 
 
 
 
 
 

616 lines
16 KiB

/****************************************************************************
*
* DEVICE.CPP
*
* Copyright (C) Microsoft Corporation 1996-1999
* All rights reserved
*
***************************************************************************/
#include "Sampusd.h"
#include <stdio.h>
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<IUnknown*>
(static_cast<INonDelegatingUnknown*>
(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();
}