Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2887 lines
86 KiB

//+----------------------------------------------------------------------------
//
// 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