/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: Connect.cpp Abstract: Handles all outgoing interfaces Author: mquinton - 5/7/97 Notes: optional-notes Revision History: --*/ #include "stdafx.h" #include "uuids.h" extern IGlobalInterfaceTable * gpGIT; extern CRITICAL_SECTION gcsGlobalInterfaceTable; extern ULONG_PTR GenerateHandleAndAddToHashTable( ULONG_PTR Element); extern void RemoveHandleFromHashTable(ULONG_PTR dwHandle); extern CHashTable * gpHandleHashTable; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // CTAPIConnectionPoint - implementation of IConnectionPoint // for TAPI object (ITTAPIEventNotification outgoing interface). // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT CTAPIConnectionPoint::Initialize( IConnectionPointContainer * pCPC, IID iid ) { LOG((TL_TRACE, "Initialize enter")); #if DBG { WCHAR guidName[100]; StringFromGUID2(iid, (LPOLESTR)&guidName, 100); LOG((TL_INFO, "Initialize - IID : %S", guidName)); } #endif // // Create the unadvise event // m_hUnadviseEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); if (m_hUnadviseEvent == NULL) { LOG((TL_TRACE, "Initialize - out of memory")); return E_OUTOFMEMORY; } // // Addref the connection point container // pCPC->AddRef(); // // Addref ourselves // this->AddRef(); // // Save stuff // m_pCPC = pCPC; m_iid = iid; m_pConnectData = NULL; EnterCriticalSection( &gcsGlobalInterfaceTable ); m_cThreadsInGet = 0; m_fMarkedForDelete = FALSE; LeaveCriticalSection( &gcsGlobalInterfaceTable ); m_bInitialized = TRUE; LOG((TL_TRACE, "Initialize exit")); return S_OK; } // IConnectionPoint methods HRESULT STDMETHODCALLTYPE CTAPIConnectionPoint::GetConnectionInterface( IID * pIID ) { if ( TAPIIsBadWritePtr( pIID, sizeof (IID) ) ) { LOG((TL_ERROR, "GetConnectionInterface - bad pointer")); return E_POINTER; } Lock(); *pIID = m_iid; Unlock(); return S_OK; } HRESULT STDMETHODCALLTYPE CTAPIConnectionPoint::GetConnectionPointContainer( IConnectionPointContainer ** ppCPC ) { if ( TAPIIsBadWritePtr( ppCPC, sizeof( IConnectionPointContainer *) ) ) { LOG((TL_ERROR, "GetCPC - bad pointer")); return E_POINTER; } Lock(); *ppCPC = m_pCPC; (*ppCPC)->AddRef(); Unlock(); return S_OK; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Advise // // the application calls this function when it wants to register an // outgoing interface // // this interface is used to register the ITTAPIEventNotification // interface which is used to get all TAPI call control events // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT STDMETHODCALLTYPE CTAPIConnectionPoint::Advise( IUnknown * pUnk, DWORD * pdwCookie ) { HRESULT hr = S_OK; CONNECTDATA * pCD; IID iid; LOG((TL_TRACE, "Advise[%p] called", this)); if ( TAPIIsBadWritePtr( pdwCookie, sizeof (DWORD) ) ) { LOG((TL_ERROR, "Advise - bad pointer")); return E_POINTER; } if ( IsBadReadPtr( pUnk, sizeof(IUnknown *) ) ) { LOG((TL_ERROR, "Advise - bad IUnknown")); return E_POINTER; } Lock(); if ( m_bInitialized == FALSE ) { LOG((TL_ERROR, "Advise - not initialized")); Unlock(); return TAPI_E_NOT_INITIALIZED; } // // We only allow one callback per connection point // if ( NULL != m_pConnectData ) { LOG((TL_ERROR, "Advise - advise already called")); Unlock(); return CONNECT_E_ADVISELIMIT; } // // Create a new connectdata struct // m_pConnectData = (CONNECTDATA *) ClientAlloc( sizeof CONNECTDATA ); if (NULL == m_pConnectData) { LOG((TL_ERROR, "Advise failed - pCD == NULL")); Unlock(); return E_OUTOFMEMORY; } // // Keep a reference to the callback // try { pUnk->AddRef(); } catch(...) { LOG((TL_ERROR, "Advise - IUnknown bad")); ClientFree( m_pConnectData ); m_pConnectData = NULL; Unlock(); return E_POINTER; } // // Save the interface // m_pConnectData->pUnk = pUnk; ITTAPIEventNotification *pEventNotification; hr = pUnk->QueryInterface(IID_ITTAPIEventNotification, (void**)(&pEventNotification) ); if (SUCCEEDED(hr) ) { iid = IID_ITTAPIEventNotification; pEventNotification->Release(); } else { iid = DIID_ITTAPIDispatchEventNotification; } m_iid = iid; m_pConnectData->dwCookie = CreateHandleTableEntry((ULONG_PTR)m_pConnectData); // // Return the cookie // *pdwCookie = m_pConnectData->dwCookie; //set it to FALSE if not already set. m_fMarkedForDelete = FALSE; Unlock(); LOG((TL_TRACE, "Advise generated cookie [%lx]", *pdwCookie)); // // Put the callback in the globalinterfacetable // so it can be accessed across threads // EnterCriticalSection( &gcsGlobalInterfaceTable ); if ( NULL != gpGIT ) { hr = gpGIT->RegisterInterfaceInGlobal( pUnk, iid, &m_dwCallbackCookie ); } else { hr = E_FAIL; } LeaveCriticalSection( &gcsGlobalInterfaceTable ); if ( FAILED(hr) ) { Lock(); LOG((TL_ERROR, "Advise - RegisterInterfaceInGlobal failed - %lx", hr)); DeleteHandleTableEntry(m_pConnectData->dwCookie); *pdwCookie = 0; ClientFree( m_pConnectData ); m_pConnectData = NULL; m_fMarkedForDelete = TRUE; pUnk->Release(); Unlock(); } LOG((TL_TRACE, "Advise - exit")); return hr; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // Unadvise // // Used to unregister an interface // // dwCookie - Cookie used to identify the interface registration, returned in // advise // // returns // S_OK // CONNECT_E_NOCONNECTION // dwCookie is not a valid connection //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT STDMETHODCALLTYPE CTAPIConnectionPoint::Unadvise( DWORD dwCookie ) { HRESULT hr = S_OK; LOG((TL_TRACE, "Unadvise[%p] - enter. Cookie: [%lx]", this, dwCookie)); Lock(); // // Check connection point // if ( NULL != m_pConnectData ) { // // Check cookie // if (m_pConnectData->dwCookie == dwCookie) { LOG((TL_INFO, "Unadvise - immediate ")); // // Remove entry for this cookie from the handle table // DeleteHandleTableEntry(m_pConnectData->dwCookie); // // Free the connect data // m_pConnectData->dwCookie = 0; m_pConnectData->pUnk->Release(); ClientFree( m_pConnectData ); m_pConnectData = NULL; Unlock(); EnterCriticalSection( &gcsGlobalInterfaceTable ); // // Mark for delete // m_fMarkedForDelete = TRUE; if ( NULL != gpGIT ) { // // If there are threads in get we must wait for them to complete so // we can call revoke // while ( m_cThreadsInGet != 0 ) { LOG((TL_INFO, "Unadvise - %ld threads in get", m_cThreadsInGet)); LeaveCriticalSection( &gcsGlobalInterfaceTable ); DWORD dwSignalled; CoWaitForMultipleHandles ( 0, INFINITE, 1, &m_hUnadviseEvent, &dwSignalled ); EnterCriticalSection( &gcsGlobalInterfaceTable ); } // // We have guaranteed that no threads are in get. Do the revoke. // hr = gpGIT->RevokeInterfaceFromGlobal( m_dwCallbackCookie ); if ( FAILED(hr) ) { LOG((TL_ERROR, "Unadvise - RevokeInterfaceFromGlobal failed - hr = %lx", hr)); } m_dwCallbackCookie = 0; } else { LOG((TL_ERROR, "Unadvise - no global interface table")); } LeaveCriticalSection( &gcsGlobalInterfaceTable ); } else { Unlock(); LOG((TL_ERROR, "Unadvise - cp does not match ")); hr = CONNECT_E_NOCONNECTION; } } else { Unlock(); LOG((TL_ERROR, "Unadvise - cp not registered ")); hr = CONNECT_E_NOCONNECTION; } LOG((TL_TRACE, hr, "Unadvise - exit")); return hr; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // EnumConnections // // Used to enumerate connections already made on this connection point // // ppEnum // return enumerator in here // // returns // S_OK // E_POINTER //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ HRESULT STDMETHODCALLTYPE CTAPIConnectionPoint::EnumConnections( IEnumConnections ** ppEnum ) { HRESULT hr = S_OK; if ( TAPIIsBadWritePtr( ppEnum, sizeof( IEnumConnections *) ) ) { LOG((TL_ERROR, "EnumConnections - bad pointer")); return E_POINTER; } // // Create enumerate object // CComObject< CTapiTypeEnum , &IID_IEnumConnections> > * p; hr = CComObject< CTapiTypeEnum , &IID_IEnumConnections> >::CreateInstance( &p ); if (S_OK != hr) { return hr; } // // Initialize it // ConnectDataArray newarray; Lock(); if ( NULL != m_pConnectData ) { newarray.Add(*m_pConnectData); } Unlock(); hr = p->Initialize( newarray ); newarray.Shutdown(); if (S_OK != hr) { return hr; } *ppEnum = p; return S_OK; } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // FinalRelease // release all CONNECTDATA structs // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void CTAPIConnectionPoint::FinalRelease() { LOG((TL_TRACE, "FinalRelease - enter")); if (NULL != m_pConnectData) { // // The app didn't call unadvise. Let's do it now. // LOG((TL_INFO, "FinalRelease - calling unadvise")); Unadvise(m_pConnectData->dwCookie) ; } // // Release the connection point container // if (m_pCPC) { m_pCPC->Release(); m_pCPC = NULL; } // // Close the unadvise event // if (m_hUnadviseEvent) { CloseHandle(m_hUnadviseEvent); m_hUnadviseEvent = NULL; } LOG((TL_TRACE, "FinalRelease - exit")); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // the object calls this to get a marshaled event // pointer in the correct thread // //++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ULONG_PTR CTAPIConnectionPoint::GrabEventCallback() { IUnknown * pReturn = NULL; HRESULT hr = E_FAIL; DWORD dwCallbackCookie; IID iid; Lock(); // // If we're already released, don't try to send any events. // if ( NULL != m_pConnectData ) { // // Copy member data // iid = m_iid; Unlock(); EnterCriticalSection( &gcsGlobalInterfaceTable ); if (m_fMarkedForDelete == FALSE) { // // Add to the count of threads in get. // m_cThreadsInGet++; // // Copy member data // dwCallbackCookie = m_dwCallbackCookie; if (gpGIT != NULL) { gpGIT->AddRef(); // // Don't hold a critical section while getting // LeaveCriticalSection( &gcsGlobalInterfaceTable ); hr = gpGIT->GetInterfaceFromGlobal( dwCallbackCookie, iid, (void **)&pReturn ); if ( SUCCEEDED(hr) ) { LOG((TL_INFO, "GrabEventCallback - GetInterfaceFromGlobal suceeded [%p]", pReturn)); } else { LOG((TL_ERROR, "GrabEventCallback - GetInterfaceFromGlobal failed - hr = %lx", hr)); pReturn = NULL; } EnterCriticalSection( &gcsGlobalInterfaceTable ); gpGIT->Release(); } // // Done. Decrement the count of threads in get. // m_cThreadsInGet--; } else { LOG((TL_INFO, "GrabEventCallback - already marked for delete")); } LeaveCriticalSection( &gcsGlobalInterfaceTable ); if ( m_fMarkedForDelete == TRUE ) { // // Someone called unadvise while we were using the cookie. // Signal so they can do the revoke now. // if ( m_hUnadviseEvent ) { SetEvent(m_hUnadviseEvent); } else { LOG((TL_ERROR, "GrabEventCallback - no event")); _ASSERTE(FALSE); } // // If we got a callback, no need to return it because // unadvise has been called. // if ( pReturn != NULL ) { pReturn->Release(); pReturn = NULL; } } } else { LOG((TL_ERROR, "GrabEventCallback - already released")); Unlock(); } LOG((TL_TRACE, hr, "GrabEventCallback - exit")); return (ULONG_PTR)pReturn; }