/*++ Copyright (c) 1997 Microsoft Corporation Module Name: conn.cpp Abstract: Code to maintain list of process connections for an active device Author: Vlad Sadovsky (VladS) 11-Feb-1997 History: --*/ // // Include Headers // #include "precomp.h" #include "stiexe.h" #include "conn.h" // // Static variables // // LONG g_lGlobalConnectionId = 0; LIST_ENTRY g_ConnectionListHead; // LONG g_lTotalOpenedConnections = 0; // CRIT_SECT g_ConnectionListSync; // Global sync object for linked list syncronization // // Static functions // STI_CONN * LocateConnectionByHandle( HANDLE hConnection ); // // Methods // STI_CONN::STI_CONN( IN LPCTSTR pszDeviceName, IN DWORD dwMode, IN DWORD dwProcessId ) { BOOL fRet; // // Initialize fields // m_dwSignature = CONN_SIGNATURE; m_dwProcessId = dwProcessId; m_pOpenedDevice = NULL; m_dwOpenMode = dwMode; strDeviceName.CopyString(pszDeviceName); m_dwNotificationMessage = 0L; m_hwndProcessWindow = NULL; m_hevProcessEvent = INVALID_HANDLE_VALUE; m_GlocalListEntry.Flink = m_GlocalListEntry.Blink = NULL; m_DeviceListEntry.Flink = m_DeviceListEntry.Blink = NULL; InitializeListHead( &m_NotificationListHead ); m_hUniqueId = LongToPtr(InterlockedIncrement(&g_lGlobalConnectionId)); __try { // Critical section for protecting interthread access to the device if(!InitializeCriticalSectionAndSpinCount(&m_CritSec, MINLONG)) { m_fValid = FALSE; return; } } __except (EXCEPTION_EXECUTE_HANDLER) { // This is really bad - mark this connection as invalid m_fValid = FALSE; return; } SetFlags(0); // // Locate device incrementing it's ref count // m_pOpenedDevice = g_pDevMan->IsInList(DEV_MAN_IN_LIST_DEV_ID, pszDeviceName); if (!m_pOpenedDevice) { // Failed to connect to the device DBG_WRN(("Refused connection to non-existing device (%S)", pszDeviceName)); return; } // // Establish link to the device object // fRet = m_pOpenedDevice->AddConnection(this); m_pOpenedDevice->Release(); if (!fRet) { // // Failed to connect to the device, find out reason // DBG_WRN(("Refused connection to device (%S)", pszDeviceName)); ReportError(::GetLastError()); return; } m_fValid = TRUE; DBG_TRC(("Successfully created connection to device (%S) handle:(%x)", pszDeviceName,this)); } STI_CONN::~STI_CONN() { SetFlags(CONN_FLAG_SHUTDOWN); DBG_TRC(("Destroying connection(%X)",this)); DumpObject(); #if 0 DebugDumpScheduleList(TEXT("Conn DTOR enter")); #endif EnterCrit(); // // If there are items in notification queue - remove them // if (!IsListEmpty(&m_NotificationListHead )) { } LeaveCrit(); // // Disconnect from the device // if (m_pOpenedDevice) { m_pOpenedDevice->RemoveConnection(this); } // // Remove from global list if still there // if (m_GlocalListEntry.Flink &&!IsListEmpty(&m_GlocalListEntry)) { RemoveEntryList(&m_GlocalListEntry); } // // We know we tried to initialize it, so it is safe to delete it. // DeleteCriticalSection(&m_CritSec); m_fValid = FALSE; } /* * Reference counting methods. It is simpler to use COM ref counting , to looks the same * as COM objects, in spite of the fact we are not really supporting COM, because QI method is * not functional */ STDMETHODIMP STI_CONN::QueryInterface( REFIID riid, LPVOID * ppvObj) { return E_FAIL; } STDMETHODIMP_(ULONG) STI_CONN::AddRef( void) { return ::InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) STI_CONN::Release( void) { LONG cNew; if(!(cNew = ::InterlockedDecrement(&m_cRef))) { delete this; } return cNew; } BOOL STI_CONN:: SetSubscribeInfo( PLOCAL_SUBSCRIBE_CONTAINER pSubscribe ) { BOOL fRet = FALSE; DBG_TRC(("Subscribing to device on connection (%X)",this)); ReportError(NOERROR); // // NULL means resetting subscribe info block // if (!pSubscribe) { m_dwSubscribeFlags = 0L; m_hwndProcessWindow = NULL; m_hevProcessEvent = INVALID_HANDLE_VALUE; fRet = TRUE; } else { // // Save information needed to notify process // m_dwSubscribeFlags = pSubscribe->dwFlags; if (pSubscribe->dwFlags & STI_SUBSCRIBE_FLAG_WINDOW) { if (IsWindow((HWND)pSubscribe->upLocalWindowHandle)) { m_hwndProcessWindow = (HWND)pSubscribe->upLocalWindowHandle; m_uiNotificationMessage = pSubscribe->uiNotificationMessage; fRet = TRUE; } else { ASSERT(("Invalid window handle passed", 0)); } } else if (pSubscribe->dwFlags & STI_SUBSCRIBE_FLAG_EVENT) { HANDLE hProcessMe = GetCurrentProcess(); HANDLE hProcessClient = NULL; hProcessClient = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE, m_dwProcessId ); if (IS_VALID_HANDLE(hProcessClient)) { if (::DuplicateHandle(hProcessClient, (HANDLE)pSubscribe->upLocalEventHandle, hProcessMe, &m_hevProcessEvent, EVENT_MODIFY_STATE, 0, 0) ) { fRet = TRUE; ReportError(NOERROR); } else { DBG_WRN(("Subscribe handler failed to recognize client process on connection (%X)",this)); ReportError(::GetLastError()); } ::CloseHandle(hProcessClient); } else { ReportError(::GetLastError()); } } } return fRet; } BOOL STI_CONN:: QueueNotificationToProcess( LPSTINOTIFY pStiNotification ) { BOOL fRet; // // Validate notification block // // // Add to the tail of the list // STI_NOTIFICATION * pNotification = NULL; pNotification = new STI_NOTIFICATION(pStiNotification); if (pNotification && pNotification->IsValid()) { EnterCrit(); InsertTailList(&m_NotificationListHead,&pNotification->m_ListEntry); LeaveCrit(); } // // Notify process // if (m_dwSubscribeFlags & STI_SUBSCRIBE_FLAG_WINDOW) { ::PostMessage(m_hwndProcessWindow,m_uiNotificationMessage ,0,0L); } else if (m_dwSubscribeFlags & STI_SUBSCRIBE_FLAG_EVENT) { ::SetEvent(m_hevProcessEvent); } fRet = TRUE; return fRet; } DWORD STI_CONN:: GetNotification( PVOID pBuffer, DWORD *pdwSize ) { STI_NOTIFICATION * pNotification = NULL; TAKE_STI_CONN t(this); LIST_ENTRY *pentry; if (IsListEmpty(&m_NotificationListHead)) { return ERROR_NO_DATA; } DBG_TRC(("Request to get last notification on connection (%X) ",this)); // // Get size of the head // pentry = m_NotificationListHead.Flink; pNotification = CONTAINING_RECORD( pentry, STI_NOTIFICATION,m_ListEntry ); if (*pdwSize < pNotification->QueryAllocSize() ) { *pdwSize = pNotification->QueryAllocSize(); return ERROR_MORE_DATA; } // // Get head of the list ( and remove) and copy into user buffer // pentry = RemoveHeadList(&m_NotificationListHead); pNotification = CONTAINING_RECORD( pentry, STI_NOTIFICATION,m_ListEntry ); memcpy(pBuffer,pNotification->QueryNotifyData(),pNotification->QueryAllocSize()); delete pNotification; return NOERROR; } // // Create and initialize connection object // BOOL CreateDeviceConnection( LPCTSTR pszDeviceName, DWORD dwMode, DWORD dwProcessId, HANDLE *phConnection ) { STI_CONN *pConn = NULL; BOOL fRet = FALSE; DWORD dwErr = NOERROR; DBG_TRC(("Request to add connection to device (%S) from process(%x) with mode (%x)", pszDeviceName, dwProcessId, dwMode)); // // Create connection object // pConn = new STI_CONN(pszDeviceName, dwMode, dwProcessId); if (pConn) { if(pConn->IsValid()) { *phConnection = (HANDLE)(pConn->QueryID()); fRet = TRUE; } else { // Did not initialize properly dwErr = pConn->QueryError(); delete pConn; } } else { // Could not allocate connectionobject dwErr = ERROR_NOT_ENOUGH_MEMORY; } // // If succeeded - add created object to linked list head // if (fRet) // BEGIN PROTECTED CODE { TAKE_CRIT_SECT t(g_ConnectionListSync); InsertTailList(&g_ConnectionListHead,&pConn->m_GlocalListEntry); } // END PROTECTED CODE ::SetLastError(dwErr); return fRet; } // // // Remove connection object from the list // BOOL DestroyDeviceConnection( HANDLE hConnection, BOOL fForce ) { // // Find connection by id // DBG_TRC(("Request to remove connection (%X) ",hConnection)); STI_CONN *pConnection = NULL; // BEGIN PROTECTED CODE { TAKE_CRIT_SECT t(g_ConnectionListSync); pConnection = LocateConnectionByHandle(hConnection); if (pConnection) { if (!fForce) { pConnection->Release(); } else { delete pConnection; } } } // END PROTECTED CODE #if 0 DebugDumpScheduleList(TEXT("DestroyConnection")); #endif return (!(pConnection == NULL)); } // // Find connection object by given handle by walking all devices and all connections // for each device // BOOL LookupConnectionByHandle( HANDLE hConnection, STI_CONN **ppConnectionObject ) { STI_CONN *pConnection = NULL; *ppConnectionObject = NULL; // BEGIN PROTECTED CODE { TAKE_CRIT_SECT t(g_ConnectionListSync); pConnection = LocateConnectionByHandle(hConnection); if (pConnection) { *ppConnectionObject = pConnection; pConnection->AddRef(); } } // END PROTECTED CODE return (!(*ppConnectionObject == NULL)); } // // Requires caller to synchronize access // STI_CONN * LocateConnectionByHandle( HANDLE hConnection ) { LIST_ENTRY *pentry; LIST_ENTRY *pentryNext; STI_CONN *pConnection = NULL; ULONG ulInternalHandle = HandleToUlong(hConnection); for ( pentry = g_ConnectionListHead.Flink; pentry != &g_ConnectionListHead; pentry = pentryNext ) { pentryNext = pentry->Flink; pConnection = CONTAINING_RECORD( pentry, STI_CONN,m_GlocalListEntry ); if ( !pConnection->IsValid()) { ASSERT(("Invalid connection signature", 0)); break; } if ((ulInternalHandle == PtrToUlong(pConnection->QueryID())) && !(pConnection->QueryFlags() & CONN_FLAG_SHUTDOWN) ) { return pConnection; } } return NULL; }