// // Module : arcmgr.cpp // // Class : CArcMgr // // Purpose: AutoReconnection mananger class - drives policy for // ts client autoreconnection // // Copyright(C) Microsoft Corporation 2001 // // Author : Nadim Abdo (nadima) // #include "stdafx.h" #include "atlwarn.h" BEGIN_EXTERN_C #define TRC_GROUP TRC_GROUP_UI #define TRC_FILE "arcmgr" #include END_EXTERN_C //Header generated from IDL #include "mstsax.h" #include "mstscax.h" #include "arcmgr.h" #include "tscerrs.h" // // Total time to wait between ARC attempts // #define ARC_RECONNECTION_DELAY (3000) CArcMgr::CArcMgr() : _pMsTscAx(NULL), _nArcAttempts(0), _fAutomaticArc(TRUE), _fContinueArc(FALSE) { DC_BEGIN_FN("CArcMgr"); DC_END_FN(); } CArcMgr::~CArcMgr() { DC_BEGIN_FN("~CArcMgr"); DC_END_FN(); } // // Static timer callback procedure // see platform SDK for params // VOID CALLBACK CArcMgr::sArcTimerCallBackProc( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime ) { CArcMgr* pThis = NULL; DC_BEGIN_FN("sArcTimerCallBackProc"); // // We pass the instance pointer around as the event id. // pThis = (CArcMgr*)idEvent; TRC_ASSERT(pThis,(TB,_T("sArcTimerCallBackProc got NULL idEvent"))); if (pThis) { pThis->ArcTimerCallBackProc(hwnd, uMsg, idEvent, dwTime); } DC_END_FN(); } // // Timer callback procedure // see platform SDK for params // VOID CArcMgr::ArcTimerCallBackProc( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime ) { HRESULT hr = E_FAIL; DC_BEGIN_FN("ArcTimerCallBackProc"); // // Kill the timers to make them one shot // if (!KillTimer(hwnd, idEvent)) { TRC_ERR((TB,_T("KillTimer for 0x%x failed with code 0x%x"), idEvent, GetLastError())); } if (_fContinueArc) { // // Attempt to kick off a reconnection attempt // hr = _pMsTscAx->Connect(); if (FAILED(hr)) { TRC_ERR((TB,_T("Arc connect() failed with: 0x%x"),hr)); // // Failed to initiate a connection so trigger a disconnect // _pMsTscAx->Disconnect(); } } DC_END_FN(); } // // Arc manager's notification that disconnection has occurred // This is the main entry point for driving autoreconnection // // Params: // [IN] disconnectReason - disconnection reason code // [IN] exReasonCode - extended disconnect reason code // [OUT] pfContinueDisconnect - returns TRUE if disconnect processing // shoud continue // // Returns: // Nothing // VOID CArcMgr::OnNotifyDisconnected( LONG disconnectReason, ExtendedDisconnectReasonCode exReasonCode, PBOOL pfContinueDisconnect ) { BOOL fShouldAutoReconnect = FALSE; AutoReconnectContinueState arcContinue; HRESULT hr = E_FAIL; BOOL fContinueDisconnect = TRUE; CUI* pUI = NULL; BOOL fCoreAllowsArcContinue = FALSE; LONG maxArcConAttempts = MAX_ARC_CONNECTION_ATTEMPTS; DC_BEGIN_FN("OnNotifyDisconnected"); TRC_ASSERT(_pMsTscAx,(TB,_T("_pMsTscAx is not set"))); if (_pMsTscAx) { pUI = _pMsTscAx->GetCoreUI(); TRC_ASSERT(pUI,(TB,_T("pUI is not set"))); if (!(pUI->UI_IsCoreInitialized() && pUI->UI_CanAutoReconnect() && pUI->UI_GetEnableAutoReconnect())) { TRC_NRM((TB,_T("Skipping ARC core:%d canarc:%d arcenabled:%d"), pUI->UI_IsCoreInitialized(), pUI->UI_CanAutoReconnect(), pUI->UI_GetEnableAutoReconnect())); DC_QUIT; } maxArcConAttempts = pUI->UI_GetMaxArcAttempts(); // // 1. Make a policy decision based on the disconnect reason // as to whether or not we should try to autoreconnect // // // If this is a network error then try to autoreconnect // if (IsNetworkError(disconnectReason, exReasonCode)) { fShouldAutoReconnect = TRUE; } if (fShouldAutoReconnect) { TRC_NRM((TB,_T("Proceeding with autoreconnect"))); // // Reset continue flag on first attempt // if (0 == _nArcAttempts) { _fContinueArc = TRUE; } _nArcAttempts++; // // Default to automatic processing // arcContinue = autoReconnectContinueAutomatic; _fAutomaticArc = TRUE; // // 2.a) Fire the autoreconnection event to notify the core // pUI->UI_OnAutoReconnecting(disconnectReason, _nArcAttempts, maxArcConAttempts, &fCoreAllowsArcContinue); if (fCoreAllowsArcContinue) { // // 2.b) Fire the autoreconnection event to notify the container // hr = ((CProxy_IMsTscAxEvents*) _pMsTscAx)->Fire_AutoReconnecting( disconnectReason, _nArcAttempts, &arcContinue ); } else { // // Core said stop arc // TRC_NRM((TB,_T("Stopping arc in response to core request"))); hr = S_OK; arcContinue = autoReconnectContinueStop; } // // If the event processing succeeded or if the caller did nothing // with it as in E_NOTIMPL then carry on // if (SUCCEEDED(hr) || E_NOTIMPL == hr) { // // 3. Take action depending on what the container requested // switch (arcContinue) { case autoReconnectContinueAutomatic: { // // 1) wait Ns then try to connect // 2) if get disconnected and land back in here // allow increment and retry up to n attemps // if (_nArcAttempts <= maxArcConAttempts) { // // For first try don't do any waiting // but still dispatch thru the same deferred // message code path. The timercallback will // kick off a connection attempt // UINT nDelay = (1 == _nArcAttempts) ? 0 : ARC_RECONNECTION_DELAY; if (SetTimer(_pMsTscAx->GetHwnd(), (UINT_PTR)(this), nDelay, sArcTimerCallBackProc)) { fContinueDisconnect = FALSE; } else { fContinueDisconnect = TRUE; TRC_ERR((TB,_T("Arc settimer failed: 0x%x"), GetLastError())); } } else { TRC_NRM((TB, _T("Arc exceed con attempts: %d of %d"), _nArcAttempts, maxArcConAttempts)); fContinueDisconnect = TRUE; } } break; case autoReconnectContinueStop: { TRC_NRM((TB, _T("Container requested ARC continue stop"))); } break; case autoReconnectContinueManual: { // // Flag that this is no longer automatic // _fAutomaticArc = FALSE; // // Just return, the container will drive // the process by calling AutoReconnect // fContinueDisconnect = FALSE; } break; default: { TRC_ERR((TB,_T("Unknown arcContinue code: 0x%x"), arcContinue)); } break; } } else { TRC_ERR((TB,_T("Not arcing event ret hr: 0x%x"), hr)); } } } DC_EXIT_POINT: *pfContinueDisconnect = fContinueDisconnect; DC_END_FN(); } // // IsUserInitiatedDisconnect // Returns TRUE if the disconnection was initiated by the user // // Params: // disconnectReason - disconnection reason code // exReason - exteneded disconnection reason // BOOL CArcMgr::IsUserInitiatedDisconnect( LONG disconnectReason, ExtendedDisconnectReasonCode exReason ) { ULONG mainDiscReason; BOOL fIsUserInitiated = FALSE; DC_BEGIN_FN("IsUserInitiatedDisconnect"); // // REVIEW (nadima): mechanism to ensure new errors still work? // // // If this is a user-initiated disconnect, don't do a popup. // mainDiscReason = NL_GET_MAIN_REASON_CODE(disconnectReason); if (((disconnectReason != UI_MAKE_DISCONNECT_ERR(UI_ERR_NORMAL_DISCONNECT)) && (mainDiscReason != NL_DISCONNECT_REMOTE_BY_USER) && (mainDiscReason != NL_DISCONNECT_LOCAL)) || (exDiscReasonReplacedByOtherConnection == exReason)) { fIsUserInitiated = TRUE; } DC_END_FN(); return fIsUserInitiated; } BOOL CArcMgr::IsNetworkError( LONG disconnectReason, ExtendedDisconnectReasonCode exReason ) { BOOL fIsNetworkError = FALSE; ULONG mainReasonCode; DC_BEGIN_FN("IsNetworkError"); mainReasonCode = NL_GET_MAIN_REASON_CODE(disconnectReason); if (((mainReasonCode == NL_DISCONNECT_ERROR) || (UI_MAKE_DISCONNECT_ERR(UI_ERR_GHBNFAILED) == disconnectReason) || (UI_MAKE_DISCONNECT_ERR(UI_ERR_DNSLOOKUPFAILED) == disconnectReason)) && (exDiscReasonNoInfo == exReason)) { fIsNetworkError = TRUE; } DC_END_FN(); return fIsNetworkError; }