|
|
/*++
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 <IEnumConnections, CONNECTDATA, _Copy<CONNECTDATA>, &IID_IEnumConnections> > * p;
hr = CComObject< CTapiTypeEnum <IEnumConnections, CONNECTDATA, _Copy<CONNECTDATA>, &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; }
|