//+------------------------------------------------------------------------- // // TaskMan - NT TaskManager // Copyright (C) Microsoft // // File: netpage.cpp // // History: Oct-18-2000 Olaf Miller Created // //-------------------------------------------------------------------------- #include "precomp.h" #define MIN_GRAPH_HEIGHT 120 #define SCROLLBAR_WIDTH 17 #define INVALID_VALUE 0xFFFFFFFF #define PERCENT_SHIFT 10000000 #define PERCENT_DECIMAL_POINT 7 // Determines how the graphs are drawen (zoomed level) // static int g_NetScrollamount = 0; extern TCHAR g_szG[]; extern TCHAR g_szM[]; extern TCHAR g_szK[]; extern TCHAR g_szZero[]; extern TCHAR g_szPackets[]; extern TCHAR g_szBitsPerSec[]; extern TCHAR g_szScaleFont[]; extern TCHAR g_szPercent[]; extern TCHAR g_szNonOperational[]; extern TCHAR g_szUnreachable[]; extern TCHAR g_szDisconnected[]; extern TCHAR g_szConnecting[]; extern TCHAR g_szConnected[]; extern TCHAR g_szOperational[]; extern TCHAR g_szUnknownStatus[]; extern TCHAR g_szGroupThousSep[]; extern TCHAR g_szDecimal[]; extern ULONG g_ulGroupSep; static HGDIOBJ hOld2; // Window Proc the Network Tab // INT_PTR CALLBACK NetPageProc( HWND hwnd, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); // Colors of the pens // static const COLORREF aNetColors[] = { RGB(255, 000, 0), RGB(255, 255, 0), RGB(000, 255, 0), }; // Default values for the Networking Page's statistics columns // struct { SHORT Format; SHORT Width; } NetColumnDefaults[NUM_NETCOLUMN] = { { LVCFMT_LEFT, 96 }, // COL_ADAPTERNAME { LVCFMT_LEFT, 96 }, // COL_ADAPTERDESC { LVCFMT_RIGHT, 96 }, // COL_NETWORKUTIL { LVCFMT_RIGHT, 60 }, // COL_LINKSPEED { LVCFMT_RIGHT, 96 }, // COL_STATE { LVCFMT_RIGHT, 70 }, // COL_BYTESSENTTHRU { LVCFMT_RIGHT, 70 }, // COL_BYTESRECTHRU { LVCFMT_RIGHT, 75 }, // COL_BYTESTOTALTHRU { LVCFMT_RIGHT, 70 }, // COL_BYTESSENT { LVCFMT_RIGHT, 70 }, // COL_BYTESREC { LVCFMT_RIGHT, 50 }, // COL_BYTESTOTAL { LVCFMT_RIGHT, 70 }, // COL_BYTESSENTPERINTER { LVCFMT_RIGHT, 70 }, // COL_BYTESRECPERINTER { LVCFMT_RIGHT, 70 }, // COL_BYTESTOTALPERINTER { LVCFMT_RIGHT, 70 }, // COL_UNICASTSSSENT { LVCFMT_RIGHT, 70 }, // COL_UNICASTSREC { LVCFMT_RIGHT, 50 }, // COL_UNICASTSTOTAL { LVCFMT_RIGHT, 70 }, // COL_UNICASTSSENTPERINTER { LVCFMT_RIGHT, 70 }, // COL_UNICASTSRECPERINTER { LVCFMT_RIGHT, 70 }, // COL_UNICASTSTOTALPERINTER { LVCFMT_RIGHT, 70 }, // COL_NONUNICASTSSSENT { LVCFMT_RIGHT, 70 }, // COL_NONUNICASTSREC { LVCFMT_RIGHT, 50 }, // COL_NONUNICASTSTOTAL { LVCFMT_RIGHT, 70 }, // COL_NONUNICASTSSENTPERINTER { LVCFMT_RIGHT, 70 }, // COL_NONUNICASTSRECPERINTER { LVCFMT_RIGHT, 70 }, // COL_NONUNICASTSTOTALPERINTER }; // List of the name of the columns. These strings appear in the column header // static const _aIDNetColNames[NUM_NETCOLUMN] = { IDS_COL_ADAPTERNAME, IDS_COL_ADAPTERDESC, IDS_COL_NETWORKUTIL, IDS_COL_LINKSPEED, IDS_COL_STATE, IDS_COL_BYTESSENTTHRU, IDS_COL_BYTESRECTHRU, IDS_COL_BYTESTOTALTHRU, IDS_COL_BYTESSENT, IDS_COL_BYTESREC, IDS_COL_BYTESTOTAL, IDS_COL_BYTESSENTPERINTER, IDS_COL_BYTESRECPERINTER, IDS_COL_BYTESTOTALPERINTER, IDS_COL_UNICASTSSSENT, IDS_COL_UNICASTSREC, IDS_COL_UNICASTSTOTAL, IDS_COL_UNICASTSSENTPERINTER, IDS_COL_UNICASTSRECPERINTER, IDS_COL_UNICASTSTOTALPERINTER, IDS_COL_NONUNICASTSSSENT, IDS_COL_NONUNICASTSREC, IDS_COL_NONUNICASTSTOTAL, IDS_COL_NONUNICASTSSENTPERINTER, IDS_COL_NONUNICASTSRECPERINTER, IDS_COL_NONUNICASTSTOTALPERINTER, }; // List of window checkbox IDs. These check boxes appear in the Select a column diaglog box. // const int g_aNetDlgColIDs[] = { IDC_ADAPTERNAME, IDC_ADAPTERDESC, IDC_NETWORKUTIL, IDC_LINKSPEED, IDC_STATE, IDC_BYTESSENTTHRU, IDC_BYTESRECTHRU, IDC_BYTESTOTALTHRU, IDC_BYTESSENT, IDC_BYTESREC, IDC_BYTESTOTAL, IDC_BYTESSENTPERINTER, IDC_BYTESRECPERINTER, IDC_BYTESTOTALPERINTER, IDC_UNICASTSSSENT, IDC_UNICASTSREC, IDC_UNICASTSTOTAL, IDC_UNICASTSSENTPERINTER, IDC_UNICASTSRECPERINTER, IDC_UNICASTSTOTALPERINTER, IDC_NONUNICASTSSSENT, IDC_NONUNICASTSREC, IDC_NONUNICASTSTOTAL, IDC_NONUNICASTSSENTPERINTER, IDC_NONUNICASTSRECPERINTER, IDC_NONUNICASTSTOTALPERINTER, }; /*++ Routine Description: Changes the size of an array. Allocates new memory for the array and copies the data in the old array to the new array. If the new array is larger then the old array the extra memory is zeroed out. Arguments: ppSrc -- Pointer to the source array. dwNewSize -- The size (in bytes) of the new array. Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ HRESULT ChangeArraySize(LPVOID *ppSrc, DWORD dwNewSize) { if( ppSrc == NULL ) { return E_INVALIDARG; } if( NULL == *ppSrc ) { // The array is empty. Allocate new space for it, // *ppSrc = (LPVOID) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, dwNewSize); if( NULL == *ppSrc ) { return E_OUTOFMEMORY; } } else { LPVOID pTmp; // The array contains data. Allocate new memory for it and copy over the data in the array. // If the new array is larger then the old array the extra memory is zeroed out. // pTmp = (LPVOID)HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,*ppSrc,dwNewSize); if( pTmp ) { *ppSrc = pTmp; } else { return E_OUTOFMEMORY; } } return S_OK; } /*++ Routine Description: Initialize all of the CAdapter class members. The CAdapter class use the Ip Helper api's to collect information about the Network adapters on the local system. Arguments: NONE Return Value: VOID Revision History: 1-6-2000 Created by omiller --*/ CAdapter::CAdapter() { m_dwAdapterCount = 0; m_ppaiAdapterStats = NULL; m_pifTable = NULL; m_bToggle = 0; m_dwLastReportedNumberOfAdapters = 0; } /*++ Routine Description: Adds any new adapters reported by the IPHLPAPI and removes any old adapters that are no longer reported by the IPHLPAPI Arguments: void Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ HRESULT CAdapter::RefreshAdapterTable() { DWORD dwSize; DWORD dwRequiredSize; DWORD dwRetVal; DWORD dwOldAdapter; DWORD dwNewAdapter; HRESULT hr; // Get the list of active adapters // do { // Determine the size of the adapter array // dwSize = (DWORD)(m_pifTable == NULL ? 0 : HeapSize(GetProcessHeap(),0,m_pifTable)); // Collect all of the adapter information for all adapters (WAN and LAN) // dwRetVal = GetInterfaceInfo(m_pifTable,&dwSize); switch(dwRetVal) { case ERROR_INSUFFICIENT_BUFFER: // The array is to small. Need to make it bigger and try again. // hr = ChangeArraySize((LPVOID *)&m_pifTable,dwSize); if( FAILED(hr) ) { // Unable to expand the size of the array // return hr; } break; case NO_ERROR: // Everything is happy // break; case ERROR_MORE_DATA: // Error code 234 // For some reason this error message means that there are no adapters. BUG? // m_dwAdapterCount = 0; return S_FALSE; default: // Something went wrong fail. // return E_FAIL; } } while(dwRetVal); // Remove any adapters that are no longer active. i.e. are not in the the refreshed adatrer list m_pifTable // all adapters up to m_dwAdapterCount already have memory allocated so no need to check m_ppaiAdapterStats[dwOldAdapter] == NULL // dwOldAdapter=0; while( dwOldAdapter < m_dwAdapterCount ) //for(dwOldAdapter=0; dwOldAdapter < m_dwAdapterCount; dwOldAdapter++) { BOOLEAN bFound = FALSE; for(dwNewAdapter=0; dwNewAdapter < (DWORD)m_pifTable->NumAdapters; dwNewAdapter++) { if( m_ppaiAdapterStats[dwOldAdapter]->ifRowStartStats.dwIndex == m_pifTable->Adapter[dwNewAdapter].Index ) { // The adapter is still active. Mark this adapter as invalid to indicate that this adapter is already in our list. // m_pifTable->Adapter[dwNewAdapter].Index = INVALID_VALUE; bFound = TRUE; break; } } if( !bFound ) { // Shift the array up to overwrite the unwanted adapter. So we don't have to free memory // put the adapter we are throwing away at the end of our list. We can reuse its memory. // PADAPTER_INFOEX ptr = m_ppaiAdapterStats[dwOldAdapter]; for(DWORD i=dwOldAdapter; iNumAdapters < MAX_ADAPTERS ? m_pifTable->NumAdapters : MAX_ADAPTERS)); if( dwSize < dwRequiredSize ) { // Resize our adapter list, incase new adapters have been added. Do not add more than 32 adapters. // hr = ChangeArraySize((LPVOID *)&m_ppaiAdapterStats,dwRequiredSize); //sizeof(PADAPTER_INFOEX) * (m_pifTable->NumAdapters < MAX_ADAPTERS ? m_pifTable->NumAdapters : MAX_ADAPTERS)); if( FAILED(hr) ) { // Unable to resize, out of memory? // return hr; } } // Count the number of adapters in our list. // m_dwAdapterCount = 0; // Append the new adapters to the end of our adapter list // for(dwNewAdapter=0; dwNewAdapter < (DWORD)m_pifTable->NumAdapters && m_dwAdapterCount < MAX_ADAPTERS; dwNewAdapter++) { if( m_pifTable->Adapter[dwNewAdapter].Index != INVALID_VALUE ) { // Initialize the adapter information and add it to our list // hr = InitializeAdapter(&m_ppaiAdapterStats[m_dwAdapterCount],&m_pifTable->Adapter[dwNewAdapter]); if( SUCCEEDED(hr) ) { m_dwAdapterCount++; } } else { // The adapter is already in our list // m_dwAdapterCount++; } } return S_OK; } /*++ Routine Description: Update the connection names of the network adapter. This function is only called when the user selects the refresh menu item. The connection rarely change, so it would be a waste of time to update the connection name every time the get the adapter statistics. Arguments: void Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ void CAdapter::RefreshConnectionNames() { // Update the connection name for each adapter in out list // for(DWORD dwAdapter=0; dwAdapter < m_dwAdapterCount; dwAdapter++) { (void)GetConnectionName(m_ppaiAdapterStats[dwAdapter]->wszGuid,m_ppaiAdapterStats[dwAdapter]->wszConnectionName); } } /*++ Routine Description: Get the connection name of an adapter. Arguments: pwszAdapterGuid -- The guid of the adapter pwszConnectionName -- returns the connection name of the adapter Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ HRESULT CAdapter::GetConnectionName(LPWSTR pwszAdapterGuid, LPWSTR pwszConnectionName) { GUID IfGuid; WCHAR wszConnName[MAXLEN_IFDESCR]; DWORD Size; DWORD dwRetVal; HRESULT hr; Size = sizeof(wszConnName); // Convert the GUID into a string // hr = CLSIDFromString(pwszAdapterGuid,&IfGuid); if( SUCCEEDED(hr) ) { // Use the private IPHLPAPI to get the connection name of the device // dwRetVal = NhGetInterfaceNameFromDeviceGuid(&IfGuid, wszConnName, &Size, FALSE, TRUE); if( NO_ERROR == dwRetVal ) { lstrcpyn(pwszConnectionName, wszConnName, MAXLEN_IFDESCR); return S_OK; } } return E_FAIL; } /*++ Routine Description: Initialize the adapter information. i.e. get the adapter name, guid, adapter description and the initial adapter statistics (bytes sent, bytes recieved etc) Arguments: ppaiAdapterStats -- Adapter to initialize pAdapterDescription -- Information about the adapter (Index and Adapter GUID) Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ HRESULT CAdapter::InitializeAdapter(PPADAPTER_INFOEX ppaiAdapterStats, PIP_ADAPTER_INDEX_MAP pAdapterDescription) { DWORD dwRetVal; HRESULT hr; INT iAdapterNameLength; if( !ppaiAdapterStats || !pAdapterDescription) { return E_INVALIDARG; } if( NULL == *ppaiAdapterStats ) { // This slot was never used before, we need to allocate memory for it // *ppaiAdapterStats = (PADAPTER_INFOEX)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(ADAPTER_INFOEX)); } if( *ppaiAdapterStats ) { // Initialize the adapter by filling in all the adapter values. // (*ppaiAdapterStats)->ifRowStartStats.dwIndex = (DWORD)pAdapterDescription->Index; // Get the initial statistics for this adapter // dwRetVal = GetIfEntry(&(*ppaiAdapterStats)->ifRowStartStats); if( NO_ERROR == dwRetVal ) { if( (*ppaiAdapterStats)->ifRowStartStats.dwType == MIB_IF_TYPE_PPP || (*ppaiAdapterStats)->ifRowStartStats.dwType == MIB_IF_TYPE_SLIP) { // We only need to adjust the link speed of modems since they compress data causing // network utilization to exceed 100%. The link speed of modems also changes during a // connection but the IPHLPAPIs do not report this change. // (*ppaiAdapterStats)->bAdjustLinkSpeed = TRUE; } else { (*ppaiAdapterStats)->bAdjustLinkSpeed = FALSE; } // Initialize the adapter values. The start current and last stats are all the same at the start // memcpy(&(*ppaiAdapterStats)->ifRowStats[0],&(*ppaiAdapterStats)->ifRowStartStats,sizeof(MIB_IFROW)); memcpy(&(*ppaiAdapterStats)->ifRowStats[1],&(*ppaiAdapterStats)->ifRowStartStats,sizeof(MIB_IFROW)); memset(&(*ppaiAdapterStats)->ulHistory[0],INVALID_VALUE, sizeof(ULONG) * HIST_SIZE); memset(&(*ppaiAdapterStats)->ulHistory[1],INVALID_VALUE, sizeof(ULONG) * HIST_SIZE); mbstowcs((*ppaiAdapterStats)->wszDesc,(LPCSTR)(*ppaiAdapterStats)->ifRowStartStats.bDescr,MAXLEN_IFDESCR); // Extract the Device guid of the adapter // hr = E_FAIL; iAdapterNameLength = lstrlen(pAdapterDescription->Name); if( iAdapterNameLength >= GUID_STR_LENGTH ) { // The Guid is the last GUID_STR_LENGTH chars in the name. Get the guid and from the guid get the connection name // lstrcpyn((*ppaiAdapterStats)->wszGuid,&pAdapterDescription->Name[iAdapterNameLength - GUID_STR_LENGTH], GUID_STR_LENGTH + 1); hr = GetConnectionName((*ppaiAdapterStats)->wszGuid,(*ppaiAdapterStats)->wszConnectionName); } if( FAILED(hr) ) { // We were unable to get the connection name, use the adapter description as the connection name // lstrcpyn((*ppaiAdapterStats)->wszConnectionName,(*ppaiAdapterStats)->wszDesc,MAXLEN_IFDESCR); } return S_OK; } } return E_OUTOFMEMORY; } /*++ Routine Description: Adjusts the link speed of an adapter. Modems change link speed during a connection and the also compress data. This often causes taskmgr to report network utilization greater than 100%. To avoid confusing the user modems use the max link speed that taskmgr sees. If the link speed changes the adapters graph is adjusted to reflect the change in link speed. Arguments: pAdapterInfo -- Adapter whos link speed needs to be adjusted. Return Value: void Revision History: 1-6-2000 Created by omiller --*/ void CAdapter::AdjustLinkSpeed(PADAPTER_INFOEX pAdapterInfo) { if( pAdapterInfo && pAdapterInfo->ullTickCountDiff) { ULONGLONG ullBitsPerSecond; ULONGLONG ullBytesMoved; // Compute the total number of bits moved in this interval // ullBytesMoved = (pAdapterInfo->ifRowStats[m_bToggle].dwInOctets + pAdapterInfo->ifRowStats[m_bToggle].dwOutOctets) - (pAdapterInfo->ifRowStats[!m_bToggle].dwInOctets + pAdapterInfo->ifRowStats[!m_bToggle].dwOutOctets); // Compute the real link speed. Modems lie about there link speed based on the number of bytes moved in this interval // ullBitsPerSecond = ullBytesMoved * 8 * 1000/ pAdapterInfo->ullTickCountDiff; // Memorize and use the highest link speed // pAdapterInfo->ifRowStats[m_bToggle].dwSpeed = (DWORD)max(max(ullBitsPerSecond,pAdapterInfo->ullLinkspeed),pAdapterInfo->ifRowStats[m_bToggle].dwSpeed); if( pAdapterInfo->ullLinkspeed == 0 ) { // First time run, no need to adjust the graphs // pAdapterInfo->ullLinkspeed = (DWORD) pAdapterInfo->ifRowStats[m_bToggle].dwSpeed; } else if( pAdapterInfo->ullLinkspeed != pAdapterInfo->ifRowStats[m_bToggle].dwSpeed ) { // Adjust the points on the adapters graphs to reflect the new link speed. // for(DWORD dwPoint=0; dwPointulHistory[BYTES_RECEIVED_UTIL][dwPoint] != INVALID_VALUE ) { pAdapterInfo->ulHistory[BYTES_RECEIVED_UTIL][dwPoint] = (ULONG)(pAdapterInfo->ulHistory[BYTES_RECEIVED_UTIL][dwPoint] * pAdapterInfo->ullLinkspeed / pAdapterInfo->ifRowStats[m_bToggle].dwSpeed); pAdapterInfo->ulHistory[BYTES_SENT_UTIL][dwPoint] = (ULONG)(pAdapterInfo->ulHistory[BYTES_SENT_UTIL][dwPoint] * pAdapterInfo->ullLinkspeed / pAdapterInfo->ifRowStats[m_bToggle].dwSpeed); } } // Remember the new linkspeed // pAdapterInfo->ullLinkspeed = (DWORD) pAdapterInfo->ifRowStats[m_bToggle].dwSpeed; } } } /*++ Routine Description: Every few seconds this function is called to collect data about each of active network adapter (Adapter name, bytes sent, bytes received, unicasts packets sent, unicast packets received etc). The CAdapter class memorizes the first and last set of data that it collected. It does this so the user can determine i.e. the number of bytes sent between now and when taskmgr started and the number of bytes sent between now and the last interval. Furthermore it memorizes the length of the interval (in milliseconds) Arguments: bAdapterListChange -- Indicates if the adapters have been added or removed. Return Value: HRESULT Note: The current and last data is stored in a two dimentional array. When the data is collected the next time (i.e. when this function is called again), the Current data becomes the last data. Inorder to save time Current and last data are stored in a two dimensional array and m_bToggle is used to indicate which dimenstion is current (m_bToggle) and which is the last set of data (!m_bToggle). Revision History: 1-6-2000 Created by omiller --*/ HRESULT CAdapter::Update(BOOLEAN & bAdapterListChange) { DWORD dwRetVal; DWORD dwNumberOfAdaptersReported = 0; HRESULT hr = S_OK; bAdapterListChange = FALSE; if( m_dwAdapterCount < MAX_ADAPTERS ) { // Check if there are any new adapters. If we already have 32 adapters don't bother, our list is full // If an Adapter is removed we will catch that later. // dwRetVal = GetNumberOfInterfaces(&dwNumberOfAdaptersReported); if( NO_ERROR == dwRetVal ) { // Make sure the number of interfaces is still the same. If not we need to update our interface table. // if( m_dwLastReportedNumberOfAdapters != dwNumberOfAdaptersReported ) { // The number of adapters changed, refresh our list // hr = RefreshAdapterTable(); bAdapterListChange = TRUE; m_dwLastReportedNumberOfAdapters = dwNumberOfAdaptersReported; } } else { // Unknow error, abort // hr = E_FAIL; } } if( SUCCEEDED(hr) ) { m_bToggle = !m_bToggle; // Get the statistics for each adapter // for(DWORD dwAdapter=0; dwAdapter < m_dwAdapterCount; dwAdapter++) { dwRetVal = GetIfEntry(&m_ppaiAdapterStats[dwAdapter]->ifRowStats[m_bToggle]); if( NO_ERROR == dwRetVal ) { // Compute the sampling interval for this adapter. If it the first run make sure delta time is 0 // Advance the adapter's throughput history // ULONGLONG ullTickCount = GetTickCount(); m_ppaiAdapterStats[dwAdapter]->ullTickCountDiff = ullTickCount - (m_ppaiAdapterStats[dwAdapter]->ullLastTickCount ? m_ppaiAdapterStats[dwAdapter]->ullLastTickCount : ullTickCount); m_ppaiAdapterStats[dwAdapter]->ullLastTickCount = ullTickCount; m_ppaiAdapterStats[dwAdapter]->ifRowStartStats.dwSpeed = m_ppaiAdapterStats[dwAdapter]->ifRowStats[m_bToggle].dwSpeed; if( m_ppaiAdapterStats[dwAdapter]->bAdjustLinkSpeed ) { AdjustLinkSpeed(m_ppaiAdapterStats[dwAdapter]); } AdvanceAdapterHistory(dwAdapter); } else if( ERROR_INVALID_DATA == dwRetVal ) { // The adapter is no longer active. When the list is refreshed the adapter will be removed. // bAdapterListChange = TRUE; } else { // Something went wrong abort // hr = E_FAIL; break; } } } if( bAdapterListChange ) { // Our adapter list is not uptodate, adapters that were active are no longer active. Remove them from our list // hr = RefreshAdapterTable(); } return hr; } /*++ Routine Description: Adds commas into a number string to make it more readable Arguments: ullValue - Number to simplify pwsz - Buffer to store resulting string cchNumber -- size of the buffer pwsz. Return Value: String containing the simplified number Revision History: 1-6-2000 Created by omiller --*/ WCHAR * CNetPage::CommaNumber(ULONGLONG ullValue, WCHAR *pwsz, int cchNumber) { WCHAR wsz[100]; NUMBERFMT nfmt; nfmt.NumDigits = 0; nfmt.LeadingZero = 0; nfmt.Grouping = UINT(g_ulGroupSep); nfmt.lpDecimalSep = g_szDecimal; nfmt.lpThousandSep = g_szGroupThousSep; nfmt.NegativeOrder = 0; _ui64tow(ullValue,wsz,10); GetNumberFormat(LOCALE_USER_DEFAULT, 0, wsz, &nfmt, pwsz, cchNumber); return pwsz; } /*++ Routine Description: Simplifies a number by converting the number into Giga, Mega or Kilo Arguments: ullValue - Number to simplify psz - Buffer to store resulting string bBytes - Indicates if the number is in bytes Return Value: String containing the simplified number Revision History: 1-6-2000 Created by omiller --*/ WCHAR * CNetPage::SimplifyNumber(ULONGLONG ullValue, WCHAR *psz) { ULONG ulDivValue=1; LPWSTR pwszUnit=L""; // The max value of a ulonglong is 2^64 which is 20 digits so this buffer is big enough to hold the number, commas and M, G, K WCHAR wsz[100]; // ullValue is not number of bytes so div by 10^X // if( ullValue >= 1000000000 ) { ulDivValue = 1000000000; pwszUnit = g_szG; } else if( ullValue >= 1000000 ) { ulDivValue = 1000000; pwszUnit = g_szM; } else if( ullValue >= 1000 ) { ulDivValue = 1000; pwszUnit = g_szK; } lstrcpy(psz,CommaNumber(ullValue/ulDivValue,wsz,100)); lstrcat(psz,L" "); lstrcat(psz,pwszUnit); return psz; } /*++ Routine Description: Converts a floating point value (Stored as an integer shifted by PERCENT_SHIFT) into a string Arguments: ulValue - floating point value to convert psz - Buffer to store resulting string bDisplayDecimal - Indicates if the decimal value should be displayed. Return Value: String containg the Floating point string Revision History: 1-6-2000 Created by omiller --*/ WCHAR * CNetPage::FloatToString(ULONGLONG ullValue, WCHAR *psz, BOOLEAN bDisplayDecimal) { ULONGLONG ulDecimal = 0; ULONGLONG ulNumber = 0; WCHAR wsz[100]; // Get the integer value of the number // ulNumber = ullValue / PERCENT_SHIFT; if( _ui64tow(ulNumber,wsz,10) ) { lstrcpy(psz,wsz); if( ulNumber ) { ullValue = ullValue - ulNumber * PERCENT_SHIFT; } if( !ulNumber || bDisplayDecimal) { ulDecimal = ullValue * 100 / PERCENT_SHIFT; if( ulDecimal && _ui64tow(ulDecimal,wsz,10) ) { lstrcat(psz,g_szDecimal); if( ulDecimal < 10 ) lstrcat(psz,g_szZero); if( wsz[1] == L'0' ) wsz[1] = L'\0'; lstrcat(psz,wsz); } } } return psz; } /*++ Routine Description: Initialize the networking tab. This function always returns 1. The networking tab displays all active connections (LAN and WAN). If no connections are present when taskmgr is started, the Network tab should still be displayed (thus return 1) incase a connection is established after taskmgr has started. The network tab will detect any newly established connections. Arguments: None Return Value: 1 Revision History: 1-6-2000 Created by omiller --*/ BYTE InitNetInfo() { // The network page should always appear even if there are no Network Adapters currently present. // return 1; } /*++ Routine Description: Uninitialize all CAdapter class members. Frees all memory that was allocated by the class. Arguments: NONE Return Value: VOID Revision History: 1-6-2000 Created by omiller --*/ CAdapter::~CAdapter() { if( m_pifTable ) { HeapFree(GetProcessHeap(),0,m_pifTable); } if( m_dwAdapterCount ) { DWORD dwSize; DWORD dwTotalAdapterCount; // Get the total size of the array and compute the number of entries in the array. // m_dwAdapterCount only indicates the adapters are active. The array could have been bigger // at one point. Free the memory for all the entries. // dwSize = (DWORD)(m_ppaiAdapterStats == NULL ? 0 : HeapSize(GetProcessHeap(),0,m_ppaiAdapterStats)); dwTotalAdapterCount = dwSize / sizeof(PADAPTER_INFOEX); for(DWORD dwAdapter=0; dwAdapter < dwTotalAdapterCount; dwAdapter++) { if( m_ppaiAdapterStats[dwAdapter] ) { HeapFree(GetProcessHeap(),0,m_ppaiAdapterStats[dwAdapter]); } } HeapFree(GetProcessHeap(),0,m_ppaiAdapterStats); } } /*++ Routine Description: Returns the number active adapters. The IP helper functions provide information about physical and non-physical adapters (such as the Microsoft TCP loop back adapter). Arguments: bEvenHiddenAdapters -- If true return the total number of adapters. if false return only the number of physical adapters. Return Value: Number of active adapters Revision History: 1-6-2000 Created by omiller --*/ DWORD CAdapter::GetNumberOfAdapters() { return m_dwAdapterCount; } /*++ Routine Description: Resets the initial data for all Network adapters. The CAdapter class collects information for all active network adapters (Bytes sent, bytes received, unicasts packets sent, unicast packets received etc). The class memorizes the first set of data for each adapter. This is done so that the user can determine the number of i.e. bytes sent from when taskmgr was first started. (i.e. the number of bytes seen now minuse the number of bytes seen when taskmgr was started). When the user selects the Reset option, this functions overwrites the initial data, collected when taskmgr started, with the current data. Arguments: NONE Return Value: S_OK Revision History: 1-6-2000 Created by omiller --*/ HRESULT CAdapter::Reset() { for(DWORD dwAdapter=0; dwAdapter < m_dwAdapterCount; dwAdapter++) { // Overwrite the Start and Last stats with the Current stats // memcpy(&m_ppaiAdapterStats[dwAdapter]->ifRowStartStats,&m_ppaiAdapterStats[dwAdapter]->ifRowStats[m_bToggle],sizeof(MIB_IFROW)); memcpy(&m_ppaiAdapterStats[dwAdapter]->ifRowStats[!m_bToggle],&m_ppaiAdapterStats[dwAdapter]->ifRowStats[m_bToggle],sizeof(MIB_IFROW)); } return S_OK; } /*++ Routine Description: Extracts the text fields (i.e. connection name and description) from a specified adapter Arguments: deAdapter -- Adapter who text the caller wants nStatValue -- Specifies which field the caller wants i.e. (Name or description) Return Value: NULL -- if the adapter index is invalid or nStatValue is invalid Revision History: 1-6-2000 Created by omiller --*/ LPWSTR CAdapter::GetAdapterText(DWORD dwAdapter, NETCOLUMNID nStatValue) { if( dwAdapter >= m_dwAdapterCount ) { // Invalid adapter index // return 0; } switch(nStatValue) { case COL_ADAPTERNAME: // Get adapter name // return m_ppaiAdapterStats[dwAdapter]->wszConnectionName; case COL_ADAPTERDESC: // Get adapter description // return m_ppaiAdapterStats[dwAdapter]->wszDesc; case COL_STATE: switch(m_ppaiAdapterStats[dwAdapter]->ifRowStats[m_bToggle].dwOperStatus) { case IF_OPER_STATUS_NON_OPERATIONAL: return g_szNonOperational; case IF_OPER_STATUS_UNREACHABLE: return g_szUnreachable; case IF_OPER_STATUS_DISCONNECTED: return g_szDisconnected; case IF_OPER_STATUS_CONNECTING: return g_szConnecting; case IF_OPER_STATUS_CONNECTED: return g_szConnected; case IF_OPER_STATUS_OPERATIONAL: return g_szOperational; default: return g_szUnknownStatus; } } return NULL; } /*++ Routine Description: Get the Send/Receive Network utilization history for a specified adapter Arguments: deAdapter -- Adapter who text the caller wants nHistoryType -- BYTES_SENT_UTIL for send history BYTES_RECEIVED_UTIL for receive history Return Value: NULL -- if the adapter index is invalid or nStatValue is invalid Revision History: 1-6-2000 Created by omiller --*/ ULONG * CAdapter::GetAdapterHistory(DWORD dwAdapter, ADAPTER_HISTORY nHistoryType) { if( dwAdapter < m_dwAdapterCount ) { // Return the history // return m_ppaiAdapterStats[dwAdapter]->ulHistory[nHistoryType]; } return NULL; } /*++ Routine Description: Updates the Send/Received Network Utilization adapter history Arguments: dwAdapter -- Adapter Index Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ BOOLEAN CAdapter::AdvanceAdapterHistory(DWORD dwAdapter) { ULONG *pulHistory; if( dwAdapter < m_dwAdapterCount ) { // Shift the receive adapter history by one and add the new point // pulHistory = m_ppaiAdapterStats[dwAdapter]->ulHistory[BYTES_RECEIVED_UTIL]; MoveMemory((LPVOID) (pulHistory + 1), (LPVOID) (pulHistory), sizeof(ULONG) * (HIST_SIZE - 1) ); // Get and add the receive network utiliation // pulHistory[0] = (ULONG)GetAdapterStat(dwAdapter,COL_BYTESRECTHRU); // Shift the send adapter history by one and add the new point // pulHistory = m_ppaiAdapterStats[dwAdapter]->ulHistory[BYTES_SENT_UTIL]; MoveMemory((LPVOID) (pulHistory + 1), (LPVOID) (pulHistory), sizeof(ULONG) * (HIST_SIZE - 1) ); // Get and add the send network utilization // pulHistory[0] = (ULONG)GetAdapterStat(dwAdapter, COL_BYTESSENTTHRU); return TRUE; } return FALSE; } /*++ Routine Description: Get the scale. The scale specifies the maximum value that is in the Adapters history. This value determines how the data is graphed. Arguments: dwAdapter -- Adapter Index Return Value: Maximum value in history Revision History: 1-6-2000 Created by omiller --*/ DWORD CAdapter::GetScale(DWORD dwAdapter) { if( dwAdapter < m_dwAdapterCount ) { return m_ppaiAdapterStats[dwAdapter]->dwScale; } return 0; } /*++ Routine Description: Set the scale. The scale specifies the maximum value that is in the Adapters history. This value determines how the data is graphed. Arguments: dwAdapter -- Adapter Index Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CAdapter::SetScale(DWORD dwAdapter, DWORD dwScale) { if( dwAdapter < m_dwAdapterCount ) { m_ppaiAdapterStats[dwAdapter]->dwScale = dwScale; } } /*++ Routine Description: Computes and returns the requested statistiocs value for the specified adapter Arguments: dwAdapter -- Adapter Index nStatValue -- The stat value requested bAccumulative -- If true the current value is returned if fale the current - start value is returned. Return Value: The requested Statistic value Revision History: 1-6-2000 Created by omiller --*/ ULONGLONG CAdapter::GetAdapterStat(DWORD dwAdapter, NETCOLUMNID nStatValue, BOOL bAccumulative) { if( dwAdapter >= m_dwAdapterCount ) { // Invalid adapter index // return 0; } // Create pointers to the arrays so I do not have to write so much // PMIB_IFROW pifrStart = &m_ppaiAdapterStats[dwAdapter]->ifRowStartStats; PMIB_IFROW pifrLast = &m_ppaiAdapterStats[dwAdapter]->ifRowStats[!m_bToggle]; PMIB_IFROW pifrCurrent = &m_ppaiAdapterStats[dwAdapter]->ifRowStats[m_bToggle]; ULONGLONG ullTickCountDiff = m_ppaiAdapterStats[dwAdapter]->ullTickCountDiff; // Contains the maximum number of bytes that could have been transimited in the time intrval // between Last and Current) // ULONGLONG ullMaxBytesTransmittedInInterval; ULONGLONG ull = 0; // Get the stats value, do the calculation and return the value // switch(nStatValue) { case COL_NETWORKUTIL: case COL_BYTESSENTTHRU: case COL_BYTESRECTHRU: case COL_BYTESTOTALTHRU: { ullMaxBytesTransmittedInInterval = (pifrCurrent->dwSpeed * ullTickCountDiff)/(8 * 1000); if( ullMaxBytesTransmittedInInterval == 0 ) { return 0; } switch(nStatValue) { case COL_BYTESTOTALTHRU: case COL_NETWORKUTIL: ull = (pifrCurrent->dwInOctets - pifrLast->dwInOctets) + (pifrCurrent->dwOutOctets - pifrLast->dwOutOctets); break; case COL_BYTESSENTTHRU: ull = (pifrCurrent->dwOutOctets - pifrLast->dwOutOctets); break; case COL_BYTESRECTHRU: ull = (pifrCurrent->dwInOctets - pifrLast->dwInOctets); break; } ull *= 100 * PERCENT_SHIFT; ull /= ullMaxBytesTransmittedInInterval; // Make usre we do not exceed 100%, just confuses the user. Davepl inside joke! // return ull > 100 * PERCENT_SHIFT ? 99 * PERCENT_SHIFT : ull; } case COL_LINKSPEED: return pifrStart->dwSpeed; case COL_BYTESSENT: return (ULONGLONG)(pifrCurrent->dwOutOctets - (bAccumulative ? pifrStart->dwOutOctets : 0)); case COL_BYTESREC: return (ULONGLONG)(pifrCurrent->dwInOctets - (bAccumulative ? pifrStart->dwInOctets : 0)); case COL_BYTESTOTAL: return (ULONGLONG)((pifrCurrent->dwInOctets + pifrCurrent->dwOutOctets) - (bAccumulative ? (pifrStart->dwInOctets + pifrStart->dwOutOctets) : 0)); case COL_BYTESSENTPERINTER: return (ULONGLONG)(pifrCurrent->dwOutOctets - pifrLast->dwOutOctets); case COL_BYTESRECPERINTER: return (ULONGLONG)(pifrCurrent->dwInOctets - pifrLast->dwInOctets); case COL_BYTESTOTALPERINTER: return (ULONGLONG)((pifrCurrent->dwOutOctets + pifrCurrent->dwInOctets) - (pifrLast->dwOutOctets + pifrLast->dwInOctets)); case COL_UNICASTSSSENT: return (ULONGLONG)(pifrCurrent->dwInUcastPkts - (bAccumulative ? pifrStart->dwInUcastPkts : 0)); case COL_UNICASTSREC: return (ULONGLONG)(pifrCurrent->dwOutUcastPkts - (bAccumulative ? pifrStart->dwOutUcastPkts : 0)); case COL_UNICASTSTOTAL: return (ULONGLONG)((pifrCurrent->dwInUcastPkts + pifrCurrent->dwOutUcastPkts) - (bAccumulative ? (pifrStart->dwInUcastPkts + pifrStart->dwOutUcastPkts) : 0)); case COL_UNICASTSSENTPERINTER: return (ULONGLONG)(pifrCurrent->dwOutUcastPkts - pifrLast->dwOutUcastPkts); case COL_UNICASTSRECPERINTER: return (ULONGLONG)(pifrCurrent->dwInUcastPkts - pifrLast->dwInUcastPkts); case COL_UNICASTSTOTALPERINTER: return (ULONGLONG)((pifrCurrent->dwOutUcastPkts + pifrCurrent->dwInUcastPkts) - (pifrLast->dwOutUcastPkts + pifrLast->dwInUcastPkts)); case COL_NONUNICASTSSSENT: return (ULONGLONG)(pifrCurrent->dwInNUcastPkts - (bAccumulative ? pifrStart->dwInNUcastPkts : 0)); case COL_NONUNICASTSREC: return (ULONGLONG)(pifrCurrent->dwOutNUcastPkts - (bAccumulative ? pifrStart->dwOutNUcastPkts : 0)); case COL_NONUNICASTSTOTAL: return (ULONGLONG)((pifrCurrent->dwInNUcastPkts + pifrCurrent->dwOutNUcastPkts) - (bAccumulative ? (pifrStart->dwInNUcastPkts + pifrStart->dwOutNUcastPkts) : 0)); case COL_NONUNICASTSSENTPERINTER: return (ULONGLONG)(pifrCurrent->dwOutNUcastPkts - pifrLast->dwOutNUcastPkts); case COL_NONUNICASTSRECPERINTER: return (ULONGLONG)(pifrCurrent->dwInNUcastPkts - pifrLast->dwInNUcastPkts); case COL_NONUNICASTSTOTALPERINTER: return (ULONGLONG)((pifrCurrent->dwOutNUcastPkts + pifrCurrent->dwInNUcastPkts) - (pifrLast->dwOutNUcastPkts + pifrLast->dwInNUcastPkts)); } return 0; } /*++ Routine Description: Initilize all member variables. The CNetPage class displays the information collected by the CAdpter class Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ CNetPage::CNetPage() { ZeroMemory((LPVOID) m_hPens, sizeof(m_hPens)); m_bReset = TRUE; m_hPage = NULL; // Handle to this page's dlg m_hwndTabs = NULL; // Parent window m_hdcGraph = NULL; // Inmemory dc for cpu hist m_hbmpGraph = NULL; // Inmemory bmp for adapter hist m_bPageActive = FALSE; m_hScaleFont = NULL; m_lScaleWidth = 0; m_lScaleFontHeight = 0; m_pGraph = NULL; m_dwGraphCount = 0; m_dwFirstVisibleAdapter = 0; m_dwGraphsPerPage = 0; } /*++ Routine Description: Clean up Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ CNetPage::~CNetPage() { DestroyGraphs(); ReleasePens(); ReleaseScaleFont(); }; /*++ Routine Description: Reset the start Adapter set Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::Reset() { m_bReset = TRUE; } /*++ Routine Description: Creates the number of required graphs Arguments: dwGraphsRequired -- Graphs required Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ HRESULT CNetPage::CreateGraphs(DWORD dwGraphsRequired) { DWORD dwSize; LRESULT lFont; HRESULT hr = S_OK; // Create moore graphs if required // if( dwGraphsRequired > m_dwGraphCount ) { // Expand the size of the array so it can hold more graphs // dwSize = dwGraphsRequired * sizeof(GRAPH); hr = ChangeArraySize((LPVOID *)&m_pGraph,dwSize); if( SUCCEEDED(hr) ) { // Get the font of the parent window // lFont = SendMessage(m_hPage,WM_GETFONT,NULL,NULL); for(; m_dwGraphCount < dwGraphsRequired; m_dwGraphCount++) { // Create the graph window. The graph is drawn in the window // m_pGraph[m_dwGraphCount].hwndGraph = CreateWindowEx(WS_EX_CLIENTEDGE, L"BUTTON", L"", WS_CHILDWINDOW | BS_OWNERDRAW | WS_DISABLED, 0,0,0,0, m_hPage, (HMENU)ULongToPtr(IDC_NICGRAPH + m_dwGraphCount), NULL,NULL); if( m_pGraph[m_dwGraphCount].hwndGraph ) { if( m_dwGraphCount == 0 ) { HDC hdc = GetDC(m_pGraph[m_dwGraphCount].hwndGraph); if(hdc ) { CreateScaleFont(hdc); ReleaseDC(m_pGraph[m_dwGraphCount].hwndGraph,hdc); } } // Create the frame window. The window draws a pretty border around the graph // m_pGraph[m_dwGraphCount].hwndFrame = CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"DavesFrameClass", L"", 0x7 | WS_CHILDWINDOW, 0,0,0,0, m_hPage, NULL,NULL,NULL); if( m_pGraph[m_dwGraphCount].hwndFrame ) { // Create the graph window. The graph is drawn in the window // SendMessage(m_pGraph[m_dwGraphCount].hwndFrame,WM_SETFONT,lFont,FALSE); } else { // Destroy the graph window and abort // TODO hr = error and break; DestroyWindow(m_pGraph[m_dwGraphCount].hwndGraph); return E_OUTOFMEMORY; } } else { // Unable to create the window, abort // return E_OUTOFMEMORY; } } } } return hr; } /*++ Routine Description: Destroies the History graph windows Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::DestroyGraphs() { // WHILE loop (--m_dwGraphCount) for(DWORD dwGraph=0; dwGraph < m_dwGraphCount; dwGraph++) { DestroyWindow(m_pGraph[dwGraph].hwndGraph); DestroyWindow(m_pGraph[dwGraph].hwndFrame); } if( m_pGraph ) { HeapFree(GetProcessHeap(),0,m_pGraph); m_pGraph = NULL; } m_dwGraphCount = 0; } /*++ Routine Description: Restores the order of the Network tab's list view columns. The order is stored in g_Options. When taskmgr is closed, the order is stored in the registry Arguments: hwndList -- Handle to the list view Return Value: NONE Revision History: 1-6-2000 Borrowed by omiller --*/ void CNetPage::RestoreColumnOrder(HWND hwndList) { INT rgOrder[ARRAYSIZE(g_aNetDlgColIDs)]; INT cOrder = 0; INT iOrder = 0; // Get the order of the columns // for (int i = 0; i < ARRAYSIZE(g_aNetDlgColIDs); i++) { iOrder = g_Options.m_NetColumnPositions[i]; if (-1 == iOrder) break; rgOrder[cOrder++] = iOrder; } if (0 < cOrder) { // Setthe order of the columns // const HWND hwndHeader = ListView_GetHeader(hwndList); Header_SetOrderArray(hwndHeader, Header_GetItemCount(hwndHeader), rgOrder); } } /*++ Routine Description: Remember the order of the Network tab's list view columns. The order is stored in g_Options. When taskmgr is closed, the order is stored in the registry Arguments: hwndList -- Handle to the list view Return Value: NONE Revision History: 1-6-2000 Borrowed by omiller --*/ void CNetPage::RememberColumnOrder(HWND hwndList) { const HWND hwndHeader = ListView_GetHeader(hwndList); int x; x = Header_GetItemCount(hwndHeader); ASSERT(Header_GetItemCount(hwndHeader) <= ARRAYSIZE(g_Options.m_NetColumnPositions)); // Clear the array // FillMemory(&g_Options.m_NetColumnPositions, sizeof(g_Options.m_NetColumnPositions), 0xFF); // Get the order of the columns and store it in the array // Header_GetOrderArray(hwndHeader, Header_GetItemCount(hwndHeader), g_Options.m_NetColumnPositions); } /*++ Routine Description: The Window Proc for the select a column Dialog box. Allows the user to select the collumns. Arguments: hwndDlg -- Handle to the dialog box uMsg -- Window message wParam -- Window message lParam -- WIndows Message Return Value: Something Revision History: 1-6-2000 Borrowed by omiller --*/ INT_PTR CALLBACK NetColSelectDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static CNetPage * pPage = NULL; switch(uMsg) { case WM_INITDIALOG: { // Looks scary, but we're single threaded pPage = (CNetPage *) lParam; // Start with none of the boxes checked for (int i = 0; i < ARRAYSIZE(g_aNetDlgColIDs) /*NUM_NETCOLUMN*/; i++) { CheckDlgButton(hwndDlg, g_aNetDlgColIDs[i], BST_UNCHECKED); } // Then turn on the ones for the columns we have active for (i = 0; i < ARRAYSIZE(g_aNetDlgColIDs)/*NUM_NETCOLUMN + 1*/; i++) { if (g_Options.m_ActiveNetCol[i] == -1) { break; } CheckDlgButton(hwndDlg, g_aNetDlgColIDs[g_Options.m_ActiveNetCol[i]], BST_CHECKED); } return TRUE; } case WM_COMMAND: { // If user clicked OK, add the columns to the array and reset the listview if (LOWORD(wParam) == IDOK) { // First, make sure the column width array is up to date pPage->SaveColumnWidths(); INT iCol = 0; for (int i = 0; i < NUM_NETCOLUMN && g_aNetDlgColIDs[i] >= 0; i++) { if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, g_aNetDlgColIDs[i])) { // It is checked if (g_Options.m_ActiveNetCol[iCol] != (NETCOLUMNID) i) { // If the column wasn't already there, insert its column // width into the column width array ShiftArray(g_Options.m_NetColumnWidths, iCol, SHIFT_UP); ShiftArray(g_Options.m_ActiveNetCol, iCol, SHIFT_UP); g_Options.m_NetColumnWidths[iCol] = NetColumnDefaults[ i ].Width; g_Options.m_ActiveNetCol[iCol] = (NETCOLUMNID) i; } iCol++; } else { // Not checked, column not active. If it used to be active, // remove its column width from the column width array if (g_Options.m_ActiveNetCol[iCol] == (NETCOLUMNID) i) { ShiftArray(g_Options.m_NetColumnWidths, iCol, SHIFT_DOWN); ShiftArray(g_Options.m_ActiveNetCol, iCol, SHIFT_DOWN); } } } // Terminate the column list g_Options.m_ActiveNetCol[iCol] = (NETCOLUMNID) -1; pPage->SetupColumns(); //pPage->TimerEvent(); EndDialog(hwndDlg, IDOK); } else if (LOWORD(wParam) == IDCANCEL) { EndDialog(hwndDlg, IDCANCEL); } } } return FALSE; } /*++ Routine Description: Handle the slect a column dialog box Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::PickColumns() { DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_SELECTNETCOLS), g_hMainWnd, NetColSelectDlgProc, (LPARAM) this); } /*++ Routine Description: Creates the necessary pens Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::CreatePens() { for (int i = 0; i < ARRAYSIZE(aNetColors); i++) { // Create the pens. If a failure occurs, just substitute // the white pen m_hPens[i] = CreatePen(PS_SOLID, 1, aNetColors[i]); if (NULL == m_hPens[i]) { m_hPens[i] = (HPEN) GetStockObject(WHITE_PEN); } } } /*++ Routine Description: Destroys the pens Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::ReleasePens() { for (int i = 0; i < NUM_PENS; i++) { if (m_hPens[i]) { DeleteObject(m_hPens[i]); } } } /*++ Routine Description: Compute the width (in px) of a string Arguments: hdc - DC handle pszwText - String to determine the width of. Return Value: The width of the string Revision History: 1-6-2000 Created by omiller --*/ int GetStrWidth(HDC hdc, WCHAR *pszwText) { int iWidth; int iTotalWidth = 0; if( pszwText ) { // Sum the width of the chars in the string // for(int i=0; pszwText[i]!=L'\0'; i++) { if( GetCharWidth32(hdc,pszwText[i],pszwText[i],&iWidth) ) { iTotalWidth += iWidth; } else { // GetCharWidth32 failed, return -1 as an error code return -1; } } } // Return the total width of the string // return iTotalWidth; } /*++ Routine Description: Creates the font for the scale. The font is created when the first graph is created. The hdc of the raph is used to determine the height of the font and the total with of the scale. Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::CreateScaleFont(HDC hdc) { if( !m_hScaleFont && hdc) { // The font has not yet been created. INT FontSize; NONCLIENTMETRICS ncm = {0}; LOGFONT lf; HFONT hOldFont = NULL; ncm.cbSize = sizeof(ncm); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); lf = ncm.lfMessageFont; lf.lfWeight = FW_THIN; lstrcpy(lf.lfFaceName, g_szScaleFont); // BUG Localize FontSize = 8; lf.lfHeight = 0 - GetDeviceCaps(hdc, LOGPIXELSY) * FontSize / 72; m_hScaleFont = CreateFontIndirect(&lf); hOldFont = (HFONT)SelectObject(hdc,(HGDIOBJ)m_hScaleFont); m_lScaleFontHeight = lf.lfHeight - 2; m_lScaleWidth = GetStrWidth(hdc,L" 22.5 %"); // 12.5 %"); if( hOldFont ) { SelectObject(hdc,(HGDIOBJ)hOldFont); } } } /*++ Routine Description: Releases the fonts Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::ReleaseScaleFont() { if( m_hScaleFont ) { DeleteObject(m_hScaleFont); m_hScaleFont = NULL; } } /*++ Routine Description: Draws the scale for the graph Arguments: hdcGraph - hdc of the graph prcGraph - Graph region dwMaxScaleValue - The highest scale value Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ INT CNetPage::DrawScale(HDC hdcGraph, RECT *prcGraph, DWORD dwMaxScaleValue) { HPEN hOldPen = NULL; HFONT hOldFont = NULL; INT Leftside = prcGraph->left; WCHAR sz[100]; RECT rc; if( g_Options.m_bShowScale ) { if( m_hScaleFont ) { // Set the scale font hOldFont = (HFONT) SelectObject(hdcGraph,(HGDIOBJ)m_hScaleFont); } // Set the text attributes // SetBkMode(hdcGraph,TRANSPARENT); SetTextColor(hdcGraph,RGB(255, 255, 0)); // Make room for the scale // Leftside += m_lScaleWidth; rc.left = prcGraph->left; rc.right = Leftside - 3; // Draw the upper scale value // rc.top = prcGraph->top; rc.bottom = rc.top - m_lScaleFontHeight; FloatToString(dwMaxScaleValue*PERCENT_SHIFT,sz,TRUE); lstrcat(sz,L" "); lstrcat(sz,g_szPercent); DrawText(hdcGraph,sz,lstrlen(sz),&rc,DT_RIGHT); // Draw the middle scale value. multi by PERCENT_SHIFT because FloatToString takes a number shifted by PERCENT_SHIFT // rc.top = prcGraph->top + (prcGraph->bottom - prcGraph->top)/2 + m_lScaleFontHeight/2; rc.bottom = rc.top - m_lScaleFontHeight; FloatToString((dwMaxScaleValue*PERCENT_SHIFT)/2,sz,TRUE); lstrcat(sz,L" "); lstrcat(sz,g_szPercent); DrawText(hdcGraph,sz,lstrlen(sz),&rc,DT_RIGHT); // Draw the botton scale value // rc.top = prcGraph->bottom + m_lScaleFontHeight; rc.bottom = rc.top - m_lScaleFontHeight; // BUG : loc lstrcpy(sz,g_szZero); lstrcat(sz,L" "); lstrcat(sz,g_szPercent); DrawText(hdcGraph,sz,lstrlen(sz),&rc,DT_RIGHT); if( hOldFont ) { SelectObject(hdcGraph,hOldFont); } // Draw the scale line. Divides the scale from the graph // hOldPen = (HPEN)SelectObject(hdcGraph, m_hPens[1]); MoveToEx(hdcGraph, Leftside, prcGraph->top, (LPPOINT) NULL); LineTo(hdcGraph, Leftside, prcGraph->bottom); if( hOldPen) { SelectObject(hdcGraph,hOldPen); } } return Leftside; } /*++ Routine Description: Draw the graph paper. The size of the squares depends of the zoom level. Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ ULONG CNetPage::DrawAdapterGraphPaper(HDC hdcGraph, RECT * prcGraph, int Width, DWORD dwZoom) { #define GRAPHPAPERSIZE 12 HPEN hPen; int Leftside = prcGraph->left; ULONG ulSquareSize = GRAPHPAPERSIZE + (20 * (100 - 100/dwZoom)) / 100; //28 int nLineCount = 0; Leftside = DrawScale(hdcGraph,prcGraph,100/dwZoom); // Bug: keep hPen hPen = CreatePen(PS_SOLID, 1, RGB(0, 128, 64)); HGDIOBJ hOld = SelectObject(hdcGraph, hPen); // Draw the vertical lines // for (int i = (ulSquareSize) + 1; i < prcGraph->bottom - prcGraph->top; i+= ulSquareSize) { MoveToEx(hdcGraph, Leftside, prcGraph->bottom - i, (LPPOINT) NULL); LineTo(hdcGraph, prcGraph->right, prcGraph->bottom - i); nLineCount++; } // Draw the horizontal lines // for (i = prcGraph->right - g_NetScrollamount; i > Leftside; i -= GRAPHPAPERSIZE) { MoveToEx(hdcGraph, i, prcGraph->top, (LPPOINT) NULL); LineTo(hdcGraph, i, prcGraph->bottom); } if (hOld) { SelectObject(hdcGraph, hOld); } if( hPen ) { DeleteObject(hPen); } return Leftside - prcGraph->left - 3; } /*++ Routine Description: Add the column headers to the Network tab's List view Arguments: NONE Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ HRESULT CNetPage::SetupColumns() { // Delete all the items in the list view // ListView_DeleteAllItems(m_hListView); // Remove all existing columns LV_COLUMN lvcolumn; while(ListView_DeleteColumn(m_hListView, 0)) { NULL; } // Add all of the new columns INT iColumn = 0; while (g_Options.m_ActiveNetCol[iColumn] >= 0) { INT idColumn = g_Options.m_ActiveNetCol[iColumn]; TCHAR szTitle[MAX_PATH]; LoadString(g_hInstance, _aIDNetColNames[idColumn], szTitle, ARRAYSIZE(szTitle)); lvcolumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_TEXT | LVCF_WIDTH; lvcolumn.fmt = NetColumnDefaults[ idColumn ].Format | (idColumn > 1 ? LVCFMT_RIGHT : 0); // If no width preference has been recorded for this column, use the // default if (-1 == g_Options.m_NetColumnWidths[iColumn]) { lvcolumn.cx = NetColumnDefaults[ idColumn ].Width; } else { lvcolumn.cx = g_Options.m_NetColumnWidths[iColumn]; } lvcolumn.pszText = szTitle; lvcolumn.iSubItem = iColumn; if (-1 == ListView_InsertColumn(m_hListView, iColumn, &lvcolumn)) { return E_FAIL; } iColumn++; } ListView_SetExtendedListViewStyleEx(m_hListView,LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT , LVS_EX_HEADERDRAGDROP | LVS_EX_FULLROWSELECT); return S_OK; } /*++ Routine Description: Save the width of the columns Arguments: NONE Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::SaveColumnWidths() { UINT i = 0; LV_COLUMN col = { 0 }; while (g_Options.m_ActiveNetCol[i] != (NETCOLUMNID) -1) { col.mask = LVCF_WIDTH; if (ListView_GetColumn(m_hListView, i, &col) ) { g_Options.m_NetColumnWidths[i] = (SHORT)col.cx; } else { ASSERT(0 && "Couldn't get the column width"); } i++; } } /*++ Routine Description: Initialize the Network tab Arguments: NONE Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ HRESULT CNetPage::Initialize(HWND hwndParent) { CreatePens(); // Our pseudo-parent is the tab contrl, and is what we base our // sizing on. However, in order to keep tab order right among // the controls, we actually create ourselves with the main // window as the parent m_hwndTabs = hwndParent; // // Create the dialog which represents the body of this page // m_hPage = CreateDialogParam( g_hInstance, // handle to application instance MAKEINTRESOURCE(IDD_NETPAGE), // identifies dialog box template name g_hMainWnd, // handle to owner window NetPageProc, // pointer to dialog box procedure (LPARAM) this ); // User data (our this pointer) if (NULL == m_hPage) { return GetLastHRESULT(); } // no fail if GetDlgItem m_hNoAdapterText = GetDlgItem(m_hPage, IDC_NOADAPTERS); if( !m_hNoAdapterText ) { return E_FAIL; } m_hScrollBar = GetDlgItem(m_hPage, IDC_GRAPHSCROLLVERT); if( !m_hScrollBar ) { return E_FAIL; } m_hListView = GetDlgItem(m_hPage, IDC_NICTOTALS); if( !m_hListView ) { return E_FAIL; } // Add the columns to the list view // SetupColumns(); // Order the columns for the user // RestoreColumnOrder(m_hListView); return S_OK; } /*++ Routine Description: Creates the memory bitmaps for the graphs Arguments: NONE Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ HRESULT CNetPage::CreateMemoryBitmaps(int x, int y) { // // Create the inmemory bitmaps and DCs that we will use // HDC hdcPage = GetDC(m_hPage); m_hdcGraph = CreateCompatibleDC(hdcPage); if (NULL == m_hdcGraph) { ReleaseDC(m_hPage, hdcPage); return GetLastHRESULT(); } m_rcGraph.left = 0; m_rcGraph.top = 0; m_rcGraph.right = x; m_rcGraph.bottom = y; m_hbmpGraph = CreateCompatibleBitmap(hdcPage, x, y); ReleaseDC(m_hPage, hdcPage); if (NULL == m_hbmpGraph) { HRESULT hr = GetLastHRESULT(); DeleteDC(m_hdcGraph); m_hdcGraph = NULL; return hr; } // Select the bitmap into the DC hOld2 = SelectObject(m_hdcGraph, m_hbmpGraph); return S_OK; } /*++ Routine Description: Destroy the bitmaps for the graphs Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::FreeMemoryBitmaps() { if (m_hdcGraph) { if (hOld2) SelectObject(m_hdcGraph, m_hbmpGraph); DeleteDC(m_hdcGraph); } if (m_hbmpGraph) { DeleteObject(m_hbmpGraph); } } /*++ Routine Description: This function is called when the Network tab is activated. Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ HRESULT CNetPage::Activate() { // Adjust the size and position of our dialog relative // to the tab control which "owns" us RECT rcParent; GetClientRect(m_hwndTabs, &rcParent); MapWindowPoints(m_hwndTabs, g_hMainWnd, (LPPOINT) &rcParent, 2); TabCtrl_AdjustRect(m_hwndTabs, FALSE, &rcParent); // The user has switched to the tab. The Networking tab is now active // The tab will stay active for the whole life time of taskmgr // m_bPageActive = TRUE; SetWindowPos(m_hPage, HWND_TOP, rcParent.left, rcParent.top, rcParent.right - rcParent.left, rcParent.bottom - rcParent.top, 0); // Make this page visible ShowWindow(m_hPage, SW_SHOW); // Newly created dialogs seem to steal the focus, so give it back to the // tab control (which must have had it if we got here in the first place) SetFocus(m_hwndTabs); TimerEvent(); // Change the menu bar to be the menu for this page HMENU hMenuOld = GetMenu(g_hMainWnd); HMENU hMenuNew = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MAINMENU_NET)); AdjustMenuBar(hMenuNew); CheckMenuItem(hMenuNew,IDM_BYTESSENT,g_Options.m_bAutoSize ? MF_CHECKED:MF_UNCHECKED); CheckMenuItem(hMenuNew,IDM_BYTESSENT,g_Options.m_bGraphBytesSent ? MF_CHECKED:MF_UNCHECKED); CheckMenuItem(hMenuNew,IDM_BYTESRECEIVED,g_Options.m_bGraphBytesReceived ? MF_CHECKED:MF_UNCHECKED); CheckMenuItem(hMenuNew,IDM_BYTESTOTAL,g_Options.m_bGraphBytesTotal ? MF_CHECKED:MF_UNCHECKED); g_hMenu = hMenuNew; if (g_Options.m_fNoTitle == FALSE) { SetMenu(g_hMainWnd, hMenuNew); } if (hMenuOld) { DestroyMenu(hMenuOld); } SizeNetPage(); return S_OK; } /*++ Routine Description: This function is called when the Network tab is deactivated. Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::Deactivate() { SaveColumnWidths(); if (m_hPage) { ShowWindow(m_hPage, SW_HIDE); } if (m_hbmpGraph) { DeleteObject(m_hbmpGraph); m_hbmpGraph = NULL; } if (m_hdcGraph) { DeleteDC(m_hdcGraph); m_hdcGraph = NULL; } } HRESULT CNetPage::Destroy() { // // When we are being destroyed, kill off our dialog // if (m_hPage) { DestroyWindow(m_hPage); m_hPage = NULL; } if (m_hbmpGraph) { DeleteObject(m_hbmpGraph); m_hbmpGraph = NULL; } if (m_hdcGraph) { DeleteDC(m_hdcGraph); m_hdcGraph = NULL; } return S_OK; } /*++ Routine Description: Get the title of the networking tab i.e. "Networking" Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::GetTitle(LPTSTR pszText, size_t bufsize) { LoadString(g_hInstance, IDS_NETPAGETITLE, pszText, static_cast(bufsize)); } /*++ Routine Description: Size the History graph Arguments: hdwp -- Defered Window Handle pGraph -- History graph to size pRect -- The corrdinates of the graph pDimRect -- The dimentions of the actual history graph Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::SizeGraph(HDWP hdwp, GRAPH *pGraph, RECT *pRect, RECT *pDimRect) { RECT rc; DWORD dwGraphWidth = pRect->right - g_DefSpacing * 2; DWORD dwGraphHeight = pRect->bottom - g_TopSpacing - g_DefSpacing; // Size the frame // rc.left = pRect->left; rc.top = pRect->top; rc.right = pRect->left + pRect->right; rc.bottom = pRect->top + pRect->bottom; DeferWindowPos(hdwp, pGraph->hwndFrame, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); // Size the History graph // rc.left = rc.left + g_DefSpacing; rc.top = rc.top + g_TopSpacing; rc.right = rc.left + dwGraphWidth; rc.bottom = rc.top + dwGraphHeight; DeferWindowPos(hdwp, pGraph->hwndGraph, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); if( pDimRect ) { // Return the size of the history graph // memcpy(pDimRect,&rc,sizeof(RECT)); } } /*++ Routine Description: Hide the history graph Arguments: hdwp -- Defered Window Handle pGraph -- History graph to hide Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::HideGraph(HDWP hdwp, GRAPH *pGraph) { // Hide the frame // DeferWindowPos(hdwp, pGraph->hwndFrame, NULL, 0,0,0,0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW); // Hide the graph // DeferWindowPos(hdwp, pGraph->hwndGraph, NULL, 0,0,0,0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW); } /*++ Routine Description: Compute the number of graphs we can squeeze on a page Arguments: dwHeight -- Height of the graphing area dwAdapterCount -- Total number of adapters Return Value: Number of adapters that can be squeeze on to the tab Revision History: 1-6-2000 Created by omiller --*/ DWORD CNetPage::GraphsPerPage(DWORD dwHeight, DWORD dwAdapterCount) { DWORD dwGraphsPerPage = 0; if( dwAdapterCount ) { DWORD dwGraphHeight; // Compute the average height of an adapter if all adapters were put on the page // If they all fit then squueze all of them on the page, if the height is smaller then the min // height compute the number of adapters we can squeeze on the page. // dwGraphHeight = dwHeight / dwAdapterCount; dwGraphHeight = dwGraphHeight < MIN_GRAPH_HEIGHT ? MIN_GRAPH_HEIGHT : dwGraphHeight; dwGraphsPerPage = dwHeight > dwGraphHeight ? dwHeight / dwGraphHeight : 1; } return dwGraphsPerPage; } /*++ Routine Description: Get the first adapter that the user sees graphed. Arguments: void Return Value: The first adapter the user sees graphed Revision History: 1-6-2000 Created by omiller --*/ DWORD CNetPage::GetFirstVisibleAdapter() { DWORD dwAdapter = m_dwFirstVisibleAdapter; DWORD dwAdapterCount = m_Adapter.GetNumberOfAdapters(); if( dwAdapter + m_dwGraphsPerPage > dwAdapterCount ) { dwAdapter = dwAdapterCount - m_dwGraphsPerPage; } if( dwAdapter >= dwAdapterCount ) { dwAdapter = 0; } return dwAdapter; } /*++ Routine Description: Assigns a name to each graph. Arguments: void Return Value: void Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::LabelGraphs() { DWORD dwAdapter; dwAdapter = GetFirstVisibleAdapter(); for(DWORD dwGraph=0; dwGraph < m_dwGraphsPerPage; dwGraph++) { SetWindowText(m_pGraph[dwGraph].hwndFrame,m_Adapter.GetAdapterText(dwAdapter + dwGraph,COL_ADAPTERNAME)); } UpdateGraphs(); } /*++ Routine Description: Size the history graphs. Arguments: void Return Value: void Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::SizeNetPage() { HRESULT hr; HDWP hdwp; RECT rcParent; RECT rcGraph = {0}; RECT rcGraphDim = {0}; DWORD dwAdapterCount; DWORD dwGraphHistoryHeight = 0; BOOLEAN bNeedScrollBar = FALSE; m_dwGraphsPerPage = 0; if (g_Options.m_fNoTitle) { // Just display the graphs, not the list view or the tabs // GetClientRect(g_hMainWnd, &rcParent); dwGraphHistoryHeight = rcParent.bottom - rcParent.top - g_DefSpacing; } else { // Display the graphs, list view and tabs // GetClientRect(m_hwndTabs, &rcParent); MapWindowPoints(m_hwndTabs, m_hPage, (LPPOINT) &rcParent, 2); TabCtrl_AdjustRect(m_hwndTabs, FALSE, &rcParent); dwGraphHistoryHeight = (rcParent.bottom - rcParent.top - g_DefSpacing) * 3 / 4; } // Determine the number of adapters so we can compute the number of graphs to display perpage. // dwAdapterCount = m_Adapter.GetNumberOfAdapters(); if( dwAdapterCount ) { // Compute the number of graphs we can squeeze onto the page. One graph always fits onto the tab. // m_dwGraphsPerPage = GraphsPerPage(dwGraphHistoryHeight,dwAdapterCount); hr = CreateGraphs(m_dwGraphsPerPage); if( FAILED(hr) ) { // Unable to create the graphs, abort // return; } // Determine if we need to display the scroll bar // bNeedScrollBar = (dwAdapterCount > m_dwGraphsPerPage); // Determine the rect for the first graph // rcGraph.left = rcParent.left + g_DefSpacing; rcGraph.right = (rcParent.right - rcParent.left) - g_DefSpacing*2 - (bNeedScrollBar ? SCROLLBAR_WIDTH + g_DefSpacing : 0); rcGraph.top = rcParent.top + g_DefSpacing; rcGraph.bottom = dwGraphHistoryHeight / m_dwGraphsPerPage; } hdwp = BeginDeferWindowPos(10); if( hdwp ) { // Position the scroll bar window // DeferWindowPos(hdwp, m_hScrollBar, NULL, rcParent.right - g_DefSpacing - SCROLLBAR_WIDTH, rcParent.top + g_DefSpacing, SCROLLBAR_WIDTH, rcGraph.bottom * m_dwGraphsPerPage, (bNeedScrollBar ? SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW : SWP_HIDEWINDOW)); // Position the induvidual graphs. We might have created more graphs then we needed, hide the extra graphs // for(DWORD dwGraph=0; dwGraph < m_dwGraphCount; dwGraph++ ) { if( dwGraph < m_dwGraphsPerPage ) { SizeGraph(hdwp,&m_pGraph[dwGraph], &rcGraph, &rcGraphDim); rcGraph.top += rcGraph.bottom; } else { // Do not display these graphs // HideGraph(hdwp,&m_pGraph[dwGraph]); } } // Postion the list view that displays the stats DeferWindowPos(hdwp, m_hListView, NULL, rcGraph.left, rcGraph.top + g_DefSpacing, rcParent.right - rcParent.left - rcGraph.left - g_DefSpacing, rcParent.bottom - rcGraph.top - g_DefSpacing, dwAdapterCount ? SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW : SWP_HIDEWINDOW); // Position the "No Active Adapters found" text // DeferWindowPos(hdwp, m_hNoAdapterText, NULL, rcParent.left , rcParent.top + (rcParent.bottom - rcParent.top) / 2 - 40, rcParent.right - rcParent.left, rcParent.bottom - rcParent.top, dwAdapterCount ? SWP_HIDEWINDOW : SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); EndDeferWindowPos(hdwp); FreeMemoryBitmaps(); // Free any old ones CreateMemoryBitmaps(rcGraphDim.right - rcGraphDim.left, rcGraphDim.bottom - rcGraphDim.top - 4); LabelGraphs(); if( bNeedScrollBar ) { SCROLLINFO si; // Set up the scroll bar // si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_PAGE | SIF_RANGE; si.nPage = 1; si.nMin = 0; si.nMax = dwAdapterCount - m_dwGraphsPerPage; SetScrollInfo(m_hScrollBar,SB_CTL,&si,TRUE); } } } /*++ Routine Description: Update all of the Networking graphs. i.e. redraw the graphs Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::UpdateGraphs() { for (DWORD dwGraph = 0; dwGraph < m_dwGraphsPerPage; dwGraph++) { InvalidateRect(m_pGraph[dwGraph].hwndGraph,NULL,FALSE); UpdateWindow(m_pGraph[dwGraph].hwndGraph); } } /*++ Routine Description: Draw a Networking graph Arguments: prc -- the coordinates of the graph hPen -- The color of the graph line dwZoom -- the zoom level of the graph pHistory -- The history to plot pHistory2 -- The other history to draw in cobonation with the first history i.e. pHistory + pHistory2 Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ DWORD CNetPage::DrawGraph(LPRECT prc, HPEN hPen, DWORD dwZoom, ULONG *pHistory, ULONG *pHistory2) { HGDIOBJ hOldA; ULONGLONG nValue; DWORD nMax=0; int Width = prc->right - prc->left; int Scale = (Width - 1) / HIST_SIZE; if (0 == Scale) { Scale = 2; } hOldA = SelectObject(m_hdcGraph, hPen ); // Compute the height of the graph // int GraphHeight = m_rcGraph.bottom - m_rcGraph.top; // Get the first value to plot // if( pHistory2 ) { if( pHistory[0] == INVALID_VALUE || pHistory2[0] == INVALID_VALUE ) { return nMax; } nValue = (DWORD) (pHistory[0]+pHistory2[0]); } else { if( pHistory[0] == INVALID_VALUE ) { return nMax; } nValue = (DWORD) (pHistory[0]); } // Memorize the max value that is plotted (effects the zoom level) // nMax = (DWORD)(nValue/PERCENT_SHIFT > nMax ? nValue/PERCENT_SHIFT : nMax); nValue = (nValue * GraphHeight * dwZoom/ 100)/PERCENT_SHIFT; nValue = nValue == 0 ? 1 : nValue; MoveToEx(m_hdcGraph, m_rcGraph.right, m_rcGraph.bottom - (ULONG)nValue, (LPPOINT) NULL); // Plot the points // for (INT nPoint = 1; nPoint < HIST_SIZE && nPoint * Scale < Width; nPoint++) { if( pHistory2 ) { if( pHistory[nPoint] == INVALID_VALUE || pHistory2[nPoint] == INVALID_VALUE ) { return nMax; } // Get both points // nValue = (DWORD) (pHistory[nPoint]+pHistory2[nPoint]); } else { if( pHistory[nPoint] == INVALID_VALUE ) { return nMax; } // Just get the first point // nValue = (DWORD) (pHistory[nPoint]); } //nValue /= PERCENT_SHIFT; // Memorize the max value that is plotted (effects the zoom level) // nMax = (DWORD)(nValue/PERCENT_SHIFT > nMax ? nValue/PERCENT_SHIFT : nMax); nValue = (nValue * GraphHeight * dwZoom / 100) / PERCENT_SHIFT; nValue = nValue == 0 ? 1 : nValue; LineTo(m_hdcGraph, m_rcGraph.right - (Scale * nPoint), m_rcGraph.bottom - (ULONG)nValue); } if (hOldA) { SelectObject(m_hdcGraph, hOldA); } // Return the maximum value plotted // return nMax; } /*++ Routine Description: Draw a Networking graph Arguments: lpdi -- the coordinates of the graph iPane -- The id of the graph to draw Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::DrawAdapterGraph(LPDRAWITEMSTRUCT lpdi, UINT iPane) { DWORD dwZoom = 1; DWORD dwScale = 100; DWORD dwAdapter; if( iPane > m_dwGraphCount ) { return; } // Get the adapter index. // dwAdapter = iPane + GetFirstVisibleAdapter(); if( dwAdapter >= m_Adapter.GetNumberOfAdapters() ) { // Invalid adapter, abort. // return; } // Get the scale for the adapter // dwScale = m_Adapter.GetScale(dwAdapter); // Determine the zoom level // if( g_Options.m_bAutoSize ) { if( dwScale < 1 ) { dwZoom = 100; } else if( dwScale < 5) { dwZoom = 20; } else if( dwScale < 25) { dwZoom = 4; } else if( dwScale < 50) { dwZoom = 2; } else { dwZoom = 1; } } if (NULL == m_hdcGraph) { return; } // Draw a black background into the graph // FillRect(m_hdcGraph, &m_rcGraph, (HBRUSH) GetStockObject(BLACK_BRUSH)); int Width = lpdi->rcItem.right - lpdi->rcItem.left; int Scale = (Width - 1) / HIST_SIZE; if (0 == Scale) { Scale = 2; } // Draw the graph paper. The zoom effects the horzontal lines // ULONG ulWidth = DrawAdapterGraphPaper(m_hdcGraph, &m_rcGraph, Width, dwZoom); DWORD nValue; dwScale = 0; lpdi->rcItem.left += ulWidth; if( g_Options.m_bGraphBytesSent ) { // Draw the bytes sent graph. Check the max value plotted // nValue = DrawGraph(&lpdi->rcItem, m_hPens[0], dwZoom, m_Adapter.GetAdapterHistory(dwAdapter,BYTES_SENT_UTIL)); dwScale = nValue > dwScale ? nValue : dwScale; } if( g_Options.m_bGraphBytesReceived ) { // Draw the bytes received graph. Check the max value plotted // nValue = DrawGraph(&lpdi->rcItem,m_hPens[1], dwZoom, m_Adapter.GetAdapterHistory(dwAdapter,BYTES_RECEIVED_UTIL)); dwScale = nValue > dwScale ? nValue : dwScale; } if( g_Options.m_bGraphBytesTotal) { // Draw the bytes total graph. Check the max value plotted // nValue = DrawGraph(&lpdi->rcItem,m_hPens[2],dwZoom, m_Adapter.GetAdapterHistory(dwAdapter,BYTES_SENT_UTIL),m_Adapter.GetAdapterHistory(dwAdapter,BYTES_RECEIVED_UTIL)); dwScale = nValue > dwScale ? nValue : dwScale; } lpdi->rcItem.left -= ulWidth; // Save the max value plotted // m_Adapter.SetScale(dwAdapter,dwScale); // Shift and display the graph // INT xDiff = 0; //(m_rcGraph.right - m_rcGraph.left) - (lpdi->rcItem.right - lpdi->rcItem.left); BitBlt( lpdi->hDC, lpdi->rcItem.left, lpdi->rcItem.top, lpdi->rcItem.right - lpdi->rcItem.left, lpdi->rcItem.bottom - lpdi->rcItem.top, m_hdcGraph, xDiff, 0, SRCCOPY); } /*++ Routine Description: Updates the Network tab's listview Arguments: NONE Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ HRESULT CNetPage::UpdatePage() { HRESULT hr = S_OK; LVITEM lvitem; INT iColumn = 0; DWORD dwItemCount; DWORD dwItem=0; ULONGLONG ull; DWORD dwAdapterCount = m_Adapter.GetNumberOfAdapters(); dwItemCount = ListView_GetItemCount(m_hListView); // Add or update the list view items // for(DWORD dwAdapter = 0; dwAdapter < dwAdapterCount; dwAdapter++) { // Only show the requested stats // iColumn = 0; while (g_Options.m_ActiveNetCol[iColumn] >= 0) { // This buffer needs to hold a 20 digit number with commas and G, M K and sometime bs so it is big enough WCHAR szw[100]; lvitem.mask = LVIF_TEXT; lvitem.iSubItem = iColumn; lvitem.iItem = dwItem; lvitem.pszText = L""; lvitem.lParam = (LPARAM)NULL; //&m_Adapter.m_pAdapterInfo[dwAdapter]; //dwAdapter; //NULL; //(LPARAM)pna; // Get the value // switch(g_Options.m_ActiveNetCol[iColumn]) { case COL_ADAPTERNAME: case COL_ADAPTERDESC: case COL_STATE: lvitem.pszText = m_Adapter.GetAdapterText(dwAdapter,g_Options.m_ActiveNetCol[iColumn]); break; case COL_NETWORKUTIL: case COL_BYTESSENTTHRU: case COL_BYTESRECTHRU: case COL_BYTESTOTALTHRU: // This buffer needs to hold a 20 digit number with commas and G, M K and sometime bs so it is big enough ull = m_Adapter.GetAdapterStat(dwAdapter,g_Options.m_ActiveNetCol[iColumn],!g_Options.m_bNetShowAll); FloatToString(ull,szw,FALSE); lstrcat(szw,L" "); lstrcat(szw,g_szPercent); lvitem.pszText = szw; break; case COL_LINKSPEED: ull = m_Adapter.GetAdapterStat(dwAdapter,g_Options.m_ActiveNetCol[iColumn],!g_Options.m_bNetShowAll); SimplifyNumber(ull,szw); lstrcat(szw,g_szBitsPerSec); lvitem.pszText = szw; break; case COL_BYTESSENT: case COL_BYTESREC: case COL_BYTESTOTAL: case COL_BYTESSENTPERINTER: case COL_BYTESRECPERINTER: case COL_BYTESTOTALPERINTER: ull = m_Adapter.GetAdapterStat(dwAdapter,g_Options.m_ActiveNetCol[iColumn],!g_Options.m_bNetShowAll); CommaNumber(ull,szw,100); lvitem.pszText = szw; break; case COL_UNICASTSSSENT: case COL_UNICASTSREC: case COL_UNICASTSTOTAL: case COL_UNICASTSSENTPERINTER: case COL_UNICASTSRECPERINTER: case COL_UNICASTSTOTALPERINTER: case COL_NONUNICASTSSSENT: case COL_NONUNICASTSREC: case COL_NONUNICASTSTOTAL: case COL_NONUNICASTSSENTPERINTER: case COL_NONUNICASTSRECPERINTER: case COL_NONUNICASTSTOTALPERINTER: ull = m_Adapter.GetAdapterStat(dwAdapter,g_Options.m_ActiveNetCol[iColumn],!g_Options.m_bNetShowAll); CommaNumber(ull,szw,100); lvitem.pszText = szw; break; } if( dwItem >= dwItemCount) { // The adapter is not in the listview, add it // lvitem.mask |= LVIF_PARAM; if( -1 == ListView_InsertItem(m_hListView, &lvitem) ) { return E_FAIL; } dwItemCount = ListView_GetItemCount(m_hListView); } else { // The adapter is already in the list view, update the value // ListView_SetItem(m_hListView, &lvitem); } iColumn++; } dwItem++; } return hr; } /*++ Routine Description: Collect the Adapter information and update the tab Arguments: fUpdateHistory -- not used Return Value: HRESULT Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::CalcNetTime(BOOL fUpdateHistory) { BOOLEAN bAdapterListChange = FALSE; HRESULT hr; // Update our adapter list with the new stats // hr = m_Adapter.Update(bAdapterListChange); if( SUCCEEDED(hr) ) { // Collect the adapter information // if( m_bReset ) { // Reset the Adapter start values // m_Adapter.Reset(); m_bReset = FALSE; } if( bAdapterListChange ) { // Some adapters changed, update the graphs (create and delete graphs) // Refresh(); } // Update the listview // UpdatePage(); } } void CNetPage::Refresh() { m_Adapter.RefreshConnectionNames(); SizeNetPage(); ListView_DeleteAllItems(m_hListView); } /*++ Routine Description: Handle the timer event Arguments: NONE Return Value: NONE Revision History: 1-6-2000 Created by omiller --*/ void CNetPage::TimerEvent() { // If the Network tab is not selected and the user does not want to waste cpu usage for the networking adapter history // do not do any of the Networking calculations. // if( m_bPageActive || g_Options.m_bTabAlwaysActive) { // This will make the graph scrolll // g_NetScrollamount+=2; g_NetScrollamount %= GRAPHPAPERSIZE; // Collect the Adapter information // CalcNetTime(TRUE); // Check if window minimized // if (FALSE == IsIconic(g_hMainWnd)) { UpdateGraphs(); } } } DWORD CNetPage::GetNumberOfGraphs() { return m_dwGraphCount; } void CNetPage::ScrollGraphs(WPARAM wParam, LPARAM lParam) { SCROLLINFO si; si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_ALL; if( GetScrollInfo(m_hScrollBar,SB_CTL,&si) ) { switch(LOWORD(wParam)) { case SB_BOTTOM: si.nPos = si.nMax; break; case SB_TOP: si.nPos = si.nMin; break; case SB_LINEDOWN: si.nPos++; break; case SB_LINEUP: si.nPos--; break; case SB_PAGEUP: si.nPos -= m_dwGraphsPerPage; break; case SB_PAGEDOWN: si.nPos += m_dwGraphsPerPage; break; case SB_THUMBTRACK: case SB_THUMBPOSITION: si.nPos = HIWORD(wParam); break; } if( si.nPos < si.nMin ) { si.nPos = si.nMin; } else if( si.nPos > si.nMax ) { si.nPos = si.nMax; } m_dwFirstVisibleAdapter = si.nPos; SetScrollPos(m_hScrollBar,SB_CTL,si.nPos,TRUE); LabelGraphs(); } } /*++ Routine Description: Window Proc for the Networking tab Arguments: hwnd -- handle to dialog box uMsg -- message wParam -- first message parameter lParam -- second message parameter Return Value: NO IDEA Revision History: 1-6-2000 Created by omiller --*/ INT_PTR CALLBACK NetPageProc( HWND hwnd, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { CNetPage * thispage = (CNetPage *) GetWindowLongPtr(hwnd, GWLP_USERDATA); // See if the parent wants this message // if (TRUE == CheckParentDeferrals(uMsg, wParam, lParam)) { return TRUE; } switch(uMsg) { // Size our kids // case WM_SHOWWINDOW: case WM_SIZE: thispage->SizeNetPage(); return TRUE; case WM_INITDIALOG: { SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam); DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE); dwStyle |= WS_CLIPCHILDREN; SetWindowLong(hwnd, GWL_STYLE, dwStyle); return TRUE; } // We need to fake client mouse clicks in this child to appear as nonclient // (caption) clicks in the parent so that the user can drag the entire app // when the title bar is hidden by dragging the client area of this child case WM_LBUTTONUP: case WM_LBUTTONDOWN: { if (g_Options.m_fNoTitle) { SendMessage(g_hMainWnd, uMsg == WM_LBUTTONUP ? WM_NCLBUTTONUP : WM_NCLBUTTONDOWN, HTCAPTION, lParam); } break; } case WM_COMMAND : { if( LOWORD(wParam) == IDM_NETRESET) { thispage->Reset(); } break; } case WM_NCLBUTTONDBLCLK: case WM_LBUTTONDBLCLK: { return 0; } // Draw one of our owner draw controls // case WM_DRAWITEM: { if (wParam >= IDC_NICGRAPH && wParam <= (WPARAM)(IDC_NICGRAPH + thispage->GetNumberOfGraphs()) ) { thispage->DrawAdapterGraph( (LPDRAWITEMSTRUCT) lParam, (UINT)wParam - IDC_NICGRAPH); return TRUE; } } case WM_DESTROY: thispage->RememberColumnOrder(GetDlgItem(hwnd, IDC_NICTOTALS)); break; case WM_VSCROLL: thispage->ScrollGraphs(wParam,lParam); return 0; default: return FALSE; } return FALSE; }