|
|
//+----------------------------------------------------------------------------
//
// File: ConnStat.cpp
//
// Module: CMMON32.EXE
//
// Synopsis: Implementation of class CConnStatistics
//
// Copyright (c) 1998-1999 Microsoft Corporation
//
// Author: Fengsun Created 10/15/97
//
//+----------------------------------------------------------------------------
#include "cmmaster.h"
#include "ConnStat.h"
#include "cm_misc.h" // for MYDBGASSERT
#include "DynamicLib.h"
#include "resource.h"
#include "perf_str.h"
//
// DeviceIoControl code
//
#define UNIMODEM_IOCTL_GET_STATISTICS 0x0000a007
//
// Constructor and destructor
//
CConnStatistics::CConnStatistics() { m_TrafficRing.Reset(); m_dwReadPerSecond = m_dwWritePerSecond = m_dwBaudRate = m_dwDuration = 0; m_dwInitBytesRead = m_dwInitBytesWrite = (DWORD)-1; m_hStatDevice = NULL; m_hKey = NULL; m_fAdapter2 = FALSE; m_fAdapterSet = FALSE; m_pszTotalBytesRecvd = m_pszTotalBytesXmit = m_pszConnectSpeed = NULL; }
CConnStatistics::~CConnStatistics() { Close(); }
//+----------------------------------------------------------------------------
//
// Function: CConnStatistics::OpenByDevice
//
// Synopsis:
//
// Arguments: HRASCONN hrcRasConn - the ras connection handle, needed for
// non-tunnle connection, when registry is not available
//
// Returns: BOOL - Whether open succeeded.
// Because the TAPI device handle maybe available later from cmstat dll
// Use IsAvailable() to see whether statistics is available
//
// History: fengsun Created Header 10/29/97
//
//+----------------------------------------------------------------------------
BOOL CConnStatistics::OpenByDevice(HRASCONN hrcRasConn) { MYDBGASSERT(OS_W95); MYDBGASSERT(!IsAvailable()); MYDBGASSERT(hrcRasConn);
if (GetDeviceHandle(hrcRasConn)) { return TRUE; }
//
// NOTE: For win95 gold, GetDeviceHandle will fail if TAPI 2.1 is installed.
// We used to have a hack there to hook the lights.exe. We decided to take
// it out and to let the setup program ask user to upgrade TAPI or DUN. We
// dropped HookLight(), because it does not work for multiple connections.
//
MYDBGASSERT(FALSE); return FALSE; }
//+---------------------------------------------------------------------------
//
// Function: Open()
//
// Synopsis: Encapsulates the opening of the statistics data store
//
// Arguments: HINSTANCE hInst - The instance to LoadString "Dial-up Adapter"
// DWORD dwInitBytesRecv - Initial value of TotalBytesRecvd
// DWORD dwInitBytesSend - Initial value of TotalBytesXmit
// HRASCONN hDial - Handle to dial-up connection, if any
// HRASCONN hTunnel - Handle to tunnel connection, if any
//
// Returns: TRUE if succeed
// FALSE otherwise
//
// History: nickball 03/04/00 Created. Wrapped existing code.
//
// Note: This function initialize the connection statistics from one
// of three places.
//
// 1) W98 registry
// 2) NT5 RAS API
// 3) W95 Tapi device handle.
//
//----------------------------------------------------------------------------
void CConnStatistics::Open(HINSTANCE hInst, DWORD dwInitBytesRecv, DWORD dwInitBytesSend, HRASCONN hDial, HRASCONN hTunnel) { //
// Start statistics
//
if (OS_NT5) { OpenByStatisticsApi(dwInitBytesRecv, dwInitBytesSend, hDial, hTunnel); } else { OpenByPerformanceKey(hInst, dwInitBytesRecv, dwInitBytesSend); }
//
// See if we have stats, go with plan B if not.
//
if (!IsAvailable()) { //
// On W95, we have a fallback position of hooking the TAPI handle
// via RAS, so use it. Note: We will retry initializing stats on every
// timer tick if we don't get them here, so all is not lost for W98.
// Note that we only check hDial here because if you are on win95 without
// MSDUN 1.2, you aren't able to tunnel.
//
if (OS_W95 && hDial) { OpenByDevice(hDial); } } }
//+---------------------------------------------------------------------------
//
// Function: OpenByStatisticsApi()
//
// Synopsis: Sets initial values and makes sure the RasApis are loaded.
//
// Arguments: DWORD dwInitBytesRecv - Initial value of TotalBytesRecvd
// DWORD dwInitBytesSend - Initial value of TotalBytesXmit
// HRASCONN hDial - Handle to dial-up connection, if any
// HRASCONN hTunnel - Handle to tunnel connection, if any
//
// Returns: Nothing
//
// History: nickball 03/04/00 Created from OpenByPerformanceKey
//
//----------------------------------------------------------------------------
void CConnStatistics::OpenByStatisticsApi(DWORD dwInitBytesRecv, DWORD dwInitBytesSend, HRASCONN hDial, HRASCONN hTunnel) { //
// Initialize our APIs
//
m_RasApiDll.Load(); //
// Get the handle that we'll use to look up stats.
// Try tunnel first, then drop back to dial-up
//
CMTRACE2(TEXT("CConnStatistics::OpenByStatisticsApi() hTunnel is 0x%x and hDial is 0x%x"), hTunnel, hDial);
m_hRasConn = hTunnel ? hTunnel : hDial;
//
// Init the bytes sent and received with whatever was pushed down to us.
//
m_dwInitBytesRead = dwInitBytesRecv; m_dwInitBytesWrite = dwInitBytesSend; }
//+---------------------------------------------------------------------------
//
// Function: OpenByPerformanceKey()
//
// Synopsis: Open the registry key for Dial-Up Adapter Performance Data
//
// Arguments: HINSTANCE hInst - The instance to LoadString "Dial-up Adapter"
// DWORD dwInitBytesRecv - Initial value of TotalBytesRecvd
// DWORD dwInitBytesSend - Initial value of TotalBytesXmit
//
// Returns: TRUE if succeed
// FALSE otherwise
//
// History: byao 07/16/97 Created
// fengsun 10/01/97 Make it a member fuction
// nickball 11/14/98 If key exists, use it
//
// Note: This function initialize the connection statistics from the
// registry. It is used when the initial bytes sent/recvd are
// known as is the case when CMDIAL hands off to CMMON.
//
//----------------------------------------------------------------------------
void CConnStatistics::OpenByPerformanceKey(HINSTANCE hInst, DWORD dwInitBytesRecv, DWORD dwInitBytesSend) { //
// If available, there's nothing to do here
//
if (IsAvailable() || !m_fAdapterSet) { MYDBGASSERT(FALSE); return;
} //
// We haven't opened the key yet, try to do so
//
MYDBGASSERT(!m_hKey);
if (m_hKey) { RegCloseKey(m_hKey); m_hKey = NULL; }
DWORD dwErrCode = RegOpenKeyExU( HKEY_DYN_DATA, c_pszDialupPerfKey, 0, KEY_ALL_ACCESS, &m_hKey );
if (dwErrCode != ERROR_SUCCESS) { CMTRACE1(TEXT("OpenDAPPerfKey() RegOpenKeyEx() returned GLE=%u."), dwErrCode); m_hKey = NULL; return; }
m_dwInitBytesRead = dwInitBytesRecv; m_dwInitBytesWrite = dwInitBytesSend;
GetStatRegValues(hInst);
//
// If intial values are -1, reget the initial values.
//
if (((DWORD)-1 == dwInitBytesRecv) || ((DWORD)-1 == dwInitBytesSend)) { //
// Get the initial statistics info
//
if (!GetPerfData(m_dwInitBytesRead, m_dwInitBytesWrite, m_dwBaudRate)) { //
// No dial-up statistic info
//
RegCloseKey(m_hKey); m_hKey = NULL;
CMTRACE(TEXT("CConnStatistics::OpenByPerformanceKey() - failed to find stats")); } } }
//+----------------------------------------------------------------------------
//
// Function: CConnStatistics::GetStatRegValues
//
// Synopsis: Helper method, builds the reg value names using the localized
// form of the word "Dial-up Adapter".
//
// Arguments: HINSTANCE hInst
//
// Returns: Nothing
//
// History: nickball Created 11/14/98
//
//+----------------------------------------------------------------------------
void CConnStatistics::GetStatRegValues(HINSTANCE hInst) { CMTRACE1(TEXT("CConnStatistics::GetStatRegValues - m_pszTotalBytesRecvd is %s"), m_pszTotalBytesRecvd);
//
// bug 149367 The word "Dial-up Adapter" need to be localized.
// Load it from resource if no loaded yet
//
if (m_pszTotalBytesRecvd == NULL) { m_pszTotalBytesRecvd = CmLoadString(hInst, IDS_REG_DIALUP_ADAPTER); CmStrCatAlloc(&m_pszTotalBytesRecvd, m_fAdapter2 ? c_pszDialup_2_TotalBytesRcvd : c_pszDialupTotalBytesRcvd);
m_pszTotalBytesXmit = CmLoadString(hInst, IDS_REG_DIALUP_ADAPTER); CmStrCatAlloc(&m_pszTotalBytesXmit, m_fAdapter2 ? c_pszDialup_2_TotalBytesXmit : c_pszDialupTotalBytesXmit);
m_pszConnectSpeed = CmLoadString(hInst, IDS_REG_DIALUP_ADAPTER); CmStrCatAlloc(&m_pszConnectSpeed, m_fAdapter2 ? c_pszDialup_2_ConnectSpeed : c_pszDialupConnectSpeed); } }
//+----------------------------------------------------------------------------
//
// Function: CConnStatistics::Close
//
// Synopsis: Stop gathering statistic and close the handle
//
// Arguments:
//
// Returns:
//
// History: Created Header 10/15/97
//
//+----------------------------------------------------------------------------
void CConnStatistics::Close() { if (m_hStatDevice) { BOOL bRes = CloseHandle(m_hStatDevice); m_hStatDevice = NULL;
#ifdef DEBUG
if (!bRes) { CMTRACE1(TEXT("CConnStatistics::Close() CloseHandle() failed, GLE=%u."), GetLastError()); } #endif
}
if (m_hKey) { DWORD dwErrCode = RegCloseKey(m_hKey); CMTRACE1(TEXT("Close() RegCloseKey() returned GLE=%u."), dwErrCode); m_hKey = NULL; }
CmFree( m_pszTotalBytesRecvd ); CmFree( m_pszTotalBytesXmit ); CmFree( m_pszConnectSpeed );
m_pszTotalBytesRecvd = m_pszTotalBytesXmit = m_pszConnectSpeed = NULL; }
//+----------------------------------------------------------------------------
//
// Function: CConnStatistics::Update
//
// Synopsis: Gather new statistic information
//
// Arguments: None
//
// Returns: Nothing
//
// History: Fengsun Created 10/15/97
//
//+----------------------------------------------------------------------------
void CConnStatistics::Update() { if (!IsAvailable()) { MYDBGASSERT(FALSE); return; } CTraffic curTraffic; curTraffic.dwTime = GetTickCount();
MYDBGASSERT(curTraffic.dwTime > m_TrafficRing.GetOldest().dwTime);
if (curTraffic.dwTime == m_TrafficRing.GetOldest().dwTime) { return; }
//
// Prefer performace registry data
//
if (OS_NT5) { RAS_STATS RasStats; ZeroMemory(&RasStats, sizeof(RasStats)); RasStats.dwSize = sizeof(RAS_STATS); DWORD dwRet = m_RasApiDll.RasGetConnectionStatistics(m_hRasConn, &RasStats);
if (ERROR_SUCCESS == dwRet) { curTraffic.dwRead = RasStats.dwBytesRcved; curTraffic.dwWrite = RasStats.dwBytesXmited; m_dwBaudRate = RasStats.dwBps; m_dwDuration = RasStats.dwConnectDuration; } } else { //
// Not NT5, try the registry
//
if (m_hKey) { if (!GetPerfData(curTraffic.dwRead, curTraffic.dwWrite, m_dwBaudRate)) { return; } curTraffic.dwRead -= m_dwInitBytesRead; curTraffic.dwWrite -= m_dwInitBytesWrite; } else { //
// Last resort for 9x, try to use stat device
//
if (m_hStatDevice) { if (!GetTapiDeviceStats(curTraffic.dwRead, curTraffic.dwWrite, m_dwBaudRate)) { BOOL bRes = CloseHandle(m_hStatDevice); m_hStatDevice = NULL;
if (!bRes) { CMTRACE1(TEXT("CConnStatistics::Update() CloseHandle() failed, GLE=%u."), GetLastError()); } return; } } else { MYDBGASSERT(m_hStatDevice); return; } } }
//
// Calculate the avarage between two interval
//
const CTraffic& lastTraffic = m_TrafficRing.GetOldest();
DWORD dwDeltaTime = curTraffic.dwTime - lastTraffic.dwTime; m_dwReadPerSecond = ((curTraffic.dwRead - lastTraffic.dwRead)*1000) /dwDeltaTime; m_dwWritePerSecond = ((curTraffic.dwWrite - lastTraffic.dwWrite)*1000) /dwDeltaTime;
m_TrafficRing.Add(curTraffic); }
//+---------------------------------------------------------------------------
//
// Function: GetPerfData
//
// Synopsis: Get Performance Data from DUN1.2 performance registry
//
// Arguments:
//
// Returns: TRUE: succeed
// FALSE otherwise
//
// History: byao created 7/16/97
// fengsun change it into a member function 10/14/97
//
//----------------------------------------------------------------------------
BOOL CConnStatistics::GetPerfData(DWORD& dwRead, DWORD& dwWrite, DWORD& dwBaudRate) const { if (OS_W9X) { MYDBGASSERT(m_hKey != NULL); MYDBGASSERT(m_pszTotalBytesRecvd && *m_pszTotalBytesRecvd);
LONG dwErrCode;
DWORD dwValueSize, dwValueType; DWORD dwValue;
//
// "Dial-up Adapter\TotalBytesRecvd"
//
dwValueSize = sizeof(DWORD); dwErrCode = RegQueryValueExU( m_hKey, m_pszTotalBytesRecvd, NULL, &dwValueType, (PBYTE)&dwValue, &dwValueSize);
if (dwErrCode == ERROR_SUCCESS) { dwRead = dwValue; } else { CMTRACE2(TEXT("GetPerfData() RegQueryValueEx() %s failed and returned GLE=%u."), m_pszTotalBytesRecvd, dwErrCode);
return FALSE; }
//
// "Dial-up Adapter\TotalBytesXmit"
//
dwValueSize = sizeof(DWORD); dwErrCode = RegQueryValueExU( m_hKey, m_pszTotalBytesXmit, NULL, &dwValueType, (PBYTE)&dwValue, &dwValueSize);
if (dwErrCode == ERROR_SUCCESS) { dwWrite = dwValue; } else { CMTRACE2(TEXT("GetPerfData() RegQueryValueEx() %s failed and returned GLE=%u."), m_pszTotalBytesXmit, dwErrCode);
return FALSE; }
//
// "Dial-up Adapter\ConnectSpeed"
//
dwValueSize = sizeof(DWORD); dwErrCode = RegQueryValueExU( m_hKey, m_pszConnectSpeed, NULL, &dwValueType, (PBYTE)&dwValue, &dwValueSize);
if (dwErrCode == ERROR_SUCCESS) { dwBaudRate = dwValue; } else { CMTRACE2(TEXT("GetPerfData() RegQueryValueEx() %s failed and returned GLE=%u."), m_pszConnectSpeed, dwErrCode); return FALSE; } }
return TRUE; }
//+---------------------------------------------------------------------------
//
// Function: GetTapiDeviceStats
//
// Synopsis: Get Modem Performance Data by DeviceIoControl
//
// Arguments:
//
// Returns: TRUE: succeed
// FALSE otherwise
//
// History: byao created 7/16/97
// fengsun change it into a member function 10/14/97
//
//----------------------------------------------------------------------------
BOOL CConnStatistics::GetTapiDeviceStats(DWORD& dwRead, DWORD& dwWrite, DWORD& dwBaudRate) const { BOOL bRes; DWORD dwRet;
typedef struct tagAPISTATS { LPVOID hPort; DWORD fConnected; DWORD DCERate; DWORD dwPerfRead; DWORD dwPerfWrite; } APISTATS;
APISTATS ApiStats;
if (m_hStatDevice) { bRes = DeviceIoControl(m_hStatDevice, UNIMODEM_IOCTL_GET_STATISTICS, &ApiStats, sizeof(ApiStats), &ApiStats, sizeof(ApiStats), &dwRet, NULL); if (bRes && ApiStats.fConnected) { dwRead = ApiStats.dwPerfRead; dwWrite = ApiStats.dwPerfWrite; dwBaudRate = ApiStats.DCERate; return (TRUE); } CMTRACE(TEXT("GetTapiDeviceStats() DeviceIoControl() failed - disabling hStatDevice.")); }
return (FALSE); }
//+----------------------------------------------------------------------------
//
// Function: CConnStatistics::GetDeviceHandle
//
// Synopsis: Get the TAPI device handle
//
// Arguments: HRASCONN hrcRasConn - the ras connection handle
//
// Returns: BOOL - TRUE if succeed
//
// History: fengsun Created Header 10/29/97
//
//+----------------------------------------------------------------------------
BOOL CConnStatistics::GetDeviceHandle(HRASCONN hrcRasConn) { MYDBGASSERT(hrcRasConn); MYDBGASSERT(!m_hStatDevice);
typedef struct tagDEVICE_PORT_INFO { DWORD dwSize; HANDLE hDevicePort; HLINE hLine; HCALL hCall; DWORD dwAddressID; DWORD dwLinkSpeed; char szDeviceClass[RAS_MaxDeviceType+1]; } DEVICE_PORT_INFO, *LPDEVICE_PORT_INFO;
typedef struct tagMacInfo { VARSTRING varstring; HANDLE hCommDevice; char szDeviceClass[1]; } MacInfo;
typedef DWORD (WINAPI *RnaGetDevicePortFUNC)(HANDLE,LPDEVICE_PORT_INFO); RnaGetDevicePortFUNC pfnRnaGetDevicePort;
//
// Load rasapi32.dll and call RnaGetDevicePort
//
//
// The destructor of CDynamicLibrary automaticly call FreeLibrary
//
CDynamicLibrary RasLib;
if (!RasLib.Load(TEXT("rasapi32.dll"))) { CMTRACE1(TEXT("GetDeviceHandle() LoadLibrary() failed, GLE=%u."), GetLastError()); return FALSE; } pfnRnaGetDevicePort = (RnaGetDevicePortFUNC) RasLib.GetProcAddress("RnaGetDevicePort"); if (!pfnRnaGetDevicePort) { CMTRACE1(TEXT("GetDeviceHandle() GetProcAddress() failed, GLE=%u."), GetLastError()); return FALSE; }
DWORD dwRes; DEVICE_PORT_INFO dpi;
ZeroMemory(&dpi,sizeof(dpi)); dpi.dwSize = sizeof(dpi);
dwRes = pfnRnaGetDevicePort(hrcRasConn,&dpi); if (dwRes) { CMTRACE1(TEXT("GetDeviceHandle() RnaGetDevicePort() failed, GLE=%u."), dwRes); return FALSE; }
//
// Load TAPI32.dll
// CDynamicLibrary Free the lib on destructor
//
CDynamicLibrary LibTapi;
if (!LibTapi.Load(TEXT("TAPI32.DLL"))) { return FALSE; }
typedef LONG (WINAPI *TapiLineGetIDFUNC) (HLINE, DWORD, HCALL, DWORD, LPVARSTRING, LPCSTR);
//
// Always call the Ansi version since this is a Win9x only function
//
TapiLineGetIDFUNC pfnTapiLineGetID; pfnTapiLineGetID = (TapiLineGetIDFUNC) LibTapi.GetProcAddress("lineGetID");
if (pfnTapiLineGetID == NULL) { MYDBGASSERT(pfnTapiLineGetID != NULL); return FALSE; }
LONG lRes;
CMTRACE3(TEXT("GetDeviceHandle() hDevicePort=0x%x, hLine=0x%x, hCall=0x%x,"), dpi.hDevicePort, dpi.hLine, dpi.hCall); CMTRACE3(TEXT("\tdwAddressID=0x%x, dwLinkSpeed=%u, szDeviceClass=%s."), dpi.dwAddressID, dpi.dwLinkSpeed, dpi.szDeviceClass);
m_dwBaudRate = dpi.dwLinkSpeed; MacInfo* pmi = NULL; DWORD dwSize = sizeof(*pmi); do { CmFree(pmi); pmi = (MacInfo *) CmMalloc(dwSize); if (pmi == NULL) { lRes = ERROR_OUTOFMEMORY; break; }
pmi->varstring.dwTotalSize = dwSize;
lRes = pfnTapiLineGetID(dpi.hLine, dpi.dwAddressID, NULL, LINECALLSELECT_ADDRESS, &pmi->varstring, "comm/datamodem");
dwSize = pmi->varstring.dwNeededSize; } while(pmi->varstring.dwNeededSize > pmi->varstring.dwTotalSize);
#ifdef DEBUG
if (lRes) { CMTRACE1(TEXT("CConnStatistics::GetDeviceHandle() lineGetID() failed, GLE=%u."), lRes); } #endif
if (!lRes && pmi != NULL ) { m_hStatDevice = pmi->hCommDevice; }
CmFree(pmi);
return m_hStatDevice != NULL; }
#ifdef DEBUG
//+----------------------------------------------------------------------------
//
// Function: CConnStatistics::AssertValid
//
// Synopsis: For debug purpose only, assert the object is valid
//
// Arguments: None
//
// Returns: Nothing
//
// History: Created Header 2/12/98
//
//+----------------------------------------------------------------------------
void CConnStatistics::AssertValid() const { MYDBGASSERT(m_hKey == NULL || m_hStatDevice == NULL); MYDBGASSERT(m_fAdapter2 == TRUE || m_fAdapter2 == FALSE); ASSERT_VALID(&m_TrafficRing); } #endif
|