BUG: NetStatisticsGet2 Not Implemented
ID: Q178885
|
The information in this article applies to:
-
Microsoft Win32 Application Programming Interface (API), used with:
-
Microsoft Windows NT 4.0
-
Microsoft Windows 2000
SYMPTOMS
A Visual C++ compiler and linker fails to build an application using the
NetStatisticsGet2 function. The compiler reports "function not defined." If
the function prototype is explicitly declared in the application, the
linker reports an "unresolved external" for this function.
CAUSE
The NetStatisticsGet2 function is not included in the lmstats.h Win32 SDK
header file, and this function is not defined in the NETAPI32 library.
RESOLUTION
This function provides various statistics for the Windows NT Workstation or
Server components. There is no alternative to get Windows NT Workstation
information, but some statistics for server information can be found within
the performance registry of Windows NT. An application can get the counter
data for the server object.
STATUS
Microsoft has confirmed this to be a bug in the Microsoft products listed
at the beginning of this article. We are researching this bug and will post
new information here in the Microsoft Knowledge Base as it becomes
available.
MORE INFORMATION
You can include the following function definition in an application for
Windows NT to gather server statistics for a Windows NT Server or Windows
NT Workstation. You can use this function to determine how busy Windows NT
is performing as a network server.
The function defined here is named GetServerStats. There are some other
supporting subroutines that need to be included with any code to implement
the GetServerStats function.
NOTE: NetStatisticsGet() is defined and available to get similar information via the STAT_WORKSTATION_0 and STAT_SERVER_0 structures. Please see the Platform SDK documentation on NetStatisticsGet() for more information.
Sample Code
================== netperf.h ================================
#define TYPE_MASK 0x00000300
#define TYPE_NOT_SUPPORTED -1
#define TYPE_DWORD 0
#define TYPE_LARGE 1
#define MAX_INDEX 3000
typedef struct
{
DWORDLONG dwlPerf_Time;
DWORDLONG dwlPerf_Frequency;
DWORDLONG dwlBytes_Total;
DWORDLONG dwlBytes_Received;
DWORDLONG dwlBytes_Transmitted;
DWORD dwSessions_Timed_Out;
DWORD dwSessions_Errored_Out;
DWORD dwSessions_Logged_Off;
DWORD dwSessions_Forced_Off;
DWORD dwErrors_Logon;
DWORD dwErrors_Access_Permissions;
DWORD dwErrors_Granted_Access;
DWORD dwErrors_System;
DWORD dwBlocking_Requests_Rejected;
DWORD dwWork_Item_Shortages;
DWORD dwFiles_Opened_Total;
DWORD dwFiles_Open;
DWORD dwServer_Sessions;
DWORD dwContext_Blocks_Queued;
DWORD dwLogon_Total;
}SERVER_STATS, *PSERVER_STATS;
typedef struct
{
TCHAR *szName;
int nType;
union
{
DWORD dwCounter;
DWORDLONG dwlCounter;
};
}COUNTER;
// Function to return server statistics.
LONG GetServerStats(TCHAR *szMachine, PSERVER_STATS pServerStats);
LONG GetCounterNames(TCHAR *szMachine, LPVOID *lplpCounterNames);
void GetCounterIndices(DWORD Index[], LPVOID lpCounterNames,
COUNTER Counter[], DWORD dwNumCounters);
LONG GetObjectData(TCHAR *szMachine, int nIndex, LPVOID *lplpObj);
void InitServerStats(PSERVER_STATS pServerStats, COUNTER Counter[]);
LONG GetCounters(LPVOID lpObj, COUNTER Counter[],
DWORD dwNumCounters, DWORD Index[]);
================== netperf.c ================================
#include "netperf.h"
LONG GetServerStats(TCHAR *szMachine, PSERVER_STATS pServerStats)
{
LPVOID lpServerObj, lpCounterNames;
DWORD dwNumCounters;
LONG lErr;
DWORD Index[MAX_INDEX + 1];
COUNTER Counter[] = {{_T("Server"), -1, 0},
{_T("Bytes Total/sec"), -1, 0},
{_T("Bytes Received/sec"), -1, 0},
{_T("Bytes Transmitted/sec"), -1, 0},
{_T("Sessions Timed Out"), -1, 0},
{_T("Sessions Errored Out"), -1, 0},
{_T("Sessions Logged Off"), -1, 0},
{_T("Sessions Forced Off"), -1, 0},
{_T("Errors Logon"), -1, 0},
{_T("Errors Access Permissions"), -1, 0},
{_T("Errors Granted Access"), -1, 0},
{_T("Errors System"), -1, 0},
{_T("Blocking Requests Rejected"), -1, 0},
{_T("Work Item Shortages"), -1, 0},
{_T("Files Opened Total"), -1, 0},
{_T("Files Open"), -1, 0},
{_T("Server Sessions"), -1, 0},
{_T("Context Blocks Queued/sec"), -1, 0},
{_T("Logon Total"), -1, 0}};
// Get the list of counter names and indices.
lErr = GetCounterNames(szMachine, &lpCounterNames);
if(lErr != ERROR_SUCCESS)
{
return lErr;
}
dwNumCounters = sizeof(Counter) / sizeof(COUNTER);
// Retrieve and save the indices for counters of interest.
GetCounterIndices(Index, lpCounterNames, Counter, dwNumCounters);
// Get the performance data for the "Server" object.
lErr = GetObjectData(szMachine, Index[0], &lpServerObj);
if(lErr != ERROR_SUCCESS)
{
GlobalFreePtr(lpCounterNames);
return lErr;
}
// Retrieve selected counter data from the "Server" object.
lErr = GetCounters(lpServerObj, Counter, dwNumCounters, Index);
if(lErr != ERROR_SUCCESS)
{
GlobalFreePtr(lpServerObj);
GlobalFreePtr(lpCounterNames);
return lErr;
}
// Initialize the SERVER_STATS data structure with our results.
InitServerStats(pServerStats, Counter);
// Save PerfTime and PerfFreq as well.
pServerStats->dwlPerf_Time =
((PPERF_DATA_BLOCK)lpServerObj)->PerfTime.HighPart << 32 |
((PPERF_DATA_BLOCK)lpServerObj)->PerfTime.LowPart;
pServerStats->dwlPerf_Frequency =
((PPERF_DATA_BLOCK)lpServerObj)->PerfFreq.HighPart << 32 |
((PPERF_DATA_BLOCK)lpServerObj)->PerfFreq.LowPart;
GlobalFreePtr(lpServerObj);
GlobalFreePtr(lpCounterNames);
return lErr;
}
LONG GetCounterNames(TCHAR *szMachine, LPVOID *lplpCounterNames)
{
DWORD dwType, dwCount = 10000, dwCountRet;
LONG lErr;
HKEY hKey1, hKey2;
if(szMachine && *szMachine)
{
lErr = RegConnectRegistry(szMachine, HKEY_LOCAL_MACHINE, &hKey1);
if(lErr != ERROR_SUCCESS)
{
return lErr; // Unable to connect
}
}
else
{
hKey1 = HKEY_LOCAL_MACHINE;
}
lErr = RegOpenKeyEx(hKey1,
(LPCTSTR)_T(
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"
),
0, KEY_READ, &hKey2);
RegCloseKey(hKey1);
if(lErr != ERROR_SUCCESS)
{
return lErr; // Not running NT
}
*lplpCounterNames = NULL;
// Keep trying until the buffer is large enough.
do
{
if(*lplpCounterNames) GlobalFreePtr(*lplpCounterNames);
*lplpCounterNames = (LPVOID *)GlobalAllocPtr(GHND, dwCount);
dwCountRet = dwCount;
// Get the counters data.
lErr = RegQueryValueEx(hKey2, (LPCTSTR)_T("Counters"), NULL,
&dwType, (LPBYTE)*lplpCounterNames, &dwCountRet);
dwCount += 1000;
}while(lErr != ERROR_SUCCESS);
RegCloseKey(hKey2);
return ERROR_SUCCESS;
}
void GetCounterIndices(DWORD Index[], LPVOID lpCounterNames,
COUNTER Counter[], DWORD dwNumCounters)
{
DWORD i;
TCHAR *szCur = (TCHAR *)lpCounterNames;
// While not at end of list.
while(*szCur)
{
for(i = 0; i < dwNumCounters; i++)
{
if(!lstrcmp(szCur, Counter[i].szName))
{
TCHAR *szIndex;
// We have a match, so back up to index.
szIndex = szCur;
szIndex -= 2;
while(*szIndex) szIndex--;
szIndex++;
if(i == 0)
{ // Store "Server" index as Index[0].
Index[i] = _ttoi((const TCHAR *)szIndex);
}
else
{ // Otherwise, store index into Counter array.
Index[_ttoi((const TCHAR *)szIndex)] = i;
}
break;
}
}
// Next string.
szCur = _tcschr(szCur, 0);
szCur++;
}
}
LONG GetObjectData(TCHAR *szMachine, int nIndex, LPVOID *lplpObj)
{
HKEY hKey;
DWORD dwType, dwCount = 1000, dwCountRet;
LONG lErr;
TCHAR szIndex[17];
if(szMachine && *szMachine)
{
// Connect to remote machine.
lErr = RegConnectRegistry(szMachine, HKEY_PERFORMANCE_DATA,
&hKey);
if(lErr != ERROR_SUCCESS)
{
return lErr; // Unable to connect.
}
}
else
{
hKey = HKEY_PERFORMANCE_DATA;
}
*lplpObj = NULL;
_itot(nIndex, szIndex, 10);
// Keep trying until the buffer is large enough.
do
{
if(*lplpObj) GlobalFreePtr(*lplpObj);
*lplpObj = (LPVOID)GlobalAllocPtr(GHND, dwCount);
dwCountRet = dwCount;
// Get the server object data.
lErr = RegQueryValueEx(hKey, szIndex, NULL, &dwType,
(LPBYTE)*lplpObj, &dwCountRet);
dwCount += 1000;
}while(lErr != ERROR_SUCCESS);
RegCloseKey(HKEY_PERFORMANCE_DATA);
return ERROR_SUCCESS;
}
void InitServerStats(PSERVER_STATS pServerStats, COUNTER Counter[])
{
pServerStats->dwlBytes_Total = Counter[1].dwlCounter;
pServerStats->dwlBytes_Received = Counter[2].dwlCounter;
pServerStats->dwlBytes_Transmitted = Counter[3].dwlCounter;
pServerStats->dwSessions_Timed_Out = Counter[4].dwCounter;
pServerStats->dwSessions_Errored_Out = Counter[5].dwCounter;
pServerStats->dwSessions_Logged_Off = Counter[6].dwCounter;
pServerStats->dwSessions_Forced_Off = Counter[7].dwCounter;
pServerStats->dwErrors_Logon = Counter[8].dwCounter;
pServerStats->dwErrors_Access_Permissions = Counter[9].dwCounter;
pServerStats->dwErrors_Granted_Access = Counter[10].dwCounter;
pServerStats->dwErrors_System = Counter[11].dwCounter;
pServerStats->dwBlocking_Requests_Rejected = Counter[12].dwCounter;
pServerStats->dwWork_Item_Shortages = Counter[13].dwCounter;
pServerStats->dwFiles_Opened_Total = Counter[14].dwCounter;
pServerStats->dwFiles_Open = Counter[15].dwCounter;
pServerStats->dwServer_Sessions = Counter[16].dwCounter;
pServerStats->dwContext_Blocks_Queued = Counter[17].dwCounter;
pServerStats->dwLogon_Total = Counter[18].dwCounter;
}
LONG GetCounters(LPVOID lpObj, COUNTER Counter[],
DWORD dwNumCounters, DWORD Index[])
{
PPERF_DATA_BLOCK pDataBlock;
PPERF_OBJECT_TYPE pObject;
PPERF_COUNTER_DEFINITION pCounterDef;
DWORD i;
pDataBlock = (PPERF_DATA_BLOCK)lpObj;
if(!pDataBlock->LittleEndian)
{
return ERROR_NOT_SUPPORTED; // Big-endian format not handled.
}
pObject = (PPERF_OBJECT_TYPE)((BYTE *)pDataBlock +
pDataBlock->HeaderLength);
// Multiple objects may be returned.
while(pObject->ObjectNameTitleIndex != Index[0])
{
pObject = (PPERF_OBJECT_TYPE)((BYTE *)pObject +
pObject->TotalByteLength);
}
// Find the first counter definition.
pCounterDef = (PPERF_COUNTER_DEFINITION)((BYTE *)pObject +
pObject->HeaderLength);
for(i = 0; i < pObject->NumCounters; i++)
{
DWORD dwIndex;
// Ignore any data that you do not explicitly seek.
if(Index[pCounterDef->CounterNameTitleIndex] > 0)
{
switch(pCounterDef->CounterType & TYPE_MASK)
{
case PERF_SIZE_DWORD:
dwIndex = pCounterDef->CounterNameTitleIndex;
Counter[Index[dwIndex]].nType = TYPE_DWORD;
// Save the offsets first, and update with data later.
dwIndex = pCounterDef->CounterNameTitleIndex;
Counter[Index[dwIndex]].dwCounter =
pCounterDef->CounterOffset;
break;
case PERF_SIZE_LARGE:
dwIndex = pCounterDef->CounterNameTitleIndex;
Counter[Index[dwIndex]].nType = TYPE_LARGE;
// Save the offsets first, and update with data later.
dwIndex = pCounterDef->CounterNameTitleIndex;
Counter[Index[dwIndex]].dwCounter =
pCounterDef->CounterOffset;
break;
default:
dwIndex = pCounterDef->CounterNameTitleIndex;
Counter[Index[dwIndex]].nType = TYPE_NOT_SUPPORTED;
break;
}
}
// Next counter.
pCounterDef = (PPERF_COUNTER_DEFINITION)((BYTE *)pCounterDef +
pCounterDef->ByteLength);
}
// No instances, so pCounterDef is now pointing
// at the PERF_COUNTER_BLOCK.
// Update data.
for(i = 1; i < dwNumCounters; i++)
{
if(Counter[i].dwlCounter != (DWORDLONG)-1)
{
switch(Counter[i].nType)
{
case TYPE_DWORD:
Counter[i].dwCounter = *(DWORD *)((BYTE *)pCounterDef +
Counter[i].dwCounter);
break;
case TYPE_LARGE:
Counter[i].dwlCounter =
*(DWORDLONG *)((BYTE *)pCounterDef +
Counter[i].dwCounter);
break;
}
}
}
return ERROR_SUCCESS;
}
Additional query words:
Keywords : kbnetwork kbAPI kbKernBase kbNTOS400 kbNTOS400bug kbWinOS2000 kbWinOS2000bug kbSDKPlatform kbNetAPI kbDSupport kbCodeSam kbGrpNet
Version : winnt:4.0
Platform : winnt
Issue type : kbbug