|
|
//*********************************************************************
//* Microsoft Windows **
//* Copyright(c) Microsoft Corp., 1995 **
//*********************************************************************
//
// DIALMON.C - Window proc for dial monitor app
//
// HISTORY:
//
// 4/18/95 jeremys Created.
//
#include "private.h"
#include <mluisupp.h>
#define TF_THISMODULE TF_DIALMON
//
// Registry keys we use to get autodial information
//
// Internet connection goes in remote access key
const TCHAR c_szRASKey[] = TEXT("RemoteAccess");
// Key name
const TCHAR c_szProfile[] = TEXT("InternetProfile"); const TCHAR c_szEnable[] = TEXT("EnableUnattended");
// registry keys of interest
const TCHAR c_szRegPathInternetSettings[] = REGSTR_PATH_INTERNET_SETTINGS; static const TCHAR szRegValEnableAutoDisconnect[] = REGSTR_VAL_ENABLEAUTODISCONNECT; static const TCHAR szRegValDisconnectIdleTime[] = REGSTR_VAL_DISCONNECTIDLETIME; static const TCHAR szRegValExitDisconnect[] = REGSTR_VAL_ENABLEEXITDISCONNECT; static const TCHAR szEllipsis[] = TEXT("..."); static const CHAR szDashes[] = "----"; static const TCHAR szAutodialMonitorClass[] = REGSTR_VAL_AUTODIAL_MONITORCLASSNAME; static const TCHAR c_szDialmonClass[] = TEXT("MS_WebcheckMonitor");
// Dialmon globals
UINT_PTR g_uDialmonSecTimerID = 0;
CDialMon * g_pDialMon = NULL;
// Function prototypes for dialog handling functions
INT_PTR CALLBACK DisconnectPromptDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL DisconnectDlgInit(HWND hDlg,DISCONNECTDLGINFO * pDisconnectDlgInfo); VOID DisconnectDlgCancel(HWND hDlg); VOID DisconnectDlgTimerProc(HWND hDlg); VOID DisconnectDlgDisableAutodisconnect(HWND hDlg); VOID DisconnectDlgShowCountdown(HWND hDlg,DWORD dwSecsRemaining); VOID EnableDisconnectDlgCtrls(HWND hDlg,BOOL fEnable); BOOL CenterWindow (HWND hwndChild, HWND hwndParent);
////////////////////////////////////////////////////////////////////////
// RAS delay load helpers
//
typedef DWORD (WINAPI* _RASSETAUTODIALPARAM) ( DWORD, LPVOID, DWORD );
typedef DWORD (WINAPI* _RASENUMCONNECTIONSA) ( LPRASCONNA, LPDWORD, LPDWORD );
typedef DWORD (WINAPI* _RASENUMCONNECTIONSW) ( LPRASCONNW, LPDWORD, LPDWORD );
typedef DWORD (WINAPI* _RASHANGUP) ( HRASCONN );
typedef struct _tagAPIMAPENTRY { FARPROC* pfn; LPSTR pszProc; } APIMAPENTRY;
static _RASSETAUTODIALPARAM pfnRasSetAutodialParam = NULL; static _RASENUMCONNECTIONSA pfnRasEnumConnectionsA = NULL; static _RASENUMCONNECTIONSW pfnRasEnumConnectionsW = NULL; static _RASHANGUP pfnRasHangUp = NULL;
static HINSTANCE g_hRasLib = NULL; static long g_lRasRefCnt = 0;
APIMAPENTRY rgRasApiMap[] = { { (FARPROC*) &pfnRasSetAutodialParam, "RasSetAutodialParamA" }, { (FARPROC*) &pfnRasEnumConnectionsA, "RasEnumConnectionsA" }, { (FARPROC*) &pfnRasEnumConnectionsW, "RasEnumConnectionsW" }, { (FARPROC*) &pfnRasHangUp, "RasHangUpA" }, { NULL, NULL }, };
/////////////////////////////////////////////////////////////////////////////
//
// RasEnumHelp
//
// Abstract grusome details of getting a correct enumeration of connections
// from RAS. Works on all 9x and NT platforms correctly, maintaining unicode
// whenever possible.
//
/////////////////////////////////////////////////////////////////////////////
class RasEnumHelp { private:
//
// Possible ways we got info from RAS
//
typedef enum { ENUM_MULTIBYTE, // Win9x
ENUM_UNICODE, // NT
} ENUM_TYPE;
//
// How we got the info
//
ENUM_TYPE _EnumType;
//
// Any error we got during enumeration
//
DWORD _dwLastError;
//
// Number of entries we got
//
DWORD _dwEntries;
//
// Pointer to info retrieved from RAS
//
RASCONNW * _rcList;
//
// Last entry returned as multibyte or unicode when conversion required
//
RASCONNW _rcCurrentEntryW;
public: RasEnumHelp(); ~RasEnumHelp();
DWORD GetError(); DWORD GetEntryCount(); LPRASCONNW GetEntryW(DWORD dwEntry); };
RasEnumHelp::RasEnumHelp() { DWORD dwBufSize, dwStructSize;
// init
_dwEntries = 0; _dwLastError = 0;
// figure out which kind of enumeration we're doing - start with multibyte
_EnumType = ENUM_MULTIBYTE; dwStructSize = sizeof(RASCONNA);
if (g_fIsWinNT) { _EnumType = ENUM_UNICODE; dwStructSize = sizeof(RASCONNW); }
// allocate space for 16 entries
dwBufSize = 16 * dwStructSize; _rcList = (LPRASCONNW)LocalAlloc(LMEM_FIXED, dwBufSize); if(_rcList) { do { // set up list
_rcList[0].dwSize = dwStructSize;
// call ras to enumerate
_dwLastError = ERROR_UNKNOWN; if(ENUM_MULTIBYTE == _EnumType) { if(pfnRasEnumConnectionsA) { _dwLastError = pfnRasEnumConnectionsA( (LPRASCONNA)_rcList, &dwBufSize, &_dwEntries ); } } else { if(pfnRasEnumConnectionsW) { _dwLastError = pfnRasEnumConnectionsW( _rcList, &dwBufSize, &_dwEntries ); } } // reallocate buffer if necessary
if(ERROR_BUFFER_TOO_SMALL == _dwLastError) { LocalFree(_rcList); _rcList = (LPRASCONNW)LocalAlloc(LMEM_FIXED, dwBufSize); if(NULL == _rcList) { _dwLastError = ERROR_NOT_ENOUGH_MEMORY; break; } } else { break; }
} while(TRUE); } else { _dwLastError = ERROR_NOT_ENOUGH_MEMORY; }
if(_rcList && (ERROR_SUCCESS != _dwLastError)) { LocalFree(_rcList); _rcList = NULL; _dwEntries = 0; }
return; }
RasEnumHelp::~RasEnumHelp() { if(_rcList) { LocalFree(_rcList); } }
DWORD RasEnumHelp::GetError() { return _dwLastError; }
DWORD RasEnumHelp::GetEntryCount() { return _dwEntries; }
LPRASCONNW RasEnumHelp::GetEntryW(DWORD dwEntryNum) { LPRASCONNW prc = NULL;
if(dwEntryNum < _dwEntries) { _rcCurrentEntryW.hrasconn = _rcList[dwEntryNum].hrasconn;
switch(_EnumType) { case ENUM_MULTIBYTE: { MultiByteToWideChar(CP_ACP, 0, ((LPRASCONNA)_rcList)[dwEntryNum].szEntryName, -1, _rcCurrentEntryW.szEntryName, ARRAYSIZE(_rcCurrentEntryW.szEntryName)); } break;
case ENUM_UNICODE: { StrCpyNW(_rcCurrentEntryW.szEntryName, _rcList[dwEntryNum].szEntryName, ARRAYSIZE(_rcCurrentEntryW.szEntryName)); } break; }
prc = &_rcCurrentEntryW; }
return prc; }
//
// Functions we can call once ras is loaded
//
DWORD _RasSetAutodialParam(DWORD dwKey, LPVOID lpvValue, DWORD dwcbValue) { if (pfnRasSetAutodialParam == NULL) return ERROR_UNKNOWN;
return (*pfnRasSetAutodialParam)(dwKey, lpvValue, dwcbValue); }
DWORD _RasEnumConnections(LPRASCONNW lpRasConn, LPDWORD lpdwSize, LPDWORD lpdwConn) { RasEnumHelp reh; DWORD dwRet = reh.GetError();
if (ERROR_SUCCESS == dwRet) { DWORD cItems = reh.GetEntryCount(); DWORD cbNeeded = cItems * sizeof(RASCONNW);
*lpdwConn = 0;
if (*lpdwSize >= cbNeeded) {
*lpdwConn = cItems;
DWORD dw;
for (dw = 0; dw < cItems; dw++) { LPRASCONNW prc = reh.GetEntryW(dw);
ASSERT(prc != NULL);
lpRasConn[dw].hrasconn = prc->hrasconn; StrCpyNW(lpRasConn[dw].szEntryName, prc->szEntryName, ARRAYSIZE(lpRasConn[dw].szEntryName)); } } else { dwRet = ERROR_BUFFER_TOO_SMALL; }
*lpdwSize = cbNeeded; }
return dwRet; }
DWORD _RasHangUp(HRASCONN hRasConn) { if (pfnRasHangUp == NULL) return ERROR_UNKNOWN;
return (*pfnRasHangUp)(hRasConn); }
BOOL LoadRasDll(void) { if(NULL == g_hRasLib) { g_hRasLib = LoadLibrary(TEXT("RASAPI32.DLL"));
if(NULL == g_hRasLib) return FALSE;
int nIndex = 0; while (rgRasApiMap[nIndex].pszProc != NULL) { *rgRasApiMap[nIndex].pfn = GetProcAddress(g_hRasLib, rgRasApiMap[nIndex].pszProc); // GetProcAddress will fail on Win95 for a couple of NT only apis.
// ASSERT(*rgRasApiMap[nIndex].pfn != NULL);
nIndex++; } }
if(g_hRasLib) { return TRUE; }
return FALSE; }
void UnloadRasDll(void) { if(g_hRasLib) { FreeLibrary(g_hRasLib); g_hRasLib = NULL; int nIndex = 0; while (rgRasApiMap[nIndex].pszProc != NULL) { *rgRasApiMap[nIndex].pfn = NULL; nIndex++; } } }
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// CDialmonClients
//
// Class to maintain a list of application Windows using Dialmon.
// Used for auto-timeout for all these applications.
// This class supports
// adding hooks to incoming applications,
// removing hooks for exiting applications,
// some aggregate operations on all clients.
//
// This is a singleton class, and needs to support only serialized access
// because all access is thru Dialmon which has a serialized message queue.
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
class CDialmonClients { private:
HWND pHwndArray[MAX_DIALMON_HANDLES]; int cCount; static CDialmonClients* pSingleton;
public:
CDialmonClients(); ~CDialmonClients(){}
static CDialmonClients* getSingleton() { if( NULL == pSingleton ) return ( pSingleton = new CDialmonClients() ); else return pSingleton; }
static void Shutdown() { if( pSingleton ) delete pSingleton; } void ClearAll(void); BOOL AddHook( HWND hWnd ); BOOL RemoveHook( HWND hWnd ); BOOL HasEntries() { return (cCount != 0); };
friend void BroadcastCanHangup( int iTimeoutMins ); friend void BroadcastHangingUp( void ); friend void OnConfirmHangup( HWND hWnd ); friend void OnDenyHangup( HWND hWnd, CDialMon* pDialMon ); friend BOOL CanHangup( void ); //doesn't have to be a friend - just bunched it together for now.
void DebugPrint( void ); };
CDialmonClients* CDialmonClients::pSingleton;
CDialmonClients::CDialmonClients():cCount(0) { for( int i=0; i<MAX_DIALMON_HANDLES; i++ ) pHwndArray[i] = NULL; } void CDialmonClients::ClearAll(void) { for( int i=0; i<MAX_DIALMON_HANDLES; i++ ) pHwndArray[i] = NULL; }
// client app. passes a handle to its messaging window when it starts up.
BOOL CDialmonClients::AddHook( HWND hWnd ) { DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: Add Hook\n") );
if( cCount >= MAX_DIALMON_HANDLES ) return false; /* BAD! */
pHwndArray[cCount++] = hWnd;
#ifdef DEBUG
DebugPrint(); #endif
return true; }
// client app. unhooks the handle from the CDialmonClients.
//IMPL: cCount always points to the next empty entry in the array.
// so when we delete a handle, we move all the entries beyond that
// handle up one place and decrement cCount.
BOOL CDialmonClients::RemoveHook( HWND hWnd ) { DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: Remove Hook\n") );
boolean found = false; int i; for( i=0; i<cCount; i++ ) { if( hWnd == pHwndArray[i] ) { pHwndArray[i] = NULL; --cCount; found = true; break; } } // move everything beyong cCount up by 1
// so that cCount represents next free index to
// insert into.
if( found ) { for( ; i<cCount; i++ ) pHwndArray[i] = pHwndArray[i+1]; pHwndArray[cCount] = NULL; } #ifdef DEBUG
DebugPrint(); #endif
return found; }
void CDialmonClients::DebugPrint(void) { DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: ClientList->\n") );
for( int i=0; i<cCount; i++ ) { if( pHwndArray[i] ) { DebugMsg( DM_TRACE, TEXT("\t\t%d: %x\n"), i, pHwndArray[i] ); } } }
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Start up and Shutdown CDialmonClients
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
CDialmonClients* g_pDialmonClients;
static void DialmonClientsInit(void) { g_pDialmonClients = new CDialmonClients(); }
static void DialmonClientsShutdown(void) { delete g_pDialmonClients; }
//#define USE_CONFIRM_ARRAY 1
// We don't need to use the pConfirmHangupArray.
// The option is to simply wait for some preset time, and if no registered
// client refuses the hangup option, to go ahead and simply hangup.
// This array lets us hangup a little earlier just in case all clients reply
// immdly.
#ifdef USE_CONFIRM_ARRAY
static BOOL pConfirmHangupArray[MAX_DIALMON_HANDLES]; #endif
static int cOutstandingReplies = 0;
// Broadcast to all registered client a query of can_hang_up?
void BroadcastCanHangup( int iTimeoutMins ) { DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: Broadcasting WM_CANHANGUP?") ); int i; cOutstandingReplies=0; for( i=0; i<g_pDialmonClients->cCount; i++ ) { if(PostMessage( g_pDialmonClients->pHwndArray[i], WM_CANHANGUP, 0, (LPARAM)iTimeoutMins )) ++cOutstandingReplies; }
// if we are using a confirm boolean array, then set ALL
// entries to false.. this is to take care of any clients that come
// in AFTER we broadcast can hangup and BEFORE all the existing clients
// have finished confirming.
#ifdef USE_CONFIRM_ARRAY
for( i=0; i<MAX_DIALMON_HANDLES; i++ ) { pConfirmHangupArray[i] = false; } #endif
}
// This is a broadcast AFTER hangingup to all registered clients.
void BroadcastHangingUp(void) {
DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: Broadcasting WM_HANGING_UP") ); for( int i=0; i<g_pDialmonClients->cCount; i++ ) PostMessage( g_pDialmonClients->pHwndArray[i], WM_HANGING_UP, 0,0 ); }
// Record that this particular client would like to hangup.
void OnConfirmHangup( HWND hWnd ) { #ifdef USE_CONFIRM_ARRAY
for( int i=0; i<g_pDialmonClients->cCount; i++ ) if( hWnd == g_pDialmonClients->pHwndArray[i] ) { pConfirmHangupArray[i] = true; break; } #endif
--cOutstandingReplies; }
// Checks if all clients have replied positively.
// Returns false if any client has not yet replied.
BOOL CanHangup( void ) { #ifdef USE_CONFIRM_ARRAY
for( int i=0; i<g_pDialmonClients->cCount; i++ ) if( false == pConfirmHangupArray[i] ) return false; return true; #else
return (cOutstandingReplies==0); #endif
}
// take action to abort hangup, ( set CDialmon::_dwElapsedTicks to 0 ).
// The effect of this would be that after another timeout period, dialmon
// would again query all the clients.
// However, the client does not have to bring up a dialog again if the user
// indicates that he is not interested in the feature.. the client can
// negate the hangup without interrupting the user.
// ( see the auto-disconnect feature in dialmon ).
void OnDenyHangup( HWND hWnd, CDialMon* pDialMon ) { #ifdef USE_CONFIRM_ARRAY
for( int i=0; i<g_pDialmonClients->cCount; i++ ) if( hWnd == g_pDialmonClients->pHwndArray[i] ) { pConfirmHangupArray[i] = false; break; } #endif
pDialMon->ResetElapsedTicks(); cOutstandingReplies=0; }
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Helper to tell dialmon something is going on
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
void IndicateDialmonActivity(void) { static HWND hwndDialmon = NULL; HWND hwndMonitor;
// this one is dynamic - have to find window every time
hwndMonitor = FindWindow(szAutodialMonitorClass, NULL); if(hwndMonitor) PostMessage(hwndMonitor, WM_WINSOCK_ACTIVITY, 0, 0);
// dialmon lives forever - find it once and we're set
if(NULL == hwndDialmon) hwndDialmon = FindWindow(c_szDialmonClass, NULL); if(hwndDialmon) PostMessage(hwndDialmon, WM_WINSOCK_ACTIVITY, 0, 0); }
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Dialmon startup and shutdown
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
BOOL DialmonInit(void) { g_pDialMon = new CDialMon;
DialmonClientsInit(); if(g_pDialMon) return TRUE;
return FALSE; }
void DialmonShutdown(void) { DialmonClientsShutdown(); SAFEDELETE(g_pDialMon); }
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Dialmon window functions
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// this cwebcheck instance is in iwebck.cpp
extern CWebCheck *g_pwc;
#ifdef DEBUG_KV
// DEBUG_KV is set to 1 if you need to test hangup logic
// without actually having a dailup connection.
static bool kvhack = true; #endif
LRESULT CALLBACK Dialmon_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CDialMon *pDialMon = (CDialMon*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch (uMsg) { case WM_CREATE: { // snag our class pointer and save in window data
CREATESTRUCT *pcs; pcs = (CREATESTRUCT *)lParam; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) pcs->lpCreateParams); break; }
// DialMon messages (starting at WM_USER+100)
case WM_SET_CONNECTOID_NAME: if(pDialMon) pDialMon->OnSetConnectoid(wParam!=0); break; case WM_WINSOCK_ACTIVITY: if(pDialMon) pDialMon->OnActivity(); break; case WM_IEXPLORER_EXITING: if(pDialMon) pDialMon->OnExplorerExit(); break; case WM_DIALMON_HOOK: DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: WM_HOOK recd. from Window 0x%x"), lParam );
BOOL fRetval; fRetval = g_pDialmonClients->AddHook( (HWND)lParam ); ASSERT( fRetval == TRUE ); #ifdef DEBUG_KV
if( kvhack == true ) { pDialMon->kvStartMonitoring(); kvhack = false; } #endif
break;
case WM_DIALMON_UNHOOK: DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: WM_UNHOOK recd. from Window 0x%x"), lParam );
g_pDialmonClients->RemoveHook( (HWND)lParam ); break;
case WM_CONFIRM_HANGUP: DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: WM_CONFIRM_HANGUP recd. from Window 0x%x"), lParam );
OnConfirmHangup( (HWND)lParam ); break;
case WM_DENY_HANGUP: DebugMsg( DM_TRACE, TEXT("\tCDIALMONCLIENTS: WM_DENY_HANGUP recd. from Window 0x%x"), lParam );
OnDenyHangup( (HWND)lParam, pDialMon ); break; case WM_TIMER: if(pDialMon) pDialMon->OnTimer(wParam); break; case WM_LOAD_SENSLCE: DBG("Dialmon_WndProc - got WM_LOAD_SENSLCE"); if(g_pwc) { g_pwc->LoadExternals(); } break; case WM_IS_SENSLCE_LOADED: if(g_pwc) { return g_pwc->AreExternalsLoaded(); } else { return FALSE; } break; case WM_WININICHANGE: if (lParam && !StrCmpI((LPCTSTR)lParam, TEXT("policy"))) { ProcessInfodeliveryPolicies(); } // FEATURE: This should be done on Policy and another filter, not for
// all changes. (The other filter hasn't been defined yet.)
// TODO: handle this in the new architecture!
//SetNotificationMgrRestrictions(NULL);
break;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam); }
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// CDialMon class implementation
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Constructor / Destructor
//
CDialMon::CDialMon() { WNDCLASS wc;
// register dialmon window class
memset(&wc, 0, sizeof(wc)); wc.lpfnWndProc = Dialmon_WndProc; wc.hInstance = g_hInst; wc.lpszClassName = c_szDialmonClass; RegisterClass(&wc);
// create dialmon window
_hwndDialmon = CreateWindow(c_szDialmonClass, c_szDialmonClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hInst, (LPVOID)this); }
CDialMon::~CDialMon() { if(_hwndDialmon) DestroyWindow(_hwndDialmon);
// unload ras if it's still around
UnloadRasDll(); }
///////////////////////////////////////////////////////////////////////////
//
// Start/StopMonitoring
//
///////////////////////////////////////////////////////////////////////////
BOOL CDialMon::StartMonitoring(void) { DBG("CDialMon::StartMonitoring");
// read timeout settings from registry
RefreshTimeoutSettings();
// set a one-minute timer
StopIdleTimer(); if(!StartIdleTimer()) return FALSE;
_dwElapsedTicks = 0; return TRUE; }
void CDialMon::StopMonitoring(void) { DBG("CDialMon::StopMonitoring");
// don't ever hang up now but keep an eye on ras connection
_dwTimeoutMins = 0; _fDisconnectOnExit = FALSE; }
///////////////////////////////////////////////////////////////////////////
//
// Start/StopIdleTimer, OnTimer
//
///////////////////////////////////////////////////////////////////////////
INT_PTR CDialMon::StartIdleTimer(void) { if(0 == _uIdleTimerID) _uIdleTimerID = SetTimer(_hwndDialmon, TIMER_ID_DIALMON_IDLE, 30000, NULL);
ASSERT(_uIdleTimerID);
return _uIdleTimerID; }
void CDialMon::StopIdleTimer(void) { if(_uIdleTimerID) { KillTimer(_hwndDialmon, _uIdleTimerID); _uIdleTimerID = 0; } }
void CDialMon::OnTimer(UINT_PTR uTimerID) { DBG("CDialMon::OnMonitorTimer");
// if we're on Millennium, just bail out. The system handles idle disconnect.
if(g_fIsMillennium) { return; }
// if it's not our timer, ignore it
if(uTimerID != _uIdleTimerID) return;
// prevent re-entrancy of timer proc (we can stay in here indefinitely
// since we may bring up a dialog box)
if (_fInDisconnectFunction) { // disconnect dialog already launched, ignore timer ticks while
// it's present
return; }
_fInDisconnectFunction = TRUE; CheckForDisconnect(TRUE); _fInDisconnectFunction = FALSE;
#ifdef DEBUG_KV
/* Don't stop idle timer */ #else
if(FALSE == _fConnected) { StopIdleTimer(); } #endif
}
///////////////////////////////////////////////////////////////////////////
//
// OnSetConnectoid/OnActivity/OnExplorerExit
//
///////////////////////////////////////////////////////////////////////////
void CDialMon::OnSetConnectoid(BOOL fNoTimeout) { RASCONN RasCon[MAX_CONNECTION]; DWORD dwBytes, dwRes, dwConnections;
// save no timeout setting
_fNoTimeout = fNoTimeout;
// Ask ras which connectoid is connected and watch that one
LoadRasDll(); RasCon[0].dwSize = sizeof(RasCon[0]); dwBytes = MAX_CONNECTION * sizeof(RasCon[0]); dwRes = _RasEnumConnections(RasCon, &dwBytes, &dwConnections); // No connections? bail.
if(0 == dwConnections) { *_pszConnectoidName = TEXT('\0'); _fConnected = FALSE; return; }
// Monitor first connectoid
StrCpyN(_pszConnectoidName, RasCon[0].szEntryName, ARRAYSIZE(_pszConnectoidName));
// send ras connect notification if we weren't previously connected
if(FALSE == _fConnected) { _fConnected = TRUE; }
// start watching it
StartMonitoring(); }
void CDialMon::OnActivity(void) { DBG("CDialMon::OnActivity");
// reset idle tick count
_dwElapsedTicks = 0;
// if the disconnect dialog is present and winsock activity
// resumes, then dismiss the dialog
if(_hDisconnectDlg) { SendMessage(_hDisconnectDlg, WM_QUIT_DISCONNECT_DLG, 0, 0); _hDisconnectDlg = NULL; } }
void CDialMon::OnExplorerExit() { DBG("CDialMon::OnIExplorerExit");
if(FALSE == _fDisconnectOnExit && FALSE == _fNoTimeout) { // no exit disconnection so bail
DBG("CDialMon::OnIExplorerExit - exit hangup not enabled"); return; }
// prevent re-entrancy of this function (we can stay in here indefinitely
// since we may bring up a dialog box)
if (_fInDisconnectFunction) { // some UI already launched
return; }
_fInDisconnectFunction = TRUE; CheckForDisconnect(FALSE); _fInDisconnectFunction = FALSE;
if(FALSE == _fConnected) { StopIdleTimer(); } }
///////////////////////////////////////////////////////////////////////////
//
// RefreshTimeoutSettings
//
///////////////////////////////////////////////////////////////////////////
BOOL CDialMon::RefreshTimeoutSettings(void) { HKEY hKey; BOOL fSuccess = FALSE; TCHAR szKey[MAX_PATH]; DWORD dwRes, dwData, dwSize, dwDisp;
// assume disconnect monitoring is off
_dwTimeoutMins = 0; _fDisconnectOnExit = FALSE;
// figure out appropriate key
wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\Profile\\%s"), REGSTR_PATH_REMOTEACCESS, _pszConnectoidName);
// open a regstry key to the internet settings section
dwRes = RegCreateKeyEx(HKEY_CURRENT_USER, szKey, 0, TEXT(""), 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hKey, &dwDisp); if(ERROR_SUCCESS == dwRes) { //
// is autodisconnect enabled?
//
dwSize = sizeof(DWORD); if (RegQueryValueEx(hKey,szRegValEnableAutoDisconnect,NULL,NULL, (LPBYTE) &dwData,&dwSize) == ERROR_SUCCESS) { if(dwData) { // what's the timeout?
dwSize = sizeof(DWORD); if (RegQueryValueEx(hKey,szRegValDisconnectIdleTime,NULL,NULL, (LPBYTE) &dwData,&dwSize) == ERROR_SUCCESS && dwData) { _dwTimeoutMins = dwData; fSuccess = TRUE; } }
// is disconnect on exit enabled?
dwSize = sizeof(DWORD); if (RegQueryValueEx(hKey,szRegValExitDisconnect,NULL,NULL, (LPBYTE) &dwData,&dwSize) == ERROR_SUCCESS && dwData) { _fDisconnectOnExit = TRUE; fSuccess = TRUE; } } else { //
// couldn't find enable autodisconnect key. Set all disconnect
// settings to their defaults
//
// set class members to default values
_dwTimeoutMins = 20; _fDisconnectOnExit = TRUE; fSuccess = TRUE;
// enable idle disconnect and exit disconnect
dwData = 1; RegSetValueEx(hKey, szRegValEnableAutoDisconnect, 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD)); RegSetValueEx(hKey, szRegValExitDisconnect, 0, REG_DWORD, (LPBYTE)&dwData, sizeof(DWORD));
// Save idle minutes
RegSetValueEx(hKey, szRegValDisconnectIdleTime, 0, REG_DWORD, (LPBYTE)&_dwTimeoutMins, sizeof(DWORD)); }
RegCloseKey(hKey); }
return fSuccess; }
///////////////////////////////////////////////////////////////////////////
//
// Disconnection handling
//
///////////////////////////////////////////////////////////////////////////
void CDialMon::CheckForDisconnect(BOOL fTimer) { BOOL fPromptForDisconnect = TRUE; // assume we should prompt for disconnect
BOOL fDisconnectDisabled = FALSE; BOOL fConnectoidAlive = FALSE; RASCONN RasCon[MAX_CONNECTION]; DWORD dwBytes, dwRes, dwConnections = 0, i; HRASCONN hConnection = NULL;
// variables for auto-hangup for other client apps. (MARS)
static int dwElapsedTicksSincePoll = 0; static BOOL fPolledForHangup = false; BOOL fClientsOkHangup = false; #define MAX_MINS_CLIENT_RESPONSE 1
#ifdef DEBUG_KV
// skip all the connection code..
goto KVHACK; #endif
// Verify we still have a connection
RasCon[0].dwSize = sizeof(RasCon[0]); dwBytes = MAX_CONNECTION * sizeof(RasCon[0]); dwRes = _RasEnumConnections(RasCon, &dwBytes, &dwConnections); // If ras is connected at all, stay alive to monitor it
if(0 == dwConnections) _fConnected = FALSE;
// Find connectoid we're supposed to watch
if(TEXT('\0') == *_pszConnectoidName) { DBG_WARN("DisconnectHandler: No designated connection to monitor"); return; } for(i=0; i<dwConnections; i++) { if(!StrCmp(RasCon[i].szEntryName, _pszConnectoidName)) { fConnectoidAlive = TRUE; hConnection = RasCon[i].hrasconn; } }
// if we're not connected to out monitor connectoid, ditch our hangup
// dialog if we have one and bail out
if(FALSE == fConnectoidAlive) { if(_hDisconnectDlg) { SendMessage(_hDisconnectDlg, WM_QUIT_DISCONNECT_DLG, 0, 0); _hDisconnectDlg = NULL; }
// Also make sure that if we were waiting for client (MARS) reponses for auto-hangup,
// we also clean up state information..
if( fPolledForHangup ) { dwElapsedTicksSincePoll = 0; fPolledForHangup = false; } return; }
#ifdef DEBUG_KV
// label to jump to after skipping connection code..
// also need to set _dwTimeoutMins since without connection
KVHACK:_dwTimeoutMins = 2; #endif
// Check timeout if we got a timer tick
if(fTimer) { // increment tick count
_dwElapsedTicks ++;
// Haven't exceeded idle threshold or not watching for idle
if (0 == _dwTimeoutMins || _dwElapsedTicks < _dwTimeoutMins * 2) fPromptForDisconnect = FALSE; }
// THIS is a good place to message out to other clients (ie Mars ) and
// see if everybody wants to hang up ( this is the point where if
// fPromptForDisconnect is true, then the earlier behavior would have
// prompted for disconnect.
// If this is a disconnect because of IExplorer exiting, we
// probably don't want to hangup if there are other clients using dialmon.
if( !fTimer && g_pDialmonClients->HasEntries() ) return; if( g_pDialmonClients->HasEntries() && fPromptForDisconnect ) { if( fPolledForHangup ) { // WM_CANHANGUP messages have been sent
// we can hangup if either all clients have replied yes,
// or if there are no refusals so far, and time has run out.
if( CanHangup() || ( dwElapsedTicksSincePoll >= 2*MAX_MINS_CLIENT_RESPONSE ) ) { //can hangup!
dwElapsedTicksSincePoll = 0; fPolledForHangup = false; fClientsOkHangup = true; } else { dwElapsedTicksSincePoll++; //ensure that hangup doesn't occur on THIS particular timer message.
fPromptForDisconnect = false; } } else { // WM_CANHANGUP queries may be sent out now..
BroadcastCanHangup( _dwTimeoutMins ); dwElapsedTicksSincePoll = 0; fPolledForHangup = true; //ensure that hangup doesn't occur now
fPromptForDisconnect = false; } } else if( fPolledForHangup ) { // activity restarted while waiting for client responses.
// do clean up of state information.
dwElapsedTicksSincePoll = 0; fPolledForHangup = false; } if(FALSE == fPromptForDisconnect) { return; }
// prompt user to see if they want to hang up
if(fClientsOkHangup || PromptForDisconnect(fTimer, &fDisconnectDisabled)) { // hang it up
ASSERT(hConnection); if(hConnection) _RasHangUp(hConnection);
// broadcast WM_HANGING_UP to remaining clients - REQUIRED??
if( g_pDialmonClients->HasEntries() ) BroadcastHangingUp(); _fConnected = FALSE; }
if (fDisconnectDisabled) { StopMonitoring(); }
_dwElapsedTicks = 0; }
BOOL CDialMon::PromptForDisconnect(BOOL fTimer, BOOL *pfDisconnectDisabled) { ASSERT(_pszConnectoidName); ASSERT(pfDisconnectDisabled);
// fill out struct to pass to dialog
DISCONNECTDLGINFO DisconnectDlgInfo; memset(&DisconnectDlgInfo,0,sizeof(DisconnectDlgInfo)); DisconnectDlgInfo.pszConnectoidName = _pszConnectoidName; DisconnectDlgInfo.fTimer = fTimer; DisconnectDlgInfo.dwTimeout = _dwTimeoutMins; DisconnectDlgInfo.pDialMon = this;
// choose the appropriate dialog depending on if this a "timeout" dialog
// or "app exiting" dialog
UINT uDlgTemplateID = fTimer ? IDD_DISCONNECT_PROMPT:IDD_APP_EXIT_PROMPT;
// run the dialog
BOOL fRet = (BOOL)DialogBoxParam(MLGetHinst(),MAKEINTRESOURCE(uDlgTemplateID), NULL, DisconnectPromptDlgProc,(LPARAM) &DisconnectDlgInfo);
// dialog box stores its window handle in our class so we can send
// messages to it, clear the global handle now that it's dismissed
_hDisconnectDlg = NULL;
*pfDisconnectDisabled = FALSE; if (!fRet && DisconnectDlgInfo.fDisconnectDisabled) { *pfDisconnectDisabled=TRUE;
// turn off reg keys for this connection
TCHAR szKey[128]; DWORD dwRes, dwValue = 0; HKEY hKey;
wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("%s\\Profile\\%s"), REGSTR_PATH_REMOTEACCESS, _pszConnectoidName); dwRes = RegOpenKey(HKEY_CURRENT_USER, szKey, &hKey); if(ERROR_SUCCESS == dwRes) {
// Turn off idle disconnect
RegSetValueEx(hKey, szRegValEnableAutoDisconnect, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD));
// Turn off exit disconnect
RegSetValueEx(hKey, szRegValExitDisconnect, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD));
RegCloseKey(hKey); } }
return fRet; }
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Disconnect dialog implementation
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
INT_PTR CALLBACK DisconnectPromptDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: // lParam points to data struct, store a pointer to it in window data
SetWindowLongPtr(hDlg, DWLP_USER,lParam); return DisconnectDlgInit(hDlg,(DISCONNECTDLGINFO *) lParam); break; case WM_COMMAND: switch (wParam) { case IDOK: EndDialog(hDlg,TRUE); break; case IDCANCEL: DisconnectDlgCancel(hDlg); EndDialog(hDlg,FALSE); break; case IDC_DISABLE_AUTODISCONNECT: DisconnectDlgDisableAutodisconnect(hDlg); break; } break; case WM_QUIT_DISCONNECT_DLG: // parent window wants to terminate us
EndDialog(hDlg,FALSE); break; case WM_TIMER: DisconnectDlgTimerProc(hDlg); break; }
return FALSE; }
BOOL __cdecl _FormatMessage(LPCWSTR szTemplate, LPWSTR szBuf, UINT cchBuf, ...) { BOOL fRet; va_list ArgList; va_start(ArgList, cchBuf);
fRet = FormatMessageWrapW(FORMAT_MESSAGE_FROM_STRING, szTemplate, 0, 0, szBuf, cchBuf, &ArgList);
va_end(ArgList); return fRet; }
BOOL DisconnectDlgInit(HWND hDlg,DISCONNECTDLGINFO * pDisconnectDlgInfo) { ASSERT(pDisconnectDlgInfo); if (!pDisconnectDlgInfo) return FALSE;
// allocate buffers to build text for dialog
BUFFER BufText(MAX_RES_LEN + MAX_CONNECTOID_DISPLAY_LEN + 1); BUFFER BufFmt(MAX_RES_LEN),BufConnectoidName(MAX_CONNECTOID_DISPLAY_LEN+4); ASSERT(BufText && BufFmt && BufConnectoidName); if (!BufText || !BufFmt || !BufConnectoidName) return FALSE;
UINT uStringID; // choose the appropriate text string for dialog
if (pDisconnectDlgInfo->fTimer) { uStringID = IDS_DISCONNECT_DLG_TEXT; } else { uStringID = IDS_APP_EXIT_TEXT; }
// load the format string from resource
MLLoadString(uStringID,BufFmt.QueryPtr(),BufFmt.QuerySize());
// copy the connectoid name into buffer, and truncate it if it's really
// long
StrCpyN(BufConnectoidName.QueryPtr(),pDisconnectDlgInfo->pszConnectoidName, BufConnectoidName.QuerySize()); if (lstrlen(pDisconnectDlgInfo->pszConnectoidName) > MAX_CONNECTOID_DISPLAY_LEN) { StrCpyN(((TCHAR *) BufConnectoidName.QueryPtr()) + MAX_CONNECTOID_DISPLAY_LEN, szEllipsis, BufConnectoidName.QuerySize()); }
if (pDisconnectDlgInfo->fTimer) { _FormatMessage(BufFmt.QueryPtr(), BufText.QueryPtr(), BufText.QuerySize(), BufConnectoidName.QueryPtr(), pDisconnectDlgInfo->dwTimeout); } else { _FormatMessage(BufFmt.QueryPtr(), BufText.QueryPtr(), BufText.QuerySize(), BufConnectoidName.QueryPtr()); }
// set text in dialog
SetDlgItemText(hDlg,IDC_TX1,BufText.QueryPtr());
// if this timeout dialog (which counts down), initialize countdown timer
if (pDisconnectDlgInfo->fTimer) { pDisconnectDlgInfo->dwCountdownVal = DISCONNECT_DLG_COUNTDOWN;
DisconnectDlgShowCountdown(hDlg,pDisconnectDlgInfo->dwCountdownVal);
// set a one-second timer
g_uDialmonSecTimerID = SetTimer(hDlg,TIMER_ID_DIALMON_SEC,1000,NULL); ASSERT(g_uDialmonSecTimerID); if (!g_uDialmonSecTimerID) { // it's very unlikely that setting the timer will fail... but if it
// does, then we'll act just like a normal dialog and won't have
// a countdown. Hide the countdown-related windows...
ShowWindow(GetDlgItem(hDlg,IDC_TX2),SW_HIDE); ShowWindow(GetDlgItem(hDlg,IDC_GRP),SW_HIDE); ShowWindow(GetDlgItem(hDlg,IDC_TIME_REMAINING),SW_HIDE); ShowWindow(GetDlgItem(hDlg,IDC_TX3),SW_HIDE); }
// beep to alert user
MessageBeep(MB_ICONEXCLAMATION); }
// center this dialog on the screen
CenterWindow(hDlg,GetDesktopWindow());
// default: assume user does not disable auto disconnect, change
// this later if they do (this field is output to dialog invoker)
pDisconnectDlgInfo->fDisconnectDisabled = FALSE;
// Save dialog handle so we can get quit messages
pDisconnectDlgInfo->pDialMon->_hDisconnectDlg = hDlg; return TRUE; }
VOID DisconnectDlgCancel(HWND hDlg) { // get pointer to data struct out of window data
DISCONNECTDLGINFO * pDisconnectDlgInfo = (DISCONNECTDLGINFO *) GetWindowLongPtr(hDlg, DWLP_USER); ASSERT(pDisconnectDlgInfo);
// check to see if user checked 'disable autodisconnect' checkbox
if(IsDlgButtonChecked(hDlg,IDC_DISABLE_AUTODISCONNECT)) { // set the output field to indicate that user wanted to disable
// auto disconnect
pDisconnectDlgInfo->fDisconnectDisabled = TRUE; } }
VOID DisconnectDlgTimerProc(HWND hDlg) { // ignore timer ticks (e.g. hold countdown) if "disable autodisconnect"
// checkbox is checked
if (IsDlgButtonChecked(hDlg,IDC_DISABLE_AUTODISCONNECT)) return;
// get pointer to data struct out of window data
DISCONNECTDLGINFO * pDisconnectDlgInfo = (DISCONNECTDLGINFO *) GetWindowLongPtr(hDlg, DWLP_USER); ASSERT(pDisconnectDlgInfo); if (!pDisconnectDlgInfo) return;
if (pDisconnectDlgInfo->dwCountdownVal) { // decrement countdown value
pDisconnectDlgInfo->dwCountdownVal --;
// update the dialog with the new value
if (pDisconnectDlgInfo->dwCountdownVal) { DisconnectDlgShowCountdown(hDlg,pDisconnectDlgInfo->dwCountdownVal); return; } }
// countdown has run out!
// kill the timer
KillTimer(hDlg,g_uDialmonSecTimerID); g_uDialmonSecTimerID = 0;
// send a 'OK' message to the dialog to dismiss it
SendMessage(hDlg,WM_COMMAND,IDOK,0); }
VOID DisconnectDlgShowCountdown(HWND hDlg,DWORD dwSecsRemaining) { // build a string showing the number of seconds left
CHAR szSecs[10]; if (dwSecsRemaining == (DWORD) -1) { lstrcpyA(szSecs, szDashes); } else { wnsprintfA(szSecs, ARRAYSIZE(szSecs), "%lu", dwSecsRemaining); }
// set string in text control
SetDlgItemTextA(hDlg, IDC_TIME_REMAINING, szSecs); }
VOID DisconnectDlgDisableAutodisconnect(HWND hDlg) { // get pointer to data struct out of window data
DISCONNECTDLGINFO * pDisconnectDlgInfo = (DISCONNECTDLGINFO *) GetWindowLongPtr(hDlg, DWLP_USER); ASSERT(pDisconnectDlgInfo);
// find out if disable autodisconnect checkbox is checked
BOOL fDisabled = IsDlgButtonChecked(hDlg,IDC_DISABLE_AUTODISCONNECT);
// enable or disable controls appropriately
EnableDisconnectDlgCtrls(hDlg,!fDisabled);
if (!fDisabled) { // reset timer if we're re-enabling autodisconnect
pDisconnectDlgInfo->dwCountdownVal = DISCONNECT_DLG_COUNTDOWN; // show timer value
DisconnectDlgShowCountdown(hDlg,pDisconnectDlgInfo->dwCountdownVal); } else { // show "--" in countdown value
DisconnectDlgShowCountdown(hDlg,(DWORD) -1); } }
VOID EnableDisconnectDlgCtrls(HWND hDlg,BOOL fEnable) { EnableWindow(GetDlgItem(hDlg,IDC_TX1),fEnable); EnableWindow(GetDlgItem(hDlg,IDC_TX2),fEnable); EnableWindow(GetDlgItem(hDlg,IDC_TX3),fEnable); EnableWindow(GetDlgItem(hDlg,IDC_TIME_REMAINING),fEnable); EnableWindow(GetDlgItem(hDlg,IDOK),fEnable); }
BOOL CenterWindow (HWND hwndChild, HWND hwndParent) { RECT rChild, rParent; int wChild, hChild, wParent, hParent; int wScreen, hScreen, xNew, yNew; HDC hdc;
// Get the Height and Width of the child window
GetWindowRect (hwndChild, &rChild); wChild = rChild.right - rChild.left; hChild = rChild.bottom - rChild.top;
// Get the Height and Width of the parent window
GetWindowRect (hwndParent, &rParent); wParent = rParent.right - rParent.left; hParent = rParent.bottom - rParent.top;
// Get the display limits
hdc = GetDC (hwndChild); wScreen = GetDeviceCaps (hdc, HORZRES); hScreen = GetDeviceCaps (hdc, VERTRES); ReleaseDC (hwndChild, hdc);
// Calculate new X position, then adjust for screen
xNew = rParent.left + ((wParent - wChild) /2); if (xNew < 0) { xNew = 0; } else if ((xNew+wChild) > wScreen) { xNew = wScreen - wChild; }
// Calculate new Y position, then adjust for screen
yNew = rParent.top + ((hParent - hChild) /2); if (yNew < 0) { yNew = 0; } else if ((yNew+hChild) > hScreen) { yNew = hScreen - hChild; }
// Set it, and return
return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); }
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// BUFFER class implementation
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
BOOL BUFFER::Alloc( UINT cchBuffer ) { _lpBuffer = (LPTSTR)::MemAlloc(LPTR,cchBuffer*sizeof(TCHAR)); if (_lpBuffer != NULL) { _cch = cchBuffer; return TRUE; } return FALSE; }
BOOL BUFFER::Realloc( UINT cchNew ) { LPVOID lpNew = ::MemReAlloc((HLOCAL)_lpBuffer, cchNew*sizeof(TCHAR), LMEM_MOVEABLE | LMEM_ZEROINIT); if (lpNew == NULL) return FALSE;
_lpBuffer = (LPTSTR)lpNew; _cch = cchNew; return TRUE; }
BUFFER::BUFFER( UINT cchInitial /* =0 */ ) : BUFFER_BASE(), _lpBuffer( NULL ) { if (cchInitial) Alloc( cchInitial ); }
BUFFER::~BUFFER() { if (_lpBuffer != NULL) { MemFree((HLOCAL) _lpBuffer); _lpBuffer = NULL; } }
BOOL BUFFER::Resize( UINT cchNew ) { BOOL fSuccess;
if (QuerySize() == 0) fSuccess = Alloc( cchNew*sizeof(TCHAR) ); else { fSuccess = Realloc( cchNew*sizeof(TCHAR) ); } if (fSuccess) _cch = cchNew; return fSuccess; }
|