1683 lines
47 KiB
C++
1683 lines
47 KiB
C++
|
//*********************************************************************
|
||
|
//* 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;
|
||
|
}
|