|
|
//+----------------------------------------------------------------------------
//
// File: connection.cpp
//
// Module: CMMON32.EXE
//
// Synopsis:
// Implement class CCmConnection
// CCmConnection manages a single connection.
//
// The m_StatusDlg lives through out the CONNECTED/DISCONNECT_COUNTDOWN
// state. The appearance changes when it comes to COUNTDOWN state.
//
// The m_ReconnectDlg is the reconnect prompt dialog. It exist during
// STATE_PROMPT_RECONNECT state.
//
// Both dialogs are modeless. (We need a initially invisible status dialog
// to receive timer and trayicon message. I did not find any way to create
// a invisible modal dialog without flashing.)
//
// CreateDialog will simply return, unlike DialogBox, which returns only after
// Dialog is ended. To simplify the implementation, we handle end-dialog event
// in the thread routine instead of in the dialog procedure.
//
// When we need to end the status or reconnect dialog, we simply post a thread
// message to end the dialog and continue to the next state. The connection
// thread runs a message loop and processes thread message.
//
// The RasMonitorDlg on NT is running in another thread. Otherwise the connection
// thread message can not be processed
//
// The connection is event driven module. ConnectionThread() is the entry
// point of the connection thread.
//
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
// Author: fengsun Created 2/11/98
//
//+----------------------------------------------------------------------------
#include "cmmaster.h"
#include "connection.h"
#include "Monitor.h"
#include "TrayIcon.h"
#include "ShellDll.h"
#include <tchar.h>
#include <rasdlg.h>
#include "cmdial.h"
#include <wininet.h> // for INTERNET_DIALSTATE_DISCONNECTED
#include "DynamicLib.h"
#include "log_str.h"
#include "userinfo_str.h"
HINSTANCE g_hInst = NULL;
//
// Functions in cmdial32.dll, the prototype is in cmdial.h
//
static const CHAR* const c_pszCmReconnect = "CmReConnect"; static const CHAR* const c_pszCmHangup = "CmCustomHangUp";
//
// CMS flags used exclusively by connection.cpp
//
static const TCHAR* const c_pszCmEntryIdleThreshold = TEXT("IdleThreshold"); static const TCHAR* const c_pszCmEntryNoPromptReconnect = TEXT("NoPromptReconnect"); static const TCHAR* const c_pszCmEntryHideTrayIcon = TEXT("HideTrayIcon");
typedef BOOL (WINAPI * CmReConnectFUNC)(LPTSTR lpszPhonebook, LPTSTR lpszEntry, LPCMDIALINFO lpCmInfo);
typedef DWORD (WINAPI * CmCustomHangUpFUNC)(HRASCONN hRasConn, LPCTSTR pszEntry, BOOL fIgnoreRefCount, BOOL fPersist);
//
// The timer interval for StateConnectedOnTimer();
//
const DWORD TIMER_INTERVAL = 1000;
DWORD CCmConnection::m_dwCurPositionId = 0; //+----------------------------------------------------------------------------
//
// Function: CCmConnection::CCmConnection
//
// Synopsis: Constructor, called in the monitor thread
//
// Arguments: const CONNECTED_INFO* pConnectedInfo - Information passed from
// cmdial upon conected
// const CM_CONNECTION* pConnectionEntry - Information in the
// Connection table
//
// Returns: Nothing
//
// History: fengsun Created Header 2/3/98
//
//+----------------------------------------------------------------------------
CCmConnection::CCmConnection(const CM_CONNECTED_INFO* pConnectedInfo, const CM_CONNECTION* pConnectionEntry) : #pragma warning(disable:4355) //'this' : used in base member initializer list
m_StatusDlg(this), #pragma warning(default:4355)
m_TrayIcon() { MYDBGASSERT(pConnectedInfo); MYDBGASSERT(pConnectionEntry);
m_dwState = STATE_CONNECTED; m_hBigIcon = m_hSmallIcon = NULL;
m_dwConnectStartTime = GetTickCount() - 500; // .5 second for round off
m_dwCountDownStartTime = 0; m_dwThreadId = 0;
//
// set this to TRUE, so the WorkingSet will be minimized before MsgWait
// while there is no more message
//
m_fToMinimizeWorkingSet = TRUE;
m_fHideTrayIcon = FALSE;
//
// Save the data from pConnectedInfo
//
//lstrcpynU(m_ReconnectInfo.szPassword, pConnectedInfo->szPassword,
// sizeof(m_ReconnectInfo.szPassword)/sizeof(m_ReconnectInfo.szPassword[0]));
//lstrcpynU(m_ReconnectInfo.szInetPassword, pConnectedInfo->szInetPassword,
// sizeof(m_ReconnectInfo.szPassword)/sizeof(m_ReconnectInfo.szPassword[0]));
m_ReconnectInfo.dwCmFlags = pConnectedInfo->dwCmFlags | FL_RECONNECT; // Cm specific flags
lstrcpynU(m_szServiceName, pConnectedInfo->szEntryName, sizeof(m_szServiceName)/sizeof(m_szServiceName[0]));
//
// NOTE: Fast User Switching is only available on WinXP and beyond, and this
// member variable should only be accessed/used for WinXP and beyond.
//
m_fGlobalGlobal = (pConnectionEntry->fAllUser && (pConnectedInfo->dwCmFlags & FL_GLOBALCREDS)); CMTRACE1(TEXT("CCmConnection::CCmConnection set m_fGlobalGlobal to %d"), m_fGlobalGlobal);
//
// Get the RAS phonebook
//
lstrcpynU(m_szRasPhoneBook, pConnectedInfo->szRasPhoneBook, sizeof(m_szRasPhoneBook)/sizeof(m_szRasPhoneBook[0]));
//
// Init m_IniProfile, m_IniService and m_IniBoth
//
InitIniFiles(pConnectedInfo->szProfilePath);
//
// Because the IdleTimeout and EnableLogging values are not saved
// per access point as all the other profile settings are, we must change the PrimaryRegPath
// value of m_IniBoth so that it points to the non-access point registry location.
//
LPCTSTR c_pszUserInfoRegPath = (pConnectionEntry->fAllUser) ? c_pszRegCmUserInfo : c_pszRegCmSingleUserInfo;
LPTSTR pszSavedPrimaryRegPath = CmStrCpyAlloc(m_IniBoth.GetPrimaryRegPath()); LPTSTR pszPrimaryRegPath = (LPTSTR)CmMalloc(sizeof(TCHAR)*(lstrlenU(c_pszUserInfoRegPath) + lstrlenU(m_szServiceName) + 1));
if (pszPrimaryRegPath && pszSavedPrimaryRegPath) { wsprintfU(pszPrimaryRegPath, TEXT("%s%s"), c_pszUserInfoRegPath, m_szServiceName); m_IniBoth.SetPrimaryRegPath(pszPrimaryRegPath);
CmFree(pszPrimaryRegPath); }
//
// Initialize Logging
//
m_Log.Init(g_hInst, pConnectionEntry->fAllUser, GetServiceName()); BOOL fEnabled = FALSE; DWORD dwMaxSize = 0; LPTSTR pszFileDir = NULL;
fEnabled = m_IniBoth.GPPB(c_pszCmSection, c_pszCmEntryEnableLogging, c_fEnableLogging); dwMaxSize = m_IniService.GPPI(c_pszCmSectionLogging, c_pszCmEntryMaxLogFileSize, c_dwMaxFileSize); pszFileDir = m_IniService.GPPS(c_pszCmSectionLogging, c_pszCmEntryLogFileDirectory, c_szLogFileDirectory); m_Log.SetParams(fEnabled, dwMaxSize, pszFileDir); if (m_Log.IsEnabled()) { m_Log.Start(FALSE); // FALSE => no banner
} else { m_Log.Stop(); } CmFree(pszFileDir);
//
// Whether to enable auto disconnect for no-traffic and no-watch-process
// 0 of dwIdleTime means never timeout
//
const DWORD DEFAULT_IDLETIMEOUT = 10; // default idle time out is 10 minute
DWORD dwIdleTime = (DWORD) m_IniBoth.GPPI(c_pszCmSection, c_pszCmEntryIdleTimeout, DEFAULT_IDLETIMEOUT);
//
// Set the m_IniBoth object back to its previous Primary Reg path
//
if (pszSavedPrimaryRegPath) { m_IniBoth.SetPrimaryRegPath(pszSavedPrimaryRegPath); CmFree(pszSavedPrimaryRegPath); }
//
// No watch-process time-out if IdleTime is "never"
//
if (dwIdleTime) { for (int i=0; pConnectedInfo->ahWatchHandles[i] != 0; i++) {
m_WatchProcess.Add(pConnectedInfo->ahWatchHandles[i]); } }
if (!OS_NT4) { m_ConnStatistics.SetDialupTwo(pConnectedInfo->fDialup2);
m_ConnStatistics.Open(CMonitor::GetInstance(), pConnectedInfo->dwInitBytesRecv, pConnectedInfo->dwInitBytesSend, pConnectionEntry->hDial, pConnectionEntry->hTunnel); if (dwIdleTime) { //
// Adjust minutes value to milliseconds
//
dwIdleTime = dwIdleTime * 1000 * 60;
DWORD dwIdleThreshold = m_IniService.GPPI(c_pszCmSection, c_pszCmEntryIdleThreshold, 0L); // default threshold is always 0 bytes
//
// Start idle statistic counter anyway
// IsIdle will return FALSE, if it is never updated
//
m_IdleStatistics.Start(dwIdleThreshold, dwIdleTime); } }
//
// Save data from pConnectionEntry
//
MYDBGASSERT(pConnectionEntry->hDial || pConnectionEntry->hTunnel); MYDBGASSERT(pConnectionEntry->CmState == CM_CONNECTED);
m_hRasDial = pConnectionEntry->hDial; m_hRasTunnel = pConnectionEntry->hTunnel;
m_szHelpFile[0] = 0; //
// the position id increased by 1 for each connection
//
m_dwPositionId = m_dwCurPositionId; m_dwCurPositionId++; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::~CCmConnection
//
// Synopsis:
//
// Arguments: None
//
// Returns: Nothing
//
// History: Created Header 2/18/98
//
//+----------------------------------------------------------------------------
CCmConnection::~CCmConnection() { ASSERT_VALID(this);
if (m_hBigIcon) { DeleteObject(m_hBigIcon); }
if (m_hSmallIcon) { DeleteObject(m_hSmallIcon); }
if (m_hEventRasNotify) { CloseHandle(m_hEventRasNotify); }
//
// UnInitialize Logging
//
m_Log.DeInit();
}
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::InitIniFiles
//
// Synopsis: Initialize data member m_IniProfile, m_IniService and m_IniBoth
//
// Arguments: const TCHAR* pszProfileName - the full path of the .cmp file
//
// Returns: Nothing
//
// History: fengsun Created Header 2/10/98
//
//+----------------------------------------------------------------------------
void CCmConnection::InitIniFiles(const TCHAR* pszProfileName) { if (NULL == pszProfileName) { return; }
g_hInst = CMonitor::GetInstance();
//
// .cmp file
//
m_IniProfile.Clear(); m_IniProfile.SetHInst(CMonitor::GetInstance()); m_IniProfile.SetFile(pszProfileName);
//
// .cms file
//
m_IniService.Clear(); m_IniService.SetHInst(CMonitor::GetInstance());
LPTSTR pszService = m_IniProfile.GPPS(c_pszCmSection,c_pszCmEntryCmsFile); MYDBGASSERT(pszService);
//
// the .cms file is relative to .cmp path, convert it to absolute path
//
LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszService);
MYDBGASSERT(pszFullPath);
if (pszFullPath) { m_IniService.SetFile(pszFullPath); }
CmFree(pszFullPath); CmFree(pszService);
// both: .CMP file takes precedence over .CMS file
// use .CMP file as primary file
//
m_IniBoth.Clear(); m_IniBoth.SetHInst(CMonitor::GetInstance()); m_IniBoth.SetFile(m_IniService.GetFile()); m_IniBoth.SetPrimaryFile(m_IniProfile.GetFile()); }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::StartConnectionThread
//
// Synopsis: Start the connection thread. Called by monitor on CONNECTED
// message from cmdial
//
// Arguments: None
//
// Returns: BOOL - Whether the thread is created successfully
//
// History: fengsun Created Header 2/3/98
//
//+----------------------------------------------------------------------------
BOOL CCmConnection::StartConnectionThread() { DWORD dwThreadId; HANDLE hThread; if ((hThread = CreateThread(NULL, 0, ConnectionThread ,this,0,&dwThreadId)) == NULL) { MYDBGASSERT(FALSE); CMTRACE(TEXT("CCmConnection::StartConnectionThread CreateThread failed")); return FALSE; }
CloseHandle(hThread);
return TRUE; }
//+----------------------------------------------------------------------------
//
// Function: static CCmConnection::ConnectionThread
//
// Synopsis: The connection thread. Call back function for CreateThread
//
// Arguments: LPVOID lParam - pConnection
//
// Returns: DWORD WINAPI - thread exit code
//
// History: fengsun Created Header 2/12/98
//
//+----------------------------------------------------------------------------
DWORD WINAPI CCmConnection::ConnectionThread(LPVOID lParam) { MYDBGASSERT(lParam);
//
// Call the non-static function
//
return ((CCmConnection*)lParam)->ConnectionThread(); }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::ConnectionThread
//
// Synopsis: The non-static connection thread, so we can referrence
// data/fuction directly
//
// Arguments: None
//
// Returns: DWORD - thread exit code
//
// History: Created Header 2/12/98
//
//+----------------------------------------------------------------------------
DWORD CCmConnection::ConnectionThread() { m_dwThreadId = GetCurrentThreadId();
m_dwState = STATE_CONNECTED;
//
// Run the connected/disconnect-count-down state
// StateConnected() will change m_dwState to the new state
//
StateConnected();
//
// Whether to remove the connection from the shared connection table
// This is TRUE, only if user clicks No for the prompt reconnect dialog
//
BOOL fRemoveFromSharedTable = FALSE;
if (m_dwState != STATE_TERMINATED) { //
// if auto reconnect is not enabled, then show the reconnect prompt
//
if (m_dwState != STATE_RECONNECTING) { //
// Run the prompt reconnect state
//
m_dwState = StatePrompt(); }
if (m_dwState != STATE_RECONNECTING) { //
// User clicks No for the reconnect-prompt dialog
// Clear the entry from connection table
//
fRemoveFromSharedTable = TRUE; } else { //
// User clicks Yes for the reconnect-prompt dialog
// Move from Connected array to reconnecting array
//
CMonitor::MoveToReconnectingConn(this);
//
// Run the reconnect state
//
Reconnect(); } }
CMTRACE(TEXT("The connection thread is terminated"));
//
// The connection is terminated without need to ask for reconnect
// Remove the connection from monitor conntected connection array
// If fRemoveFromSharedTable is FALSE, do not clear the entry from shared table.
// CmCustomHangup clears the table
// Monitor will delete the connection object. Must exit the thread after this.
//
CMonitor::RemoveConnection(this, fRemoveFromSharedTable);
CMonitor::MinimizeWorkingSet();
return 0; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::StateConnectedInit
//
// Synopsis: Initialization for the connected state, unlike the connstructor
// This is called with in the connection thread
//
// Arguments: None
//
// Returns: Nothing
//
// History: fengsun Created Header 2/12/98
//
//+----------------------------------------------------------------------------
void CCmConnection::StateConnectedInit() { m_dwConnectStartTime = GetTickCount();
//
// Load big and small connection icon: m_hBigIcon and m_hSmallIcon
//
LoadConnectionIcons();
m_StatusDlg.Create(CMonitor::GetInstance(), CMonitor::GetMonitorWindow(), m_szServiceName, m_hBigIcon); m_StatusDlg.ChangeToStatus();
//
// Change the window position, so multiple status window will not be at
// the same position
//
PositionWindow(m_StatusDlg.GetHwnd(), m_dwPositionId);
//
// Change the dialog titlebar icon
//
SendMessageU(m_StatusDlg.GetHwnd(),WM_SETICON,ICON_BIG,(LPARAM) m_hBigIcon); SendMessageU(m_StatusDlg.GetHwnd(),WM_SETICON,ICON_SMALL,(LPARAM) m_hSmallIcon);
//
// Set the help file name
//
LPTSTR lpHelpFile = LoadHelpFileName(); if (lpHelpFile) { m_StatusDlg.SetHelpFileName(lpHelpFile); } else { m_StatusDlg.SetHelpFileName(c_pszDefaultHelpFile); }
CmFree(lpHelpFile); //
// Determine whether or not, we're hiding the icon. The default is TRUE
// for NT5 because we already have full support from the folder.
//
m_fHideTrayIcon= m_IniService.GPPI(c_pszCmSection, c_pszCmEntryHideTrayIcon, OS_NT5);
if (!m_fHideTrayIcon && !(OS_NT5 && IsLogonAsSystem())) { HICON hIcon = NULL;
LPTSTR pszTmp = m_IniService.GPPS(c_pszCmSection, c_pszCmEntryTrayIcon); if (*pszTmp) { //
// The icon name is relative to the .cmp file, convert it into full name
//
LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszTmp);
hIcon = CmLoadSmallIcon(CMonitor::GetInstance(), pszFullPath); CmFree(pszFullPath); } CmFree(pszTmp);
//
// Use the default tray icon
//
if (!hIcon) { hIcon = CmLoadSmallIcon(CMonitor::GetInstance(), MAKEINTRESOURCE(IDI_APP)); }
//
// m_TrayIcon is responsible to delete the hIcon object
//
m_TrayIcon.SetIcon(hIcon, m_StatusDlg.GetHwnd(), WM_TRAYICON, 0,m_szServiceName);
// Question: , shall we also load the tray icon cmd from iniProfile?
m_TrayIcon.CreateMenu(&m_IniService, IDM_TRAYMENU); }
//
// Try to call RasConnectionNotification. When connection is losted
// a event will be signaled
//
m_RasApiDll.Load(); m_hEventRasNotify = CallRasConnectionNotification(m_hRasDial, m_hRasTunnel);
if (m_hEventRasNotify) { //
// If we got the event, unload RAS, otherwise, need to check connection on timer
//
m_RasApiDll.Unload(); } }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::StateConnected
//
// Synopsis: The connection is in the connected or disconnect-count-down state
// Run the message loop until state is changed
//
// Arguments: None
//
// Returns: CONN_STATE - The new state, either STATE_TERMINATED or
// STATE_PROMPT_RECONNECT
//
// History: fengsun Created Header 2/4/98
//
//+----------------------------------------------------------------------------
//
// The reconnect dialog only shows up on Win95 Gold
// And we need to use service name to find the dialog.
//
void ZapRNAReconnectStop(HANDLE hThread); HANDLE ZapRNAReconnectStart(BOOL *pbConnLost);
void CCmConnection::StateConnected() { ASSERT_VALID(this); MYDBGASSERT(m_dwState == STATE_CONNECTED); BOOL fLostConnection = FALSE; CMTRACE(TEXT("Enter StateConnected"));
StateConnectedInit();
HANDLE hThreadRnaReconnect = NULL;
if (OS_W95) { hThreadRnaReconnect = ZapRNAReconnectStart(NULL); }
//
// Ignore return value
//
BOOL fRV = CheckRasConnection(fLostConnection);
//
// If we lost the connection, we need to hangup so that rasman has the correct
// ref count.
//
if (fLostConnection) { CMTRACE(TEXT("StateConnected - Actually not connected. Need to hangup to notify rasman.")); m_dwState = StateConnectedProcessEvent(EVENT_CMDIAL_HANGUP); } else { while (m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN) { CONN_EVENT dwEvent = StateConnectedGetEvent(); MYDBGASSERT(dwEvent <= EVENT_NONE);
if (dwEvent < EVENT_NONE ) { //
// Call the event handler to process the event
//
m_dwState = StateConnectedProcessEvent(dwEvent); } } }
if (hThreadRnaReconnect) { ZapRNAReconnectStop(hThreadRnaReconnect); }
}
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::StateConnectedGetEvent
//
// Synopsis: In the state of CONNECTED/COUNTDOWN. Wait until some event happens.
// Also runs the message loop
//
// Arguments: None
//
// Returns: CCmConnection::CONN_EVENT - The event that can cause the
// connection change the state other than CONNECTED/COUNTDOWN
//
// History: Created Header 2/18/98
//
//+----------------------------------------------------------------------------
CCmConnection::CONN_EVENT CCmConnection::StateConnectedGetEvent() { ASSERT_VALID(this); //
// The last time SateConnectedOnTimer got called
//
DWORD dwLastTimerCalled = 0; //
// Loop until we got some event
//
while (TRUE) { MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
//
// Process all the messages in the message queue
//
MSG msg; while(PeekMessageU(&msg, NULL,0,0,PM_REMOVE)) { if (msg.hwnd == NULL) { //
// it is a thread message
//
MYDBGASSERT((msg.message >= WM_APP) || (msg.message == WM_CONN_EVENT)); if (msg.message == WM_CONN_EVENT) { MYDBGASSERT(msg.wParam < EVENT_NONE); return (CONN_EVENT)msg.wParam; } } else { //
// Also dispatch message for Modeless dialog box
//
if (!IsDialogMessageU(m_StatusDlg.GetHwnd(), &msg)) { TranslateMessage(&msg); DispatchMessageU(&msg); } } }
//
// whether cmmon needs timer.
// The timer is needed, if we have check the ras connection on timer,
// or we have to check idle disconnect on timer,
// or the state is disconnect-count-down,
// or status dialog is visible
//
BOOL fNeedTimer = !m_hEventRasNotify || m_IdleStatistics.IsStarted() || m_dwState == STATE_COUNTDOWN || IsWindowVisible(m_StatusDlg.GetHwnd());
//
// If more than 1 seconds elapsed, call the timer
//
if (fNeedTimer && GetTickCount() - dwLastTimerCalled >= TIMER_INTERVAL) { dwLastTimerCalled = GetTickCount(); CONN_EVENT dwEvent = StateConnectedOnTimer();
if (dwEvent != EVENT_NONE) { return dwEvent; } }
//
// Setup the opbject array for MsgWaitForMultipleObjects
//
HANDLE ahObjectsToWait[3]; int nObjects = 0;
if (m_hEventRasNotify) { ahObjectsToWait[nObjects] = m_hEventRasNotify; nObjects++; }
if (m_WatchProcess.GetSize()) { //
// If we have any process to watch, just add the first hProcess
// Since we want to know whether all processes exit
//
ahObjectsToWait[nObjects] = m_WatchProcess.GetProcess(0); MYDBGASSERT(ahObjectsToWait[nObjects]); nObjects++; }
//
// From MSDN:
// The documentation for MsgWaitForMultipleObjects() says that the API returns
// successfully when either the objects are signalled or the input is available.
// However, the API behaves as if it requires that the objects are signalled
// and the input is available.
//
// Put an extra event seems to fix it for NT
//
if (OS_NT && nObjects) { ahObjectsToWait[nObjects] = ahObjectsToWait[nObjects-1]; nObjects++; }
if (m_fToMinimizeWorkingSet) { //
// If we do not need a timer here, minimize the working set.
// before calling MsgWaitForMultipleObjects.
//
CMonitor::MinimizeWorkingSet(); m_fToMinimizeWorkingSet = FALSE; }
DWORD dwRes = MsgWaitForMultipleObjects(nObjects, ahObjectsToWait, FALSE, fNeedTimer ? 1000 : INFINITE, QS_ALLINPUT);
//
// Timeout
//
if (dwRes == WAIT_TIMEOUT) { //
// We always checks timer on the beginning of the loop
//
continue; }
//
// An event
//
#pragma warning(push)
#pragma warning(disable:4296)
if (dwRes >= WAIT_OBJECT_0 && dwRes < WAIT_OBJECT_0 + nObjects) #pragma warning(pop)
{ BOOL fLostConnection; //
// Ras Event
//
if (m_hEventRasNotify && ahObjectsToWait[dwRes - WAIT_OBJECT_0] == m_hEventRasNotify && !CheckRasConnection(fLostConnection)) { //
// Got a notification that the RAS connection is losted
//
CMTRACE(TEXT("CCmConnection::StateConnectedGetEvent() - m_hEventRasNotify && ahObjectsToWait[dwRes - WAIT_OBJECT_0] == m_hEventRasNotify")); return EVENT_LOST_CONNECTION; } else { //
// A watch process exits
// IsIdle() remove the process from the list
//
if (m_WatchProcess.IsIdle()) { //
// If all the watch process are terminated, change to disconnect countdown
//
CMTRACE(TEXT("CCmConnection::StateConnectedGetEvent() - m_WatchProcess.IsIdle()")); return EVENT_IDLE; }
continue; } }
//
// A message
//
if (dwRes == WAIT_OBJECT_0 + nObjects) { continue; }
if (-1 == dwRes) { CMTRACE1(TEXT("MsgWaitForMultipleObjects failed, LastError:%d"), GetLastError()); //
// Something does wrong
//
continue; }
//
// what is this return value
//
CMTRACE1(TEXT("MsgWaitForMultipleObjects returns %d"), dwRes); continue; }
//
// should never get here
//
MYDBGASSERT(FALSE); return EVENT_USER_DISCONNECT; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::StateConnectedOnTimer
//
// Synopsis: Process the timer in the state of CONNECTED/COUNTDOWN
//
// Arguments: None
//
// Returns: CCmConnection::CONN_EVENT - The event that can cause the
// connection change the state other than CONNECTED/COUNTDOWN or EVENT_NONE
//
// History: fengsun Created Header 2/18/98
//
//+----------------------------------------------------------------------------
CCmConnection::CONN_EVENT CCmConnection::StateConnectedOnTimer() { ASSERT_VALID(this); MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
if (m_dwState != STATE_CONNECTED && m_dwState != STATE_COUNTDOWN) { return EVENT_NONE; }
//
// Check whether CM is still connected, only if the Ras notify event is not available
//
if (m_hEventRasNotify == NULL) { BOOL fLostConnection; BOOL fConnect = CheckRasConnection(fLostConnection);
CMTRACE2(TEXT("CCmConnection::StateConnectedOnTimer - CheckRasConnection returns %d - fLostConnection is %d"), fConnect, fLostConnection);
if (!fConnect) { return (fLostConnection ? EVENT_LOST_CONNECTION : EVENT_USER_DISCONNECT); } }
//
// If we don't have stats, something went wrong accessing the
// registry stats earlier, so try to initialize again here.
//
if (OS_W98 && !m_ConnStatistics.IsAvailable()) { //
// Try to initialize the perf stats from the registry again
//
CMASSERTMSG(FALSE, TEXT("StateConnectedOnTimer() - Statistics unavailable, re-initializing stats now."));
m_ConnStatistics.Open(CMonitor::GetInstance(), (DWORD)-1, (DWORD)-1, m_hRasDial, m_hRasTunnel); }
//
// Get statistics for Win9x
//
if (m_ConnStatistics.IsAvailable()) { m_ConnStatistics.Update();
//
// collecting data points for monitoring idle-disconnect
// check whether ICM has received more data points than the IdleThreshold value
// during the past IDLE_SPREAD time
// Start() is not called for NT
//
if (m_IdleStatistics.IsStarted()) { m_IdleStatistics.UpdateEveryInterval(m_ConnStatistics.GetBytesRead()); } }
if (m_dwState == STATE_CONNECTED) { //
// Check idle time out
//
if (m_IdleStatistics.IsIdleTimeout()) { //
// Disconnect count down
//
CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - m_IdleStatistics.IsIdleTimeout()")); return EVENT_IDLE; }
//
// Check process watch list
//
if (m_WatchProcess.IsIdle()) { //
// Disconnect count down
//
CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - m_WatchProcess.IsIdle())")); return EVENT_IDLE; }
//
// Update the status window, only if the window is visible
//
if (IsWindowVisible(m_StatusDlg.GetHwnd())) { if (m_ConnStatistics.IsAvailable()) { m_StatusDlg.UpdateStats(m_ConnStatistics.GetBaudRate(), m_ConnStatistics.GetBytesRead(), m_ConnStatistics.GetBytesWrite(), m_ConnStatistics.GetBytesPerSecRead(), m_ConnStatistics.GetBytesPerSecWrite()); }
//
// We have exact duration numbers from RAS on NT5, so use them.
//
if (m_ConnStatistics.GetDuration()) { m_StatusDlg.UpdateDuration(m_ConnStatistics.GetDuration() / 1000); } else { m_StatusDlg.UpdateDuration((GetTickCount() - m_dwConnectStartTime) / 1000); } }
return EVENT_NONE; } else // m_dwState == STATE_COUNTDOWN
{
//
// Note: NetBEUI seems to insist on sending unsolicited
// stuff over the dial-up adapter. So we will define
// "idle" as "not having received anything".
//
//
// check whether we have new traffic that exceeds the threshold.
// But we don't care about network traffic if the countdown is caused
// by 0 watched process
//
if (!m_WatchProcess.IsIdle() && !m_IdleStatistics.IsIdle() ) {
//
// We were in our idle wait, but we just picked up some
// activity. Stay on line.
// If this is NT5 we don't use our own status dialog so just hide
// it again. If this is downlevel then just change the dialog to
// a status dialog
//
if (OS_NT5) { m_StatusDlg.DismissStatusDlg(); } else { m_StatusDlg.ChangeToStatus(); }
m_dwState = STATE_CONNECTED;
return EVENT_NONE; }
//
// If the time elapsed is more than 30 second, timeout
//
DWORD dwElapsed = GetTickCount() - m_dwCountDownStartTime;
if (dwElapsed > IDLE_DLG_WAIT_TIMEOUT) { //
// Connection has been idle for timeout period and the
// grace period is up with no user intervention, so we
// quit/disconnect, and ask for reconnect
//
CMTRACE(TEXT("CCmConnection::StateConnectedOnTimer() - dwElapsed > IDLE_DLG_WAIT_TIMEOUT")); return EVENT_COUNTDOWN_ZERO; } else { //
// Connection has been idle for timeout period but we
// are still in the grace period, show countdown.
//
int nTimeLeft = (int) ((IDLE_DLG_WAIT_TIMEOUT - dwElapsed) / 1000); //
// Update duration and countdown seconds left.
//
if (m_ConnStatistics.GetDuration()) // NT5 only
{ m_StatusDlg.UpdateCountDown(m_ConnStatistics.GetDuration() / 1000, nTimeLeft); } else { m_StatusDlg.UpdateCountDown((GetTickCount() - m_dwConnectStartTime) / 1000, nTimeLeft); } }
return EVENT_NONE; } }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::StateConnectedProcessEvent
//
// Synopsis: Process the connection event while in CONNECTED/COUNTDOWN state
//
// Arguments: CONN_EVENT dwEvent - the event to process
//
// Returns: CCmConnection::CONN_STATE - The new state of the connection
//
// History: fengsun Created Header 2/19/98
//
//+----------------------------------------------------------------------------
CCmConnection::CONN_STATE CCmConnection::StateConnectedProcessEvent(CONN_EVENT dwEvent) { ASSERT_VALID(this); switch (dwEvent) { case EVENT_IDLE: CMTRACE(TEXT("StateConnectedProcessEvent EVENT_IDLE"));
if (m_dwState != STATE_COUNTDOWN) { //
// No-traffic/ No-watch-process idle event
// change to Disconnect count down
//
m_dwCountDownStartTime = GetTickCount(); m_StatusDlg.ChangeToCountDown(); //
// Update duration and countdown seconds left
//
int nTimeLeft = IDLE_DLG_WAIT_TIMEOUT / 1000;
if (m_ConnStatistics.GetDuration()) // NT5 only
{ m_StatusDlg.UpdateCountDown(m_ConnStatistics.GetDuration() / 1000, nTimeLeft); } else { m_StatusDlg.UpdateCountDown((GetTickCount() - m_dwConnectStartTime) / 1000, nTimeLeft); }
//
// Don't show the UI if we are at Winlogon unless we are on NT4
//
if (!IsLogonAsSystem() || OS_NT4) { m_StatusDlg.BringToTop(); } }
return STATE_COUNTDOWN;
case EVENT_CMDIAL_HANGUP: CMTRACE(TEXT("StateConnectedProcessEvent EVENT_CMDIAL_HANGUP"));
m_Log.Log(DISCONNECT_EXT);
//
// Cmdial posted cmmon a message to clean up the connection.
// Do not need to call hangup here
//
StateConnectedCleanup();
return STATE_TERMINATED;
case EVENT_LOST_CONNECTION: case EVENT_COUNTDOWN_ZERO: CMTRACE(TEXT("StateConnectedProcessEvent EVENT_LOST_CONNECTION/EVENT_COUNTDOWN_ZERO")); //
// lost-ras-connection event or the count down counter is down to 0
//
if (IsPromptReconnectEnabled() && !m_WatchProcess.IsIdle() || ( dwEvent == EVENT_LOST_CONNECTION && IsAutoReconnectEnabled() ) ) { CmCustomHangup(TRUE); // fPromptReconnect = TRUE, do not remove from Conn Table
//
// It is possible
// Someone else called cmdial to disconnect, while we are disconnecting.
// If the ref count is down to 0, cmdial will remove the entry
//
CM_CONNECTION CmEntry; if (CMonitor::ConnTableGetEntry(m_szServiceName, &CmEntry)) { //
// Cmdial should change the state to CM_RECONNECTPROMPT
//
CMTRACE2(TEXT("CmEntry.CmState is %d, event is %d"), CmEntry.CmState, dwEvent); MYDBGASSERT(CmEntry.CmState == CM_RECONNECTPROMPT);
if (EVENT_LOST_CONNECTION == dwEvent) { m_Log.Log(DISCONNECT_EXT_LOST_CONN); } else if (EVENT_COUNTDOWN_ZERO == dwEvent) { m_Log.Log(DISCONNECT_INT_AUTO); }
//
// is auto reconnect enabled(don't show reconnect prompt)?
//
if (dwEvent == EVENT_LOST_CONNECTION && IsAutoReconnectEnabled()) { //
// On win98 Gold, we have a timing issue with Auto Reconnect because
// notification that the line was dropped is sent before cleanup for
// the connection takes place. Thus, we need to poll the connection
// status using RasGetConnectionStatus until the line is available
// before trying to reconnect. NTRAID 273033.
//
if (OS_W98 && (NULL != m_hRasDial)) { BOOL bConnectionActive; BOOL bLostConnection; //ignored
int iCount = 0;
do { bConnectionActive = CheckRasConnection(bLostConnection); if (bConnectionActive) { Sleep(50); }
iCount++; // 50 Milliseconds * 200 = 10 seconds
// If waiting 10 seconds hasn't fixed it, not sure that it will
// get fixed by this method.
} while ((200 >= iCount) && (bConnectionActive)); }
return STATE_RECONNECTING; }
return STATE_PROMPT_RECONNECT; } else { return STATE_TERMINATED; } }
//
// Else fall through
//
case EVENT_USER_DISCONNECT: CMTRACE(TEXT("StateConnectedProcessEvent EVENT_USER_DISCONNECT"));
m_Log.Log(DISCONNECT_INT_MANUAL);
//
// For EVENT_USER_DISCONNECT
// User can disconnect from tray icon, status dialog, or countdown dialog.
// Or prompt reconnect is not enabled
//
CmCustomHangup(FALSE); // fPromptReconnect = FALSE
return STATE_TERMINATED;
default: //
// Unexpected event, do the same thing as EVENT_USER_DISCONNECT
//
MYDBGASSERT(FALSE); CmCustomHangup(FALSE); // fPromptReconnect = FALSE
return STATE_TERMINATED; } }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::IsAutoReconnectEnabled
//
// Synopsis: See if auto-reconnect is enabled, but only if we aren't logged-in
// as system.
//
// Arguments: None
//
// Returns: Nothing
//
// History: fengsun Created Header 2/18/98
// tomkel moved from connection.h 06/06/2001
//
//+----------------------------------------------------------------------------
BOOL CCmConnection::IsAutoReconnectEnabled() const { //
// Load the AutoReconnect flag from profile
// Default is FALSE
//
BOOL fReturn = FALSE;
if (!IsLogonAsSystem()) { fReturn = m_IniService.GPPB(c_pszCmSection, c_pszCmEntryAutoReconnect, FALSE); }
return fReturn; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::StateConnectedCleanup
//
// Synopsis: Cleanup before exiting the connected state
//
// Arguments: BOOL fEndSession, whether windows is going to logoff/shutdown
//
// Returns: Nothing
//
// History: fengsun Created Header 2/18/98
//
//+----------------------------------------------------------------------------
void CCmConnection::StateConnectedCleanup(BOOL fEndSession) { ASSERT_VALID(this); //
// Remove the trayicon, destroy the status dialog
//
m_TrayIcon.RemoveIcon(); m_ConnStatistics.Close();
//
// Do not close window on WM_ENDSESSION. Otherwise, cmmon would be terminated right here
//
if (!fEndSession) { m_StatusDlg.KillRasMonitorWindow(); DestroyWindow(m_StatusDlg.GetHwnd()); } }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::IsPromptReconnectEnabled
//
// Synopsis: When prompt-reconnect is enabled by the profile
//
// Arguments: None
//
// Returns: Nothing
//
// History: fengsun Created Header 2/18/98
//
//+----------------------------------------------------------------------------
BOOL CCmConnection::IsPromptReconnectEnabled() const { //
// Load the NoPromptReconnect flag from profile. Also check the Unattended flag and
// if we are running in the system account on win2k or Whistler. If any of the above
// are true then we don't prompt, otherwise we do.
//
BOOL fPromptReconnect = !(m_IniService.GPPB(c_pszCmSection, c_pszCmEntryNoPromptReconnect, FALSE));
if (!fPromptReconnect || (m_ReconnectInfo.dwCmFlags & FL_UNATTENDED) || (IsLogonAsSystem() && OS_NT5)) { //
// Do not prompt reconnect
//
return FALSE; }
return TRUE; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::CmCustomHangup
//
// Synopsis: Call cmdial to hangup the connection
//
// Arguments: BOOL fPromptReconnect, whether cmmon are going to prompt the
// reconnect dialog
// BOOL fEndSession, whther windows is going to logoff/shutdown
// Default value is FALSE
//
// Returns: Nothing
//
// History: fegnsun Created Header 2/11/98
//
//+----------------------------------------------------------------------------
BOOL CCmConnection::CmCustomHangup(BOOL fPromptReconnect, BOOL fEndSession) { ASSERT_VALID(this);
CMTRACE2(TEXT("CCmConnection::CmCustomHangup - fPromptReconnect is %d and fEndSession is %d"), fPromptReconnect, fEndSession);
//
// Remove the trayicon close status dlg, before hangup
//
StateConnectedCleanup(fEndSession);
//
// It is possible the connection is disconnected or disconnecting
//
CM_CONNECTION CmEntry;
if (!CMonitor::ConnTableGetEntry(m_szServiceName, &CmEntry) || CmEntry.CmState != CM_CONNECTED) { //
// The connection is disconnected by someone else, do not hangup.
//
CMTRACE(TEXT("CCmConnection::CmCustomHangup - Entry is not connected, canceling hangup")); return TRUE; }
//
// Call cmdial32.dll CmCustomHangup
//
//
// The destructor of CDynamicLibrary calls FreeLibrary
//
CDynamicLibrary LibCmdial; if (!LibCmdial.Load(TEXT("cmdial32.dll"))) { MYDBGASSERT(FALSE); return FALSE; }
CmCustomHangUpFUNC pfnCmCustomHangUp;
pfnCmCustomHangUp = (CmCustomHangUpFUNC)LibCmdial.GetProcAddress(c_pszCmHangup); MYDBGASSERT(pfnCmCustomHangUp);
if (pfnCmCustomHangUp) { //
// hRasConn = NULL,
// fIgnoreRefCount = TRUE, always except for InetDialHandler
// fPersist = fPromptReconnect, do not remove the entry if
// we are going to prompt for reconnect
//
DWORD dwRet; dwRet = pfnCmCustomHangUp(NULL, m_szServiceName, TRUE, fPromptReconnect); CMTRACE1(TEXT("CCmConnection::CmCustomHangup -- Return Value from CmCustomHangup is %u"), dwRet); return (ERROR_SUCCESS == dwRet); }
return FALSE;
}
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::CallRasConnectionNotification
//
// Synopsis: call RasConnectionNotify. Ras will set the event when connection
// is lost
//
// Arguments: HRASCONN hRasDial - The dial ras handle
// HRASCONN hRasTunnel - The tunnel ras handle
//
// Returns: HANDLE - the event handle, or NULL if failed
//
// History: Created Header 2/17/98
//
//+----------------------------------------------------------------------------
HANDLE CCmConnection::CallRasConnectionNotification(HRASCONN hRasDial, HRASCONN hRasTunnel) { DWORD dwRes; MYDBGASSERT(hRasDial || hRasTunnel);
//
// Call RasConnectionNotification. Ras will call us back when connection is losted.
// However, this fuction is not avaliable for Win95 with DUN 1.0
//
if (!m_RasApiDll.HasRasConnectionNotification()) { return NULL; }
HANDLE hEvent = NULL;
if (OS_W9X) { //
// Create an manual-reset non-signaled event on Win95, Win98 & WinME
//
hEvent = CreateEventU(NULL, TRUE, FALSE, NULL); } else { //
// Create an auto-reset non-signaled event
//
hEvent = CreateEventU(NULL, FALSE, FALSE, NULL);
}
//
// v-vijayb: Changed to use INVALID_HANDLE_VALUE(notify for all disconnects), as we where
// not getting notified after a reconnect or after connecting thru winlogon.
// StateConnectedGetEvent() will check if this connection is lost to determine if it was
// a disconnection of this connection or some other.
//
if (hRasDial) { //
// Copied from RAS.h
// #if (WINVER >= 0x401)
//
#define RASCN_Disconnection 0x00000002
if (OS_NT) { dwRes = m_RasApiDll.RasConnectionNotification((HRASCONN) INVALID_HANDLE_VALUE, hEvent, RASCN_Disconnection); } else { dwRes = m_RasApiDll.RasConnectionNotification(hRasDial, hEvent, RASCN_Disconnection); } if (dwRes != ERROR_SUCCESS) { CMASSERTMSG(FALSE, TEXT("RasConnectionNotification Failed")); CloseHandle(hEvent); return NULL; } }
if (hRasTunnel) { if (OS_NT) { dwRes = m_RasApiDll.RasConnectionNotification((HRASCONN) INVALID_HANDLE_VALUE, hEvent, RASCN_Disconnection); } else { dwRes = m_RasApiDll.RasConnectionNotification(hRasTunnel, hEvent, RASCN_Disconnection); } if (dwRes != ERROR_SUCCESS) { CMASSERTMSG(FALSE, TEXT("RasConnectionNotification Failed")); CloseHandle(hEvent); return NULL; } }
return hEvent; }
//+---------------------------------------------------------------------------
//
// struct RASMON_THREAD_INFO
//
// Description: Information passed to RasMonitorDlgThread by OnStatusDetails
//
// History: fengsun Created 2/11/98
//
//----------------------------------------------------------------------------
struct RASMON_THREAD_INFO { HRASCONN hRasConn; // the ras handle to display status
HWND hwndParent; // the parent window for the RasMonitorDlg
};
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::OnStatusDetails
//
// Synopsis: Called upon pressing detailed button on NT status dlg
// Call RasMonitorDlg to display the dial-up monitor
//
// Arguments: None
//
// Returns: Nothing
//
// History: fengsun Created Header 2/11/98
//
//+----------------------------------------------------------------------------
void CCmConnection::OnStatusDetails() { ASSERT_VALID(this); //
// RasDlg.dll is not available for Win9X
//
MYDBGASSERT(OS_NT4);
if (!OS_NT4) { return; }
//
// RasMonitorDlg pops up a modal dialog box, which will block the thread message loop.
// No thread message or event can be processed
// Create another thread to call RasMonitorDlg
//
//
// Alloc the parametor from heap. It is not safe to use stack here.
// CreateThread can return before the thread routine is called
// The thread is responsible to free the pointer
//
RASMON_THREAD_INFO* pInfo = (RASMON_THREAD_INFO*)CmMalloc(sizeof(RASMON_THREAD_INFO)); if (NULL == pInfo) { CMTRACE(TEXT("CCmConnection::OnStatusDetails alloc for pInfo failed")); return; } pInfo->hRasConn = m_hRasTunnel?m_hRasTunnel : m_hRasDial; pInfo->hwndParent = m_StatusDlg.GetHwnd();
DWORD dwThreadId; HANDLE hThread; if ((hThread = CreateThread(NULL, 0, RasMonitorDlgThread ,pInfo,0,&dwThreadId)) == NULL) { MYDBGASSERT(FALSE); CMTRACE(TEXT("CCmConnection::OnStatusDetails CreateThread failed")); CmFree(pInfo); return ; }
CloseHandle(hThread); }
//+----------------------------------------------------------------------------
//
// Function: static CCmConnection::RasMonitorDlgThread
//
// Synopsis: The thread to call RasMonitorDlg to avoid blocking the thread
// message loop. RasMonitorDlg is a modal dialogbox. The thead exits
// when the dialog is closed. That happens when user close the dialog box,
// or m_StatusDlg.KillRasMonitorWindow() is called
//
// Arguments: LPVOID lParam - RASMON_THREAD_INFO* the information passed to the thread
//
// Returns: DWORD WINAPI - The thread return value
//
// History: fengsun Created Header 2/19/98
//
//+----------------------------------------------------------------------------
DWORD WINAPI CCmConnection::RasMonitorDlgThread(LPVOID lParam) { MYDBGASSERT(lParam);
RASMON_THREAD_INFO* pInfo = (RASMON_THREAD_INFO*)lParam;
MYDBGASSERT(pInfo->hRasConn); //
// Get the device name first, if tunnel is available, use tunnel device
//
RASCONNSTATUS rcsStatus; memset(&rcsStatus,0,sizeof(rcsStatus)); rcsStatus.dwSize = sizeof(rcsStatus);
//
// Load Rasapi32.dll here. This dll is not loaded on NT
// Destructor will unload the DLL
//
CRasApiDll rasApiDll; if (!rasApiDll.Load()) { MYDBGASSERT(FALSE); CmFree(pInfo); return 1; }
DWORD dwRes = rasApiDll.RasGetConnectStatus(pInfo->hRasConn, &rcsStatus); CMASSERTMSG(dwRes == ERROR_SUCCESS, TEXT("RasGetConnectStatus failed"));
//
// The connection is lost. Still call RasMonitorDlg with empty name
//
RASMONITORDLG RasInfo; WORD (WINAPI *pfnFunc)(LPTSTR,LPRASMONITORDLG) = NULL;
ZeroMemory(&RasInfo,sizeof(RasInfo)); RasInfo.dwSize = sizeof(RasInfo); RasInfo.hwndOwner = pInfo->hwndParent; RasInfo.dwStartPage = RASMDPAGE_Status;
CmFree(pInfo);
//
// Call rasdlg.dll -> RasMonitorDlg
//
//
// The destructor of CDynamicLibrary calls FreeLibrary
//
CDynamicLibrary LibRasdlg;
if (!LibRasdlg.Load(TEXT("RASDLG.DLL"))) { CMTRACE1(TEXT("Rasdlg.dll LoadLibrary() failed, GLE=%u."), GetLastError()); return 1; }
pfnFunc = (WORD (WINAPI *)(LPTSTR,LPRASMONITORDLG))LibRasdlg.GetProcAddress("RasMonitorDlg"A_W); if (pfnFunc) { pfnFunc(rcsStatus.szDeviceName, &RasInfo); }
LibRasdlg.Unload(); rasApiDll.Unload();
//
// Minimize the working set before exit the thread.
//
CMonitor::MinimizeWorkingSet(); return 0; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::StatePrompt
//
// Synopsis: The connection is in the prompt-reconnect stste
// Run the message loop until state is changed
//
// Arguments: None
//
// Returns: CONN_STATE - The new state, either STATE_TERMINATED or
// STATE_RECONNECTING
//
// History: fengsun Created Header 2/4/98
//
//+----------------------------------------------------------------------------
CCmConnection::CONN_STATE CCmConnection::StatePrompt() { ASSERT_VALID(this); MYDBGASSERT(m_dwState == STATE_PROMPT_RECONNECT); // MYDBGASSERT(!IsWindow(m_StatusDlg.GetHwnd()));
CMTRACE(TEXT("Enter StatePrompt"));
LPTSTR pszReconnectMsg = CmFmtMsg(CMonitor::GetInstance(),IDMSG_RECONNECT,m_szServiceName);
m_ReconnectDlg.Create(CMonitor::GetInstance(), NULL, pszReconnectMsg,m_hBigIcon);
CmFree(pszReconnectMsg);
//
// Change the window position, so multiple status window will not be at
// the same position
//
PositionWindow(m_ReconnectDlg.GetHwnd(), m_dwPositionId);
//
// Change the dialog titlebar icon. This does not work,
// Reconnect dialog does not have system menu icon
//
SendMessageU(m_ReconnectDlg.GetHwnd(),WM_SETICON,ICON_BIG,(LPARAM) m_hBigIcon); SendMessageU(m_ReconnectDlg.GetHwnd(),WM_SETICON,ICON_SMALL,(LPARAM) m_hSmallIcon);
//
// Minimize the working set
//
CMonitor::MinimizeWorkingSet();
MSG msg; while(GetMessageU(&msg, NULL,0,0)) { if (msg.hwnd == NULL) { //
// it is a thread message
//
MYDBGASSERT((msg.message >= WM_APP) || (msg.message == WM_CONN_EVENT)); if (msg.message == WM_CONN_EVENT) { MYDBGASSERT(msg.wParam == EVENT_USER_DISCONNECT || msg.wParam == EVENT_RECONNECT || msg.wParam == EVENT_CMDIAL_HANGUP); break; } } else { //
// Also dispatch message for Modeless dialog box
//
if (!IsDialogMessageU(m_ReconnectDlg.GetHwnd(), &msg)) { TranslateMessage(&msg); DispatchMessageU(&msg); } }
}
//
// If the status window is not destroyed yet, destroy it now
//
if (IsWindow(m_ReconnectDlg.GetHwnd())) { DestroyWindow(m_ReconnectDlg.GetHwnd()); }
if (msg.wParam == EVENT_RECONNECT) { return STATE_RECONNECTING; } else { return STATE_TERMINATED; } }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::OnTrayIcon
//
// Synopsis: called Upon tray icon message
//
// Arguments: WPARAM wParam - wParam of the message
// LPARAM lParam - lParam of the message
//
// Returns: DWORD - return value of the message
//
// History: fengsun Created Header 2/4/98
//
//+----------------------------------------------------------------------------
DWORD CCmConnection::OnTrayIcon(WPARAM, LPARAM lParam) { ASSERT_VALID(this); MYDBGASSERT(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN);
switch (lParam) { case WM_LBUTTONDBLCLK: //
// Don't show the UI if we are at Winlogon unless we are on NT4
//
if (!IsLogonAsSystem() || OS_NT4) { m_StatusDlg.BringToTop(); } break;
case WM_RBUTTONUP: { //
// Popup the tray icon menu at the mouse location
//
POINT PosMouse; if (!GetCursorPos(&PosMouse)) { MYDBGASSERT(FALSE); break; } //
// From Microsoft Knowledge Base
// PRB: Menus for Notification Icons Don't Work Correctly
// To correct the first behavior, you need to make the current window
// the foreground window before calling TrackPopupMenu
//
// see also fixes for Whistler bugs 41696 and 90576
//
if (FALSE == SetForegroundWindow(m_StatusDlg.GetHwnd())) { CMTRACE(TEXT("SetForegroundWindow before TrackPopupMenu failed")); }
m_TrayIcon.PopupMenu(PosMouse.x, PosMouse.y, m_StatusDlg.GetHwnd()); PostMessageU(m_StatusDlg.GetHwnd(), WM_NULL, 0, 0); } break;
default: break; }
return TRUE; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::OnStayOnLine
//
// Synopsis: Called when "Stay Online" button is pressed in the disconnect
// count down dialog box
//
// Arguments: None
//
// Returns: Nothing
//
// History: fengsun Created Header 2/11/98
//
//+----------------------------------------------------------------------------
void CCmConnection::OnStayOnLine() { ASSERT_VALID(this); //
// Change the dialog to display status
//
m_StatusDlg.ChangeToStatus(); m_dwState = STATE_CONNECTED;
if (m_WatchProcess.IsIdle()) { //
// If idle bacause of no watching process
// User clickes stay online, 0 watch process is not idle any more
//
m_WatchProcess.SetNotIdle(); }
if (m_IdleStatistics.IsIdleTimeout()) { //
// If idle bacause of no traffic
// User clickes stay online, restart the idle counter
//
m_IdleStatistics.Reset(); } }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::PostHangupMsg
//
// Synopsis: Called by monitor to clean up the connection. The request was
// come from cmdial to remove tray icon and status dialog
// cmdial is responsible to actually hangup the connection
//
// Arguments: None
//
// Returns: Nothing
//
// History: fengsun Created Header 2/11/98
//
//+----------------------------------------------------------------------------
void CCmConnection::PostHangupMsg() const { //
// NOTE: This function is called within the Monitor thread
// Do not referrence any volatile data
// The CMMON_HANGUP_INFO request can come in at any state
//
//
// Post a message, so this will be handled in the connection thread
//
BOOL fRet = PostThreadMessageU(m_dwThreadId,WM_CONN_EVENT, EVENT_CMDIAL_HANGUP, 0); #if DBG
if (FALSE == fRet) { CMTRACE1(TEXT("CCmConnection::PostHangupMsg -- PostThreadMessage failed (GLE = %d)"), GetLastError()); } #endif
}
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::Reconnect
//
// Synopsis: The connection is in reconnecting state
// Simply load cmdial32.dll and call CmCustomDialDlg
//
// Arguments: None
//
// Returns: BOOL whether reconnect successfully
//
// History: fengsun Created Header 2/11/98
//
//+----------------------------------------------------------------------------
BOOL CCmConnection::Reconnect() { ASSERT_VALID(this); MYDBGASSERT(m_dwState == STATE_RECONNECTING);
CMTRACE(TEXT("Enter Reconnect"));
//
// Load cmdial32.dll and call CmReConnect();
//
//
// The destructor of CDynamicLibrary calls FreeLibrary
//
CDynamicLibrary LibCmdial; if (!LibCmdial.Load(TEXT("cmdial32.dll"))) { MYDBGASSERT(FALSE); return FALSE; }
CmReConnectFUNC pfnCmReConnect;
pfnCmReConnect = (CmReConnectFUNC)LibCmdial.GetProcAddress(c_pszCmReconnect); MYDBGASSERT(pfnCmReConnect);
if (!pfnCmReConnect) { return FALSE; }
//
// Log that we're reconnecting
//
m_Log.Log(RECONNECT_EVENT);
//
// If we have a RAS phonebook name pass it to reconnect, else NULL for system
//
if (m_szRasPhoneBook[0]) { return (pfnCmReConnect(m_szRasPhoneBook, m_szServiceName, &m_ReconnectInfo)); } else { return (pfnCmReConnect(NULL, m_szServiceName, &m_ReconnectInfo)); } }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::CheckRasConnection
//
// Synopsis: Check whether the RAS connection is still connected
//
// Arguments: OUT BOOL& fLostConnection -
// If the no longer connected, TRUE means lost connection
// FALSE means user disconnect
//
// Returns: BOOL - Whether still connected
//
// History: fengsun Created Header 2/8/98
//
//+----------------------------------------------------------------------------
BOOL CCmConnection::CheckRasConnection(OUT BOOL& fLostConnection) { ASSERT_VALID(this);
MYDBGASSERT(m_hRasDial != NULL || m_hRasTunnel != NULL);
// Whether we are still connected
BOOL fConnected = TRUE; RASCONNSTATUS rcsStatus; DWORD dwRes = ERROR_SUCCESS;
if (NULL == m_hRasDial && NULL == m_hRasTunnel) { //
// If both m_hRasTunnel and m_hRasDial are NULL, we're definitely not
// connected. Yes, we have lost the connection, and return value is FALSE.
//
fLostConnection = TRUE; return FALSE; }
// check tunnel status first
if (m_hRasTunnel != NULL) { memset(&rcsStatus,0,sizeof(rcsStatus)); rcsStatus.dwSize = sizeof(rcsStatus);
//
// This function will load RASAPI32.dll, if not loaded yet
// Will not unload RASAPI32.dll, since this function is called on timer
//
dwRes = m_RasApiDll.RasGetConnectStatus(m_hRasTunnel, &rcsStatus); if (dwRes != ERROR_SUCCESS || rcsStatus.rasconnstate == RASCS_Disconnected) { //
// The connection is lost
//
fConnected = FALSE; } }
// check dialup connection status
if (fConnected && m_hRasDial != NULL) { memset(&rcsStatus,0,sizeof(rcsStatus)); rcsStatus.dwSize = sizeof(rcsStatus); dwRes = m_RasApiDll.RasGetConnectStatus(m_hRasDial, &rcsStatus); }
if ((dwRes == ERROR_SUCCESS) && ((rcsStatus.rasconnstate != RASCS_Disconnected) || (rcsStatus.dwError == PENDING))) { // CMTRACE(TEXT("CCmConnection::CheckRasConnection - rcsStatus.rasconnstate != RASCS_Disconnected || rcsStatus.dwError == PENDING"));
return TRUE; }
//
// CM is no longer connected
//
CMTRACE3(TEXT("OnTimer() RasGetConnectStatus() returns %u, rasconnstate=%u, dwError=%u."), dwRes, rcsStatus.rasconnstate, rcsStatus.dwError);
if (rcsStatus.dwError == ERROR_USER_DISCONNECTION && OS_W9X) { fLostConnection = FALSE;
//
// On NT, ERROR_USER_DISCONNECTION is received in
// the event of idle disconnect which we consider
// a lost connection
//
} else { fLostConnection = TRUE; }
return FALSE; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::OnEndSession
//
// Synopsis: Called upon WM_ENDSESSION message
//
// Arguments: BOOL fEndSession - whether the session is being ended, wParam
// BOOL fLogOff - whether the user is logging off or shutting down,
// lParam
//
// Returns: Nothing
//
// History: fengsun Created Header 5/4/98
//
//+----------------------------------------------------------------------------
BOOL CCmConnection::OnEndSession(BOOL fEndSession, BOOL) { CMTRACE(TEXT("CCmConnection::OnEndSession")); if (fEndSession) { //
// The session can end any time after this function returns
// If we are connected, hangup the connection
//
if(m_dwState == STATE_CONNECTED || m_dwState == STATE_COUNTDOWN) { return CmCustomHangup(FALSE, TRUE); // fPromptReconnect = FALSE, fEndSession = TRUE
} }
return TRUE; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::OnAdditionalTrayMenu
//
// Synopsis: Called upon additional menuitem is selected from tray icon menu
//
// Arguments: WORD nCmd - LOWORD(wParam) of WM_COMMAND, the menu id
//
// Returns: Nothing
//
// History: fengsun Created Header 2/12/98
//
//+----------------------------------------------------------------------------
void CCmConnection::OnAdditionalTrayMenu(WORD nCmd) { ASSERT_VALID(this); MYDBGASSERT( (nCmd >= IDM_TRAYMENU) && nCmd <(IDM_TRAYMENU + m_TrayIcon.GetAdditionalMenuNum()));
nCmd -= IDM_TRAYMENU; // get the index for the command
if (nCmd >= m_TrayIcon.GetAdditionalMenuNum()) { return; }
//
// Run the command line
//
ExecCmdLine(m_TrayIcon.GetMenuCommand(nCmd), m_IniService.GetFile()); }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::GetProcessId
//
// Synopsis: Find the process specified by pszModule & returns its PID
//
// Arguments: WCHAR *pszModule
//
// Returns: PID of process or 0 if not found
//
// History: v-vijayb Created 7/20/99
//
//+----------------------------------------------------------------------------
DWORD CCmConnection::GetProcessId(WCHAR *pszModule) { DWORD dwPID = 0; HINSTANCE hInstLib; DWORD cbPIDs, cbNeeded, iPID, cPIDs; DWORD *pdwPIDs = NULL; HANDLE hProcess; HMODULE hMod; WCHAR szFileName[MAX_PATH + 1]; //
// PSAPI Function Pointers.
//
BOOL (WINAPI *lpfEnumProcesses)(DWORD *, DWORD cb, DWORD *); BOOL (WINAPI *lpfEnumProcessModules)(HANDLE, HMODULE *, DWORD, LPDWORD); DWORD (WINAPI *lpfGetModuleBaseName)(HANDLE, HMODULE, WCHAR *, DWORD);
hInstLib = LoadLibrary(TEXT("PSAPI.DLL")); if (hInstLib == NULL) { return (0); }
//
// Get procedure addresses.
//
lpfEnumProcesses = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*)) GetProcAddress(hInstLib, "EnumProcesses") ; lpfEnumProcessModules = (BOOL(WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress(hInstLib, "EnumProcessModules") ; lpfGetModuleBaseName =(DWORD (WINAPI *)(HANDLE, HMODULE, WCHAR *, DWORD)) GetProcAddress(hInstLib, "GetModuleBaseNameW") ; if (lpfEnumProcesses == NULL || lpfEnumProcessModules == NULL || lpfGetModuleBaseName == NULL) { goto OnError; }
cbPIDs = 256 * sizeof(DWORD); cbNeeded = 0; do { if (pdwPIDs != NULL) { HeapFree(GetProcessHeap(), 0, pdwPIDs); cbPIDs = cbNeeded; } pdwPIDs = (DWORD *) CmMalloc(cbPIDs); if (pdwPIDs == NULL) { goto OnError; } if (!lpfEnumProcesses(pdwPIDs, cbPIDs, &cbNeeded)) { goto OnError; }
} while (cbNeeded > cbPIDs);
cPIDs = cbNeeded / sizeof(DWORD); for (iPID = 0; iPID < cPIDs; iPID ++) { szFileName[0] = 0; hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pdwPIDs[iPID]); if (hProcess != NULL) { if (lpfEnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) { if (lpfGetModuleBaseName(hProcess, hMod, szFileName, sizeof(szFileName))) { if (lstrcmpiW(pszModule, szFileName) == 0) { dwPID = pdwPIDs[iPID]; } } }
CloseHandle(hProcess); if (dwPID != 0) { break; } }
}
OnError:
if (pdwPIDs != NULL) { CmFree(pdwPIDs); } FreeLibrary(hInstLib);
return (dwPID); }
typedef BOOL (WINAPI *lpfDuplicateTokenEx)( HANDLE, DWORD, LPSECURITY_ATTRIBUTES, SECURITY_IMPERSONATION_LEVEL, TOKEN_TYPE, PHANDLE );
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::RunAsUser
//
// Synopsis: Run the action as an exe or other shell object on the choosen desktop
//
// Arguments: WCHAR *pszProgram - name of module to be launched
// WCHAR *pszParams - parameters to be passed to module
// WCHAR *pszDesktop - desktop on which to launch module
//
// Returns: HANDLE - The action Process handle, for Win32 only
//
// History: 07/19/99 v-vijayb Created
// 07/27/99 nickball Return codes and explicit path.
//
//+----------------------------------------------------------------------------
HANDLE CCmConnection::RunAsUser(WCHAR *pszProgram, WCHAR *pszParams, WCHAR *pszDesktop) { STARTUPINFOW StartupInfo = {0}; PROCESS_INFORMATION ProcessInfo = {0};
WCHAR szShell[MAX_PATH + 1]; DWORD dwPID; HANDLE hProcess = NULL; HANDLE hUserToken = NULL; HANDLE hProcessToken = NULL;
MYDBGASSERT(pszProgram); CMTRACE(TEXT("RunAsUser"));
//
// NOTE: Normally we only have one icon in the systray, being run by Explorer,
// thus any menuitem execution is done in the user's account. On NT4,
// we can't rely on the Connections Folder to handle this for us, so we
// create and manage the systray icon ourselves, but we have to make
// sure than any items executed, are executed using the User's account.
// Or, on NT5 and later, we can have the strange case where HideTrayIcon
// is set to 0, thus requiring us to create/manage a systray icon (so
// there are 2 connectoids in the systray), hence the code below checks
// for all flavors of NT (rather than just NT4).
//
if (!OS_NT) { MYDBGASSERT(FALSE); return NULL; }
//
// Get the PID for the shell. We expect explorer.exe, but could be others
//
GetProfileString(TEXT("windows"), TEXT("shell"), TEXT("explorer.exe"), szShell, MAX_PATH); dwPID = GetProcessId(szShell);
//
// Now extract the token from the shell process
//
if (dwPID) { //
// Get the Process handle from the PID
//
hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPID); CMTRACE1(TEXT("RunAsUser/OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwPID) returns 0x%x"), hProcess); if (hProcess) { //
// Get the token
//
if (OpenProcessToken(hProcess, TOKEN_ASSIGN_PRIMARY | TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_DUPLICATE, &hProcessToken)) { HINSTANCE hInstLibrary = LoadLibrary(TEXT("ADVAPI32.DLL")); CMTRACE1(TEXT("RunAsUser/LoadLibrary(ADVAPI32.DLL) returns 0x%x"), hInstLibrary);
//
// Get user token via DuplicateTokenEx and impersonate the user
//
if (hInstLibrary) { lpfDuplicateTokenEx lpfuncDuplicateTokenEx = (lpfDuplicateTokenEx) GetProcAddress(hInstLibrary, "DuplicateTokenEx"); CMTRACE1(TEXT("RunAsUser/GetProcAddress(hInstLibrary, DuplicateTokenEx) returns 0x%x"), lpfuncDuplicateTokenEx);
if (lpfuncDuplicateTokenEx) { if (lpfuncDuplicateTokenEx(hProcessToken, TOKEN_ASSIGN_PRIMARY | TOKEN_IMPERSONATE | TOKEN_READ | TOKEN_DUPLICATE, NULL, SecurityImpersonation, TokenPrimary, &hUserToken)) { BOOL bRes = ImpersonateLoggedOnUser(hUserToken);
if (FALSE == bRes) { hUserToken = NULL; CMTRACE1(TEXT("RunAsUser/ImpersonateLoggedOnUser failed, GLE=&u"), GetLastError()); } } }
FreeLibrary(hInstLibrary); }
CloseHandle(hProcessToken); }
CloseHandle(hProcess); } }
CMTRACE1(TEXT("RunAsUser - hUserToken is 0x%x"), hUserToken);
//
// Can't impersonate user, don't run because we're in the system account
//
if (NULL == hUserToken) { MYDBGASSERT(FALSE); return NULL; }
//
// Now prep CreateProcess
//
StartupInfo.cb = sizeof(StartupInfo); if (pszDesktop) { StartupInfo.lpDesktop = pszDesktop; StartupInfo.wShowWindow = SW_SHOW; }
//
// Build the path and params
//
LPWSTR pszwCommandLine = (LPWSTR) CmMalloc((2 + lstrlen(pszProgram) + 1 + lstrlen(pszParams) + 1) * sizeof(TCHAR));
if (NULL == pszwCommandLine) { MYDBGASSERT(FALSE); return NULL; }
//
// Copy path, but surround with double quotes for security
//
lstrcpyU(pszwCommandLine, TEXT("\"")); lstrcatU(pszwCommandLine, pszProgram); lstrcatU(pszwCommandLine, TEXT("\""));
if (pszParams[0] != L'\0') { lstrcatU(pszwCommandLine, TEXT(" ")); lstrcatU(pszwCommandLine, pszParams); } CMTRACE1(TEXT("RunAsUser/CreateProcessAsUser() - Launching %s"), pszwCommandLine); if (NULL == CreateProcessAsUser(hUserToken, NULL, pszwCommandLine, NULL, NULL, FALSE, CREATE_SEPARATE_WOW_VDM, NULL, NULL, &StartupInfo, &ProcessInfo)) { CMTRACE1(TEXT("RunAsUser/CreateProcessAsUser() failed, GLE=%u."), GetLastError()); ProcessInfo.hProcess = NULL; ProcessInfo.hThread = NULL; }
if (ProcessInfo.hThread) { CloseHandle(ProcessInfo.hThread); }
CloseHandle(hUserToken); RevertToSelf(); CmFree(pszwCommandLine); return (ProcessInfo.hProcess); }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::ExecCmdLine
//
// Synopsis: ShellExecute the command line
// The code is copied from CActionList::RunAsExe
//
// Arguments: const TCHAR* pszCmdLine - the command line to run, including
// arguments
// const TCHAR* pszCmsFile - Full path of CMS file
//
// Returns: BOOL - Whether ShellExecute succeed
//
// Notes: Menu actions consists of a command string and an optional argument string.
// The first delimited string is considered the command portion and anything
// thereafter is treated as the argument portion, which formatless and passed
// indiscriminately to ShellExecuteEx. Long filename command paths are
// enclosed in "+" signs by CMAK. Thus the following permutations are allowed:
//
// "C:\\Progra~1\\Custom.Exe"
// "C:\\Progra~1\\Custom.Exe Args"
// "+C:\\Program Files\\Custom.Exe+"
// "+C:\\Program Files\\Custom.Exe+ Args"
//
// History: 02/10/98 fengsun Created Header
// 02/09/99 nickball Fixed long filename bug one year later.
//
//+----------------------------------------------------------------------------
BOOL CCmConnection::ExecCmdLine(const TCHAR* pszCmdLine, const TCHAR* pszCmsFile) { LPTSTR pszArgs = NULL; LPTSTR pszCmd = NULL;
BOOL bRes = FALSE;
MYDBGASSERT(pszCmdLine); MYDBGASSERT(pszCmsFile);
if (NULL == pszCmdLine || NULL == pszCmsFile) { return FALSE; } CMTRACE1(TEXT("ExecCmdLine() pszCmdLine is %s"), pszCmdLine);
if (CmParsePath(pszCmdLine, pszCmsFile, &pszCmd, &pszArgs)) { CMTRACE1(TEXT("ExecCmdLine() pszCmd is %s"), pszCmd); CMTRACE1(TEXT("ExecCmdLine() pszArgs is %s"), pszArgs); //
// Now we have the exe name and args separated, execute it
//
if (IsLogonAsSystem()) { HANDLE hProcess = RunAsUser(pszCmd, pszArgs, TEXT("Winsta0\\default"));
if (hProcess) { CloseHandle(hProcess); bRes = TRUE; } else { bRes = FALSE; } } else { SHELLEXECUTEINFO seiInfo;
ZeroMemory(&seiInfo,sizeof(seiInfo)); seiInfo.cbSize = sizeof(seiInfo); seiInfo.fMask |= SEE_MASK_FLAG_NO_UI; seiInfo.lpFile = pszCmd; seiInfo.lpParameters = pszArgs; seiInfo.nShow = SW_SHOW;
//
// Load Shell32.dll and call Shell_ExecuteEx
//
CShellDll ShellDll(TRUE); // TRUE == don't unload shell32.dll because of bug 289463
bRes = ShellDll.ExecuteEx(&seiInfo);
CMTRACE2(TEXT("ExecCmdLine/ShellExecuteEx() returns %u - GLE=%u"), bRes, GetLastError()); } }
CmFree(pszCmd); CmFree(pszArgs); CMTRACE1(TEXT("ExecCmdLine() - Returns %d"), bRes); return bRes; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::LoadHelpFileName
//
// Synopsis: Get the connection help file name
//
// Arguments: None
//
// Returns: LPTSTR - The help file name, can be a empty string
// caller is responsible to free the pointer
//
// History: Created Header 2/13/98
//
//+----------------------------------------------------------------------------
LPTSTR CCmConnection::LoadHelpFileName() { //
// Read the filename from profile first
//
LPTSTR pszFullPath = NULL; LPTSTR pszFileName = m_IniService.GPPS(c_pszCmSection,c_pszCmEntryHelpFile);
if (pszFileName != NULL && pszFileName[0]) { //
// The help file name is relative to the .cmp file, convert it into full name
//
pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszFileName); }
CmFree(pszFileName); return pszFullPath; }
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::LoadConnectionIcons
//
// Synopsis: Load big and small icon of the connection
//
// Arguments: None
//
// Returns: Nothing
//
// History: Created Header 2/13/98
//
//+----------------------------------------------------------------------------
void CCmConnection::LoadConnectionIcons() { // Load large icon name
LPTSTR pszTmp = m_IniService.GPPS(c_pszCmSection, c_pszCmEntryBigIcon);
if (*pszTmp) { //
// The icon name is relative to the .cmp file, convert it into full name
//
LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszTmp);
m_hBigIcon = CmLoadIcon(CMonitor::GetInstance(), pszFullPath);
CmFree(pszFullPath); }
CmFree(pszTmp);
// Use default (EXE) large icon if no user icon found
if (!m_hBigIcon) { m_hBigIcon = CmLoadIcon(CMonitor::GetInstance(), MAKEINTRESOURCE(IDI_APP)); }
// Load the small Icon
// Load small icon name
pszTmp = m_IniService.GPPS(c_pszCmSection, c_pszCmEntrySmallIcon); if (*pszTmp) { //
// The icon name is relative to the .cmp file, convert it into full name
//
LPTSTR pszFullPath = CmBuildFullPathFromRelative(m_IniProfile.GetFile(), pszTmp); m_hSmallIcon = CmLoadSmallIcon(CMonitor::GetInstance(), pszFullPath);
CmFree(pszFullPath); } CmFree(pszTmp);
// Use default (EXE) small icon if no user icon found
if (!m_hSmallIcon) { m_hSmallIcon = CmLoadSmallIcon(CMonitor::GetInstance(), MAKEINTRESOURCE(IDI_APP)); }
}
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::PositionWindow
//
// Synopsis: Position the window according to dwPositionId, so multiple
// connection window would be positioned differently
//
// Arguments: HWND hWnd - The window to position
// dwPositionId - the id of the window
//
// Returns: Nothing
//
// History: fengsun Created Header 3/25/98
//
//+----------------------------------------------------------------------------
void CCmConnection::PositionWindow(HWND hWnd, DWORD dwPositionId) { MYDBGASSERT(IsWindow(hWnd));
//
// Get the rect of this window
//
RECT rcDlg; GetWindowRect(hWnd, &rcDlg);
//
// center within work area
//
RECT rcArea; SystemParametersInfoA(SPI_GETWORKAREA, NULL, &rcArea, NULL);
//
// Position the window on the desktop work area according to the PositionId
// x = Desktop.midX - width/2
// y = Desktop.midY - hight/2 + hight * (PostitionId%3 -1)
// X position is always the same
// Y position repeat for every three time
//
int xLeft = (rcArea.left + rcArea.right) / 2 - (rcDlg.right - rcDlg.left) / 2; int yTop = (rcArea.top + rcArea.bottom) / 2 - (rcDlg.bottom - rcDlg.top) / 2 + (rcDlg.bottom - rcDlg.top) * ((int)dwPositionId%3 -1);
//
// if the dialog is outside the screen, move it inside
//
if (xLeft < rcArea.left) { xLeft = rcArea.left; } else if (xLeft + (rcDlg.right - rcDlg.left) > rcArea.right) { xLeft = rcArea.right - (rcDlg.right - rcDlg.left); }
if (yTop < rcArea.top) { yTop = rcArea.top; } else if (yTop + (rcDlg.bottom - rcDlg.top) > rcArea.bottom) { yTop = rcArea.bottom - (rcDlg.bottom - rcDlg.top); }
SetWindowPos(hWnd, NULL, xLeft, yTop, -1, -1, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
#ifdef DEBUG
//+----------------------------------------------------------------------------
//
// Function: CCmConnection::AssertValid
//
// Synopsis: For debug purpose only, assert the connection object is valid
//
// Arguments: None
//
// Returns: Nothing
//
// History: Created Header 2/12/98
//
//+----------------------------------------------------------------------------
void CCmConnection::AssertValid() const { MYDBGASSERT(m_dwState >= 0 && m_dwState <= STATE_TERMINATED); MYDBGASSERT(m_hRasDial != NULL || m_hRasTunnel != NULL); ASSERT_VALID(&m_ConnStatistics); ASSERT_VALID(&m_IdleStatistics); ASSERT_VALID(&m_StatusDlg);
// ASSERT_VALID(m_TrayIcon);
// ASSERT_VALID(m_WatchProcess);
} #endif
|