/**********************************************************************
 * Copyright (c) 2005, 2007 Scapa Technologies Limited and others
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: 
 * Scapa Technologies Limited - Initial API and implementation
 **********************************************************************/

/* bugzilla 186503 - only include windows.h for WIN32 builds */
#if defined(WIN32) && !defined(_WIN64)
#include <windows.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h> 
#include <string.h>
#include "hashtable.h"
#include "perfcontrol.h"
#include "perfconstants.h"
#include "system.h"
#include "perfcalc.h"

#ifdef WIN32
    #ifdef _DEBUG
		#define _CRTDBG_MAP_ALLOC // include Microsoft memory leak detection procedures
		#define _INC_MALLOC	     // exclude standard memory alloc procedures

        #include <crtdbg.h>
    #endif
#endif


#define USE_REG_CONNECT TRUE

#define TOTALBYTES    131072
#define BYTEINCREMENT 8192

#define PERF_OBJ_TIME_TIMER 543229184
#define PERF_LARGE_RAW_BASE 1073939715
#define PERF_PRECISION_TIMESTAMP 1073939712
#define PERF_PRECISION_100NS_TIMER 542573824
#define PERF_PRECISION_OBJECT_TIMER 543622400
#define PERF_PRECISION_SYSTEM_TIMER 541525248
#define PERF_COUNTER_100NS_QUEUELEN_TYPE 5571840

LPSTR *lpNamesArray = NULL;
LPSTR lpNameStrings;
LPSTR *lpHelpArray = NULL;
LPSTR lpHelpStrings;
HashTable* instancecount;

static char* PREFIX = "Perfmon Datacollection:";
char* agent_type = "PerfmonAgent";

static HashTable* idhasht;

static HashTable* hasht_variables;
static HashTable* hasht_filters;
static HashTable* hasht_tree;

static int results_len = -1;
static char** results = NULL;
static int value_index = 0;

static BOOLEAN is_connected = 0;
static BOOLEAN disconnect = FALSE;
static char* connected_host = NULL;

int LOG_TO_FILE = FALSE;
static FILE* f = NULL;
static char filename[] = "SysPerfDll.log";

//////////////////////
// Bug 165319
static int isVista = 0;
unsigned long lpNamesArraySize = 0;
//////////////////////

void logToFile(const char*, ...);

void logToFile(const char* temp, ...)
{
	if (LOG_TO_FILE)
	{
		va_list ap;
		if (f == NULL)
			f = fopen(filename,  "w");
		va_start (ap, temp);
		vfprintf (f, temp, ap);
//		printf(temp,ap);
		va_end (ap);
		fflush(f);
	}
}

//////////////////////////////////////////////////////////////
// Bug 165319
// Check whether or not this is running on VISTA

int isVistaOrLater()
{
	int bIsWindowsVistaOrLater = 0;
	OSVERSIONINFO OSVer;
	OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	
	if(GetVersionEx(&OSVer))
	{
		bIsWindowsVistaOrLater = (OSVer.dwMajorVersion >=6);
	}
	return bIsWindowsVistaOrLater;
}
//////////////////////////////////////////////////////////////

int DC_CollectionStartup(HashTable* xhasht_variables, HashTable* xhasht_filters, HashTable* xhasht_tree)
{
	isVista = isVistaOrLater();

	hasht_variables = xhasht_variables;
	hasht_filters = xhasht_filters;
	hasht_tree = xhasht_tree;

	createValueTables();
	idhasht = tableCreate();

	return 0;
}

BOOLEAN isConnectedWin32(char* host)
{	
	DWORD dwResult, cchBuff = sizeof(1024); 
 
	// Call the WNetGetConnection function.
	//
	dwResult = WNetGetConnection(NULL, (LPSTR) host, &cchBuff); 
 
	logToFile("RESULT = %d\n", dwResult);
	switch (dwResult) 
	{ 
		//
		// Print the connection name or process errors.
		//
		case NO_ERROR: 			
			return TRUE;
        break; 

	    case ERROR_NOT_CONNECTED: 
		    return FALSE;
		break;

		default:
			return FALSE; 
		break;
	}
}

void disconnectWin32(char* host)
{	
	DWORD dwResult; 
	
	logToFile("Attempting to disconnect...\n");
 
	dwResult = WNetCancelConnection2(host, 
		CONNECT_UPDATE_PROFILE, // remove connection from profile 
		FALSE);                 // fail if open files or jobs 
 
	if (dwResult == ERROR_NOT_CONNECTED) 
	{ 
		// It was not connected...
		return; 
	}  
	else if(dwResult != NO_ERROR) 
	{ 
		logToFile("Undefined error while disconnecting... \n\n");
		return; 
	}
	
	logToFile("Succesfully disconnected...\n\n");
}

int DC_CollectionShutdown()
{
	tableDelete(idhasht);
	freeValueTables();

	if (disconnect)
	{
		if (connected_host == NULL)
		{
			disconnectWin32(connected_host);
		}
	}

	return 0;
}



// Connect using the following credentials to the specified host
BOOLEAN connectWin32(char* host, char* username, char* password)
{
	DWORD connectResult; 
	NETRESOURCE networkResource; 

	logToFile("Attempting to establish WNET Connection to %s...\n", host);
	
    networkResource.dwType = RESOURCETYPE_ANY;
    networkResource.lpLocalName = NULL;
    networkResource.lpRemoteName = host;
    networkResource.lpProvider = NULL;
	
	// Call the WNetAddConnection2 function to make the connection, do not make this connection persistent	
	connectResult = WNetAddConnection2(&networkResource, (LPSTR) password, (LPSTR) username, 0);
 
	//  The local device is already connected to a network resource.
	if (connectResult == ERROR_ALREADY_ASSIGNED) 
	{ 
		logToFile("Already connected to specified resource.\n\n"); 
		return FALSE; 
	} 
	else if (connectResult == ERROR_LOGON_FAILURE)
	{
		logToFile("The credentials for logon failed.\n\n");
		return FALSE;
	} 
	else if(connectResult != NO_ERROR) 
	{ 
		logToFile("Undefined Error # %d\n\n", connectResult);
		return FALSE; 
	} 
 
	logToFile("Connected to specified resource.\n\n"); 
	connected_host = host;
	return TRUE;
}


int getRemoteKey(char* REG_HOST, HKEY* rKey)
{
	HKEY remoteKey;
	int rt;

	if (REG_HOST == NULL) 
	{
		remoteKey = HKEY_PERFORMANCE_DATA;
	}
	else 
	{
		if (USE_REG_CONNECT) 
		{
			logToFile("%sTrying to connect to remote registry %s\n",PREFIX,REG_HOST);

			if (is_connected == FALSE)
			{
				// We need to get a connection to the remote host
				
				// First, lets just try a default connection

				char* newHostName = (char*) malloc(sizeof(char)*strlen(REG_HOST)+4);
				sprintf(newHostName, "\\\\%s%c",REG_HOST, '\0');

				if (isConnectedWin32(newHostName))
				{
					// We are already connected
					logToFile("Already connected to %s\n",newHostName);
					is_connected = TRUE;
				}
				else
				{
					if (connectWin32(newHostName, NULL, NULL))
					{
						// SUCCESS
						is_connected = TRUE;
						disconnect = TRUE;
					}
					else
					{
						// Try using passed in credentials
						char* USERNAME = (char*) tableGet(hasht_variables,PERFMON_USERNAME);
						char* PASSWORD = (char*) tableGet(hasht_variables,PERFMON_PASSWORD);
						if (PASSWORD == NULL)
							PASSWORD = "";
						if (connectWin32(newHostName, USERNAME, PASSWORD))
						{
							is_connected = TRUE;
							disconnect = TRUE;
						}
					}							
				}
			}
			// else we already have a valid connection

			//
			// Connect to the registry (may be NULL in case we will connect to the local registry
			//
			if ((rt = RegConnectRegistry(REG_HOST,HKEY_PERFORMANCE_DATA,&remoteKey)) == ERROR_BAD_NETPATH)
			{
				logToFile("%sCONNECT ERROR : ERROR BAD NETPATH (Network Busy) %d",PREFIX,rt);

				return -1;
			}
			logToFile("%sConnected to remote machine OK\n",PREFIX);
		} 
		else 
		{
			remoteKey = HKEY_PERFORMANCE_DATA;
		}
	}
	*rKey = remoteKey;
	return ERROR_SUCCESS;
}

char* getReqList(HashTable* hasht_filters)
{

	int objLen = 10;
	int objSiz, z;
	char** objList = (char**) malloc(sizeof(char*) * objLen);

	int reqLen = 1024;
	int reqCount = 0;
	char* reqList = (char*) malloc(sizeof(char) * reqLen);

	memset(objList,0,sizeof(char*) * objLen);
	while (objLen <= (objSiz = tableKeysAsList(hasht_filters,objList,objLen))) {
		objLen *= 2;
		objList = (char**) realloc(objList,sizeof(char*) * objLen);
	}

	//
	// Build a list of the requested objects so we can request just those from HKEY_PERFORMANCE_DATA
	//
	reqCount = 0;
	for (z = 0; z < objSiz; z++) {
		if (objList[z] != NULL) {
			char objTmp[64];
			char* tmp = objList[z];
			int index = 0;

			while (tmp[index] != '\0') {
				objTmp[index] = tmp[index];
				if (objTmp[index] == 'C' || objTmp[index] == 'I') {
					index = -1;
					break;
				}
				index++;
			}

			if (index > 0) {
				//string length = index
				//memcopy over the string into the buffer

				while ((reqLen-2) < (reqCount + index + 10)) {
					//increase buffer size
					reqLen *= 2;
					reqList = (char*)  realloc(reqList,sizeof(char) * reqLen);
				}

				memcpy(&(reqList[reqCount]),tmp,index);
				reqCount += index;
				reqList[reqCount++] = ' ';

			}
		}
	}

	reqList[reqCount++] = '\0';

	free(objList);
	return reqList;
}


PPERF_DATA_BLOCK getPerfData(char* REG_HOST, BOOL results, HashTable* hasht_filters, XINT64 getGlobalAgain)
{
	XINT32 TmpBufferSize = 0;
	static XINT32 BufferSize = 64000;
	DWORD rt;
	char* reqFinal;
	char* reqList;
	char* tree_reqList = NULL;
	char request_buf[20480];

    PPERF_DATA_BLOCK PerfData = NULL;
	HKEY remoteKey;

	PerfData = (PPERF_DATA_BLOCK) malloc( BufferSize );

	if (getRemoteKey(REG_HOST, &remoteKey) == -1)
	{
		free(PerfData);
		return NULL;
	}

	if (results == TRUE)
	{
		reqList = getReqList(hasht_filters);
		if (REG_HOST == NULL || USE_REG_CONNECT) 
			sprintf(request_buf,"%s%c",reqList,'\0');
		else
			sprintf(request_buf,"Foreign %s %s%c",REG_HOST,reqList,'\0');

		reqFinal = request_buf;
	}
	else
	{
		tree_reqList = getReqList(hasht_tree);
		if (REG_HOST == NULL || USE_REG_CONNECT) 
			sprintf(request_buf,"%s%c",tree_reqList,'\0');
		else
			sprintf(request_buf,"Foreign %s %s%c",REG_HOST,tree_reqList,'\0');

		reqFinal = request_buf;
	}

	if (reqFinal[0] == '\0' || getGlobalAgain == 1)
	{
		reqFinal = "Global";
	}

	TmpBufferSize = BufferSize;
	while( (rt=RegQueryValueEx(remoteKey,
							   reqFinal,
							   NULL,
							   NULL,
							   (LPBYTE) PerfData,
							   (ULONG*) &TmpBufferSize )) == ERROR_MORE_DATA ) 
	{
		/* RKD:  The key is opened, we just failed to have enough room for the data.  Close the key, create room,
		 *        and run again.
		 */
		RegCloseKey(remoteKey);

		/* We closed the key, need to reopen it again
		 * 
		 */
		if (getRemoteKey(REG_HOST, &remoteKey) == -1)
		{
			free(PerfData);
			return NULL;
		}

		BufferSize *= 2;
		PerfData = (PPERF_DATA_BLOCK) realloc( PerfData, BufferSize );
		TmpBufferSize = BufferSize;
	}

	if (rt != ERROR_SUCCESS) 
	{
		logToFile("%sFailed to query performance data registry key for tree %d\n",PREFIX,rt);
		free(PerfData);
		return NULL;
	}

	if ((rt=RegCloseKey(remoteKey)) != ERROR_SUCCESS) 
		logToFile("%sRegCloseKey FAILED %d\n",PREFIX,rt); 
	
	if (results == TRUE)
		free(reqList);

	if (tree_reqList != NULL)
		free(tree_reqList);

	return PerfData;
}

int isBaseType(int type)
{
	switch(type)
	{
		case PERF_AVERAGE_BASE:
		case PERF_SAMPLE_BASE:
		case PERF_RAW_BASE:
		case PERF_COUNTER_MULTI_BASE:
		case PERF_LARGE_RAW_BASE: 
		case PERF_PRECISION_TIMESTAMP: return 1;
	}
	return 0;
}

void printtype(char* key, PPERF_COUNTER_DEFINITION PerfCntr)
{
	logToFile("Key: %s - ",key);
	switch(PerfCntr->CounterType)
	{

		case PERF_100NSEC_MULTI_TIMER:			printf("PERF_100NSEC_MULTI_TIMER");break;
		case PERF_100NSEC_MULTI_TIMER_INV:		printf("PERF_100NSEC_MULTI_TIMER_INV");break;
		case PERF_100NSEC_TIMER:				printf("PERF_100NSEC_TIMER");break;
		case PERF_100NSEC_TIMER_INV:			printf("PERF_100NSEC_TIMER_INV");break;
		case PERF_AVERAGE_BASE:					printf("PERF_AVERAGE_BASE");break;
		case PERF_AVERAGE_BULK:					printf("PERF_AVERAGE_BULK");break;
		case PERF_AVERAGE_TIMER:				printf("PERF_AVERAGE_TIMER");break;
		case PERF_COUNTER_100NS_QUEUELEN_TYPE:	printf("PERF_COUNTER_100NS_QUEUELEN_TYPE");break;
		case PERF_COUNTER_BULK_COUNT:			printf("PERF_COUNTER_BULK_COUNT");break;
		case PERF_COUNTER_COUNTER:				printf("PERF_COUNTER_COUNTER");break;
		case PERF_COUNTER_DELTA:				printf("PERF_COUNTER_DELTA");break;
		case PERF_COUNTER_LARGE_DELTA:			printf("PERF_COUNTER_LARGE_DELTA");break;
		case PERF_COUNTER_LARGE_QUEUELEN_TYPE:	printf("PERF_COUNTER_LARGE_QUEUELEN_TYPE");break;
		case PERF_COUNTER_LARGE_RAWCOUNT:		printf("PERF_COUNTER_LARGE_RAWCOUNT");break;
		case PERF_COUNTER_LARGE_RAWCOUNT_HEX:	printf("PERF_COUNTER_LARGE_RAWCOUNT_HEX");break;
		case PERF_COUNTER_MULTI_BASE:			printf("PERF_COUNTER_MULTI_BASE");break;
		case PERF_COUNTER_MULTI_TIMER:			printf("PERF_COUNTER_MULTI_TIMER");break;
		case PERF_COUNTER_MULTI_TIMER_INV:		printf("PERF_COUNTER_MULTI_TIMER_INV");break;
		case PERF_COUNTER_NODATA:				printf("PERF_COUNTER_NODATA");break;
		case PERF_COUNTER_QUEUELEN_TYPE:		printf("PERF_COUNTER_QUEUELEN_TYPE");break;
		case PERF_COUNTER_RAWCOUNT:				printf("PERF_COUNTER_RAWCOUNT");break;
		case PERF_COUNTER_RAWCOUNT_HEX:			printf("PERF_COUNTER_RAWCOUNT_HEX");break;
		case PERF_COUNTER_TEXT:					printf("PERF_COUNTER_TEXT");break;
		case PERF_COUNTER_TIMER:				printf("PERF_COUNTER_TIMER");break;
		case PERF_COUNTER_TIMER_INV:			printf("PERF_COUNTER_TIMER_INV");break;
		case PERF_ELAPSED_TIME:					printf("PERF_ELAPSED_TIME");break;
		case PERF_LARGE_RAW_BASE:				printf("PERF_LARGE_RAW_BASE");break;
		case PERF_OBJ_TIME_TIMER:				printf("PERF_OBJ_TIME_TIMER");break;
		case PERF_PRECISION_100NS_TIMER:		printf("PERF_PRECISION_100NS_TIMER");break;
		case PERF_PRECISION_OBJECT_TIMER:		printf("PERF_PRECISION_OBJECT_TIMER");break;
		case PERF_PRECISION_SYSTEM_TIMER:		printf("PERF_PRECISION_SYSTEM_TIMER");break;
		case PERF_RAW_BASE:						printf("PERF_RAW_BASE");break;
		case PERF_RAW_FRACTION:					printf("PERF_RAW_FRACTION");break;
		case PERF_SAMPLE_BASE:					printf("PERF_SAMPLE_BASE");break;
		case PERF_SAMPLE_COUNTER:				printf("PERF_SAMPLE_COUNTER");break;
		case PERF_SAMPLE_FRACTION:				printf("PERF_SAMPLE_FRACTION");break;
		case PERF_PRECISION_TIMESTAMP:			printf("PERF_PRECISION_TIMESTAMP");break;
		default : printf("Unrecognised Type");
	}
	printf("\n");
}

PPERF_OBJECT_TYPE FirstObject( PPERF_DATA_BLOCK PerfData )
{
    return( (PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength) );
}

PPERF_OBJECT_TYPE NextObject(PPERF_OBJECT_TYPE PerfObj)
{
    return( (PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength) );
}

PPERF_INSTANCE_DEFINITION FirstInstance(PPERF_OBJECT_TYPE PerfObj)
{
    return( (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength) );
}

PPERF_INSTANCE_DEFINITION NextInstance(PPERF_INSTANCE_DEFINITION PerfInst)
{
    PPERF_COUNTER_BLOCK PerfCntrBlk;

    PerfCntrBlk = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst + PerfInst->ByteLength);

    return( (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfCntrBlk + PerfCntrBlk->ByteLength) );
}

PPERF_COUNTER_DEFINITION FirstCounter(PPERF_OBJECT_TYPE PerfObj)
{
    return( (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength) );
}

PPERF_COUNTER_DEFINITION NextCounter(PPERF_COUNTER_DEFINITION PerfCntr)
{
    return( (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength) );
}

void formatOutputString(char* output)
{
	int i = 0;
	for (i=strlen(output)-1; i>= 0; i--)
	{
		if ((output[i] >= 48 && output[i] <= 57) || output[i] == 'E' || output[i] == '-')
		{
			// The char is either a number of an E. ignore it
		}
		else
		{
			output[i] = '.';
			return;
		}
	}
}

char* checktype(char* key, PPERF_COUNTER_BLOCK PtrToCntr,PPERF_COUNTER_DEFINITION PerfCntr,PPERF_DATA_BLOCK PerfData, PPERF_OBJECT_TYPE PerfObj)
{
	char* output;

	PPERF_COUNTER_DEFINITION PerfCntr2; 

	output = malloc(1024);
	memset(output,'\0',1024);

//	printtype(key,PerfCntr);

	switch(PerfCntr->CounterType)
	{
// Preliminary Testing Done...
		case PERF_100NSEC_TIMER:				getPERF_100NSEC_TIMER(output,key, PtrToCntr, PerfCntr, PerfData);break;
		case PERF_100NSEC_TIMER_INV:			getPERF_100NSEC_TIMER_INV(output,key, PtrToCntr, PerfCntr, PerfData);break;
		case PERF_AVERAGE_BULK:					PerfCntr2 = NextCounter(PerfCntr);getPERF_AVERAGE_BULK(output,key, PtrToCntr, PerfCntr, PerfCntr2);break;
		case PERF_AVERAGE_TIMER:				PerfCntr2 = NextCounter(PerfCntr);getPERF_AVERAGE_TIMER(output,key, PtrToCntr,PerfCntr, PerfCntr2 , PerfData);break;
		case PERF_COUNTER_BULK_COUNT:			getPERF_COUNTER_BULK_COUNT(output,key, PtrToCntr,PerfCntr,PerfData);break;
		case PERF_COUNTER_COUNTER:				getPERF_COUNTER_COUNTER(output,key, PtrToCntr,PerfCntr,PerfData);break;
		case PERF_COUNTER_LARGE_RAWCOUNT:		getPERF_COUNTER_LARGE_RAWCOUNT(output, key,PtrToCntr,PerfCntr);break;
		case PERF_COUNTER_LARGE_RAWCOUNT_HEX:	getPERF_COUNTER_LARGE_RAWCOUNT_HEX(output,key, PtrToCntr,PerfCntr);break;
		case PERF_COUNTER_NODATA:				getPERF_COUNTER_NODATA(output,key, PtrToCntr,PerfCntr);break;
		case PERF_COUNTER_RAWCOUNT:				getPERF_COUNTER_RAWCOUNT(output,key, PtrToCntr,PerfCntr);break;
		case PERF_ELAPSED_TIME:					getPERF_ELAPSED_TIME(output,key, PtrToCntr,PerfCntr, PerfData, PerfObj);break;
		case PERF_RAW_FRACTION:					PerfCntr2 = NextCounter(PerfCntr); getPERF_RAW_FRACTION(output,key, PtrToCntr,PerfCntr, PerfCntr2);break;
		case PERF_SAMPLE_FRACTION:				PerfCntr2 = NextCounter(PerfCntr); getPERF_SAMPLE_FRACTION(output,key, PtrToCntr,PerfCntr,PerfCntr2);break;

// NOT TESTED
		case PERF_COUNTER_DELTA:				getPERF_COUNTER_DELTA(output,key, PtrToCntr,PerfCntr);break;
		case PERF_COUNTER_LARGE_DELTA:			getPERF_COUNTER_LARGE_DELTA(output, key,PtrToCntr,PerfCntr);break;
		case PERF_SAMPLE_COUNTER:				PerfCntr2 = NextCounter(PerfCntr); getPERF_SAMPLE_COUNTER(output,key, PtrToCntr,PerfCntr,PerfCntr2);break;
		case PERF_COUNTER_LARGE_QUEUELEN_TYPE:	getPERF_COUNTER_LARGE_QUEUELEN_TYPE(output,key, PtrToCntr,PerfCntr, PerfData);break;
		case PERF_100NSEC_MULTI_TIMER:			PerfCntr2 = NextCounter(PerfCntr);getPERF_100NSEC_MULTI_TIMER(output, key, PtrToCntr, PerfCntr, PerfCntr2, PerfData);break;
		case PERF_100NSEC_MULTI_TIMER_INV:		PerfCntr2 = NextCounter(PerfCntr);getPERF_100NSEC_MULTI_TIMER_INV(output, key,PtrToCntr, PerfCntr, PerfCntr2, PerfData);break;
		case PERF_COUNTER_RAWCOUNT_HEX:			getPERF_COUNTER_RAWCOUNT_HEX(output,key, PtrToCntr, PerfCntr);break;
		case PERF_COUNTER_TIMER:				getPERF_COUNTER_TIMER(output,key, PtrToCntr,PerfCntr, PerfData);break;
		case PERF_COUNTER_TIMER_INV:			getPERF_COUNTER_TIMER_INV(output,key, PtrToCntr,PerfCntr, PerfData);break;
		case PERF_COUNTER_MULTI_TIMER:			PerfCntr2 = NextCounter(PerfCntr);getPERF_COUNTER_MULTI_TIMER(output,key, PtrToCntr,PerfCntr, PerfCntr2, PerfData);break;
		case PERF_COUNTER_MULTI_TIMER_INV:		PerfCntr2 = NextCounter(PerfCntr);getPERF_COUNTER_MULTI_TIMER_INV(output,key, PtrToCntr,PerfCntr, PerfCntr2, PerfData);break;
		case PERF_COUNTER_QUEUELEN_TYPE:		getPERF_COUNTER_QUEUELEN_TYPE(output,key, PtrToCntr,PerfCntr, PerfData);break;
		case PERF_OBJ_TIME_TIMER:				getPERF_OBJ_TIME_TIMER(output,key, PtrToCntr,PerfCntr, PerfData);break;
		case PERF_PRECISION_100NS_TIMER:		PerfCntr2 = NextCounter(PerfCntr);getPERF_PRECISION_100NS_TIMER(output,key, PtrToCntr,PerfCntr,PerfCntr2, PerfData);break;
		case PERF_PRECISION_OBJECT_TIMER:		PerfCntr2 = NextCounter(PerfCntr);printf("PERF_PRECISION_OBJECT_TIMER");break;
		case PERF_PRECISION_SYSTEM_TIMER:		PerfCntr2 = NextCounter(PerfCntr);printf("PERF_PRECISION_SYSTEM_TIMER");break;
		case PERF_COUNTER_100NS_QUEUELEN_TYPE:	getPERF_COUNTER_100NS_QUEUELEN_TYPE(output,key, PtrToCntr,PerfCntr, PerfData);break;

// NOT DONE YET
//		case PERF_COUNTER_TEXT: getPERF_COUNTER_TEXT(output, PtrToCntr,PerfCntr);break;

//Base Types - NO CALCULA TION NEEDED
		case PERF_AVERAGE_BASE: break;
		case PERF_SAMPLE_BASE: break;
		case PERF_RAW_BASE: break;
		case PERF_COUNTER_MULTI_BASE: break;
		case PERF_LARGE_RAW_BASE: break;
		case PERF_PRECISION_TIMESTAMP: break;

//Not Recognised Types 
		default: printf("Unrecognised Type (%s) - %d\n",key,PerfCntr->CounterType);
	}

	formatOutputString(output);
	return output;
}


int GetNameStrings(char* host)
{
    HKEY hKeyPerflib;      
    HKEY hKeyPerflib009;   
    DWORD dwMaxValueLen;   // maximum size of key values
    DWORD dwBuffer;        // bytes to allocate for buffers
    DWORD dwBufferSize;    // size of dwBuffer
    LPSTR lpCurrentString; // pointer for enumerating data strings
    DWORD dwCounter;       // current counter index
	HKEY remoteKeyLocalMachine;
	int rt;

// Get the number of Counter items.

	if ((rt = RegConnectRegistry(host,HKEY_LOCAL_MACHINE,&remoteKeyLocalMachine)) != ERROR_SUCCESS)
	{
		logToFile("%sRegConnectRegistry ERROR : ? %d\n",PREFIX,rt);
		return 0;
	}


    if( (rt = RegOpenKeyEx( remoteKeyLocalMachine,
        "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib",
        0,
        KEY_READ,
        &hKeyPerflib)) != ERROR_SUCCESS
		) {
		logToFile("%sRegOpenKeyEx ERROR : ? %d\n",PREFIX,rt);
		return 0;
	}

    dwBufferSize = sizeof(dwBuffer);

    if( (rt = RegQueryValueEx( hKeyPerflib,
        "Last Counter",
        NULL,
        NULL,
        (LPBYTE) &dwBuffer,
        &dwBufferSize )) != ERROR_SUCCESS
		) {
		logToFile("%sRegQueryValueEx ERROR : ? %d\n",PREFIX,rt);
        return 0;
	}

// Allocate memory for the names array.

    lpNamesArray = malloc( (dwBuffer+1) * sizeof(LPSTR) );
	lpNamesArraySize = dwBuffer+1;
    if( lpNamesArray == NULL) {
		return 0;
	}
	memset(lpNamesArray,'\0',(dwBuffer+1) * sizeof(LPSTR));

	lpHelpArray = malloc( (dwBuffer+3) * sizeof(LPSTR) );
    if( lpHelpArray == NULL) {
		free(lpNamesArray);
        return 0;
	}
	memset(lpHelpArray,'\0',(dwBuffer+3) * sizeof(LPSTR));

// Open the key containing the counter and object names.

    if( (rt=RegOpenKeyEx( remoteKeyLocalMachine,
    "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
        0,
        KEY_READ,
        &hKeyPerflib009)) != ERROR_SUCCESS
		){
		logToFile("%sRegOpenKexEx ERROR : ? %d\n",PREFIX,rt);
        return 0;
	}

// Get the size of the largest value in the key (Counter or Help).

    if( (rt=RegQueryInfoKey( hKeyPerflib009,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        &dwMaxValueLen,
        NULL,
        NULL)) != ERROR_SUCCESS
      )	{
		logToFile("%sRegQueryValueEx ERROR : ? %d\n",PREFIX,rt);
        return 0;
	}

// Allocate memory for the counter and object names.

    dwBuffer = dwMaxValueLen + 1;

    lpNameStrings = malloc( dwBuffer * sizeof(CHAR) );
	if (lpNameStrings == NULL) {
		free( lpNamesArray );
		free( lpHelpArray );
		return 0;
	}
    lpHelpStrings = malloc( dwBuffer * sizeof(CHAR) );
    if (lpHelpStrings == NULL)
    {
        free( lpNamesArray );
		free( lpHelpArray );
		free( lpNameStrings );
		return 0;
    }

// Read the Counter value.

	dwBufferSize = dwBuffer;

    if( (rt=RegQueryValueEx( hKeyPerflib009,
        "Counter",
        NULL,
        NULL,
        lpNameStrings, &dwBuffer )) != ERROR_SUCCESS
		) {
      free( lpNamesArray );
 	  free( lpHelpArray );
	  free( lpNameStrings );
	  free( lpHelpStrings );
	  logToFile("%sUnable to read counter value : ? %d\n",PREFIX,rt);
      return 0;
	}

    if( (rt = RegQueryValueEx( hKeyPerflib009,
        "Help",
        NULL,
        NULL,
        lpHelpStrings, &dwBufferSize )) != ERROR_SUCCESS
		) {
      free( lpNamesArray );
 	  free( lpHelpArray );
	  free( lpNameStrings );
	  free( lpHelpStrings );
	  logToFile("%sUnable to read counter value : ? %d\n",PREFIX,rt);
      return 0;
	}
    RegCloseKey( hKeyPerflib009 );
	RegCloseKey(remoteKeyLocalMachine);
    RegCloseKey( hKeyPerflib );

// Load names into an array, by index.

    for( lpCurrentString = lpNameStrings; *lpCurrentString;lpCurrentString += (lstrlen(lpCurrentString)+1) )
    {
        dwCounter = atol( lpCurrentString );

        lpCurrentString += (lstrlen(lpCurrentString)+1);

        lpNamesArray[dwCounter] = (LPSTR) lpCurrentString;
    }

    for( lpCurrentString = lpHelpStrings; *lpCurrentString;lpCurrentString += (lstrlen(lpCurrentString)+1) )
    {
        dwCounter = atol( lpCurrentString );

        lpCurrentString += (lstrlen(lpCurrentString)+1);

        lpHelpArray[dwCounter] = (LPSTR) lpCurrentString;
    }

    return 1;

}

void printout(char** data,int numstrings)
{
	int i;
	for (i=0;i<numstrings;i++)
	{
		logToFile("%s\n",data[i]);
	}
}

void cleardata(char** data,int numstrings)
{
	int i;
	for (i=0;i<numstrings;i++)
	{
		free(data[i]);
	}
	free(data);
}

wchar_t* getParentInstanceName(PPERF_DATA_BLOCK PerfData, DWORD parentinstindex, DWORD objectid)
{
	PPERF_OBJECT_TYPE PerfObj;
	PPERF_INSTANCE_DEFINITION PerfInst;
	wchar_t* strname;
	DWORD i,k; 

    PerfObj = FirstObject( PerfData );

    for( i=0; i < PerfData->NumObjectTypes; i++ )
    {
		if (PerfObj->ObjectNameTitleIndex != objectid)
		{
			PerfObj = NextObject( PerfObj );
			continue;
		}

        if( PerfObj->NumInstances > 0 )
        {
            PerfInst = FirstInstance( PerfObj );

            for( k=0; k < (DWORD)(PerfObj->NumInstances); k++ )
            {

				if (k != parentinstindex)
				{
					PerfInst = NextInstance( PerfInst );
					continue;
				}

				strname = ((wchar_t*)((PBYTE)PerfInst + PerfInst->NameOffset));

				return strname;		
			}
		}
		return NULL;
	}
	return NULL;
}


int getObjectID(PPERF_OBJECT_TYPE PerfObj, char* id, int idsiz) {
	int ret = _snprintf(id,idsiz,"%d\0",PerfObj->ObjectNameTitleIndex);
	if (ret < 0) ret = idsiz;
	return ret;
}

int getObjectCntrID(PPERF_OBJECT_TYPE PerfObj, PPERF_COUNTER_DEFINITION PerfCntr, char* id, int idsiz) {
	int ret = _snprintf(id,idsiz,"%dC%d\0",PerfObj->ObjectNameTitleIndex,PerfCntr->CounterNameTitleIndex);
	if (ret < 0) ret = idsiz;
	return ret;
}

int getInstanceKey(PPERF_DATA_BLOCK PerfData, PPERF_INSTANCE_DEFINITION PerfInst, char* id, int idsiz) {

	wchar_t* temp1;
	wchar_t* temp2;
	int len1;
	int len2;

	int ret;

	char* buf;
	int count = 0;

	if (PerfInst->ParentObjectInstance != 0)
	{
		temp1 = getParentInstanceName(PerfData,PerfInst->ParentObjectInstance,PerfInst->ParentObjectTitleIndex);
		temp2 = ((wchar_t*)((PBYTE)PerfInst + PerfInst->NameOffset));
		if (temp1 != NULL)
		{
			len1 = wcslen(temp1);
			len2 = wcslen(temp2);

			buf = malloc(sizeof(char) * (len1+len2+10));
			sprintf(buf,"%S/%S%c",temp1,temp2,'\0');
		}
		else
		{
			len2 = wcslen(temp2);

			buf = malloc(sizeof(char) * (len2+10));
			sprintf(buf,"%S%c",temp2,'\0');
		}	
	}
	else
	{
		temp2 = ((wchar_t*)((PBYTE)PerfInst + PerfInst->NameOffset));

		len2 = wcslen(temp2);

		buf = malloc(sizeof(char) * (len2+10));
		sprintf(buf,"%S%c",temp2,'\0');
			
	}	

	ret = _snprintf(id,idsiz,"%s%c",buf,'\0');
	if (ret < 0) ret = idsiz;
	free(buf);
	return ret;
}

int getInstanceName(PPERF_DATA_BLOCK PerfData, char* instKey, int count, char* id, int idsiz) {
	int ret;

	if (count == 0) {
		ret = _snprintf(id,idsiz,"%s%c",instKey,'\0');
		if (ret < 0) ret = idsiz;
		return ret;
	} else {
		ret = _snprintf(id,idsiz,"%s#%d%c",instKey,count,'\0');
		if (ret < 0) ret = idsiz;
		return ret;
	}
}

int getInstanceID(PPERF_DATA_BLOCK PerfData, PPERF_OBJECT_TYPE PerfObj, char* instName, char* id, int idsiz, HashTable* idhasht, int populate_idhash) {
	int keyLen = sizeof(char) * 128;
	char* keyID = malloc(keyLen);
	int instid;
	int ret;

	while (0 > _snprintf(keyID,keyLen,"%dI%s%c",PerfObj->ObjectNameTitleIndex,instName,'\0')) {
		keyLen *= 2;
		keyID = realloc(keyID,keyLen);
	}

	instid = (int)tableGet(idhasht,keyID);

	if (instid == 0 && populate_idhash)
	{
		instid = idhasht->count+1;
 
		tablePut(idhasht,keyID,instid);
	}

	free(keyID);

	ret = _snprintf(id,idsiz,"%dI%d%c",PerfObj->ObjectNameTitleIndex,instid,'\0');
	if (ret < 0) ret = idsiz;
	return ret;
}

int getInstanceCntrID(PPERF_DATA_BLOCK PerfData, PPERF_OBJECT_TYPE PerfObj, char* instid, PPERF_COUNTER_DEFINITION PerfCntr, char* id, int idsiz, HashTable* idhasht) {
	int ret = _snprintf(id,idsiz,"%sC%d%c",instid,PerfCntr->CounterNameTitleIndex,'\0');
	if (ret < 0) ret = idsiz;
	return ret;
}

void clearData(char** array, int count)
{
	int i;
	for (i =0 ;i<count;i++) {
		free(array[i]);
	}
	free(array);
}

unsigned long getObjectCount(PPERF_DATA_BLOCK PerfData)
{
	PPERF_OBJECT_TYPE PerfObj;
	unsigned long count =0;
	unsigned long totalsize = 0;

	if (PerfData->TotalByteLength == PerfData->HeaderLength)
		return 0;

	PerfObj = FirstObject(PerfData);
	totalsize = PerfData->HeaderLength + PerfObj->TotalByteLength;
	count++;

	while (totalsize < PerfData->TotalByteLength)
	{
		PerfObj = NextObject(PerfObj);
		count++;
		totalsize = totalsize + PerfObj->TotalByteLength;
	}

	return count;
}

char* getRemoteHost()
{
	char* remoteHost = (char*) tableGet(hasht_variables,PERFMON_KEY_HOST);

	while (remoteHost == NULL)
	{
		logToFile("Waiting for variables to arrive...");
		Sleep(50);
		remoteHost = (char*) tableGet(hasht_variables,PERFMON_KEY_HOST);
	}

	if (isVista)
	{
		if (strcmp(remoteHost,"localhost") == 0 || strcmp(remoteHost,"127.0.0.1") == 0)
		{
			return NULL;
		}
	}
	else
	{
		if (strcmp(remoteHost,"localhost") == 0)
		{
			return NULL;
		}
	}


	return remoteHost;
}

char** DC_CollectionGetResults(int* numresults)
{
	int z;

	PPERF_DATA_BLOCK PerfData = NULL;

	char* REG_HOST;

	REG_HOST = getRemoteHost();	


	PerfData = getPerfData(REG_HOST, TRUE, hasht_filters,0);


	if (PerfData != NULL)
	{
	    PPERF_OBJECT_TYPE PerfObj;
		PPERF_COUNTER_DEFINITION PerfCntr;
		PPERF_INSTANCE_DEFINITION PerfInst;
		PPERF_COUNTER_BLOCK PtrToCntr;

		HashTable* instancecount = tableCreate();

		DWORD i, j, k;

		int objLen = sizeof(char) * 128;
		char* objID = malloc(128);
		int cntrLen = sizeof(char) * 128;
		char* cntrID = malloc(128);
		int instLen = sizeof(char) * 128;
		char* instID = malloc(128);
		int instnLen = sizeof(char) * 128;
		char* instName = malloc(instnLen);
		int instkLen = sizeof(char) * 128;
		char* instKey = malloc(instkLen);
		int count;

		int value_len = 0;
		char* value;

		if (results_len != -1) {
			for (z = 0; z < value_index; z++) {
				free(results[z]);
			}
			if (results != NULL) {
				free(results);
			} else {
//				logToFile("NULL results! length = %d\n",value_index);
			}
		}
		value_index = 0;
		results_len = 10;
		results = malloc(sizeof(char*) * results_len);

		PerfObj = FirstObject( PerfData );

		for( i=0; i < PerfData->NumObjectTypes; i++ ) 
		{			
			if (isVista)
			{
				// An out of bounds check on the name array
				// A generic fix that is valid for all windows versions
				if (PerfObj->ObjectNameTitleIndex > lpNamesArraySize)
				{
					PerfObj = NextObject( PerfObj );
					continue;
				}
			}

			while (objLen <= getObjectID(PerfObj,objID,objLen)) 
			{
				objLen *= 2;
				objID = realloc(objID,objLen);
			}

			if (tableGet(hasht_filters,objID) != (XINT64)NULL) 
			{
				//this object is ON
				if (PerfObj->NumInstances == PERF_NO_INSTANCES) 
				{
					// just check its counters
					
					PerfCntr = FirstCounter(PerfObj);

					for ( j=0; j < PerfObj->NumCounters; j++ ) {
						while (cntrLen <= getObjectCntrID(PerfObj,PerfCntr,cntrID,cntrLen)) {
							cntrLen *= 2;
							cntrID = realloc(cntrID,cntrLen);
						}
						if ((tableGet(hasht_filters,cntrID) != (XINT64)NULL) && (isBaseType(PerfCntr->CounterType)==0)) {
							//this counter is ON - send its value
							PtrToCntr = (PPERF_COUNTER_BLOCK) ((PBYTE)PerfObj + PerfObj->DefinitionLength );

							value = checktype(cntrID,PtrToCntr,PerfCntr,PerfData,PerfObj);
							value_len = strlen(cntrID) + strlen(value) + 4;

							results[value_index] = malloc(value_len);
							_snprintf(results[value_index],value_len,"%s%c%s%c",cntrID,'\0',value,'\0');

							value_index++;

							if (value_index == results_len) {
								results_len *= 2;
								results = realloc(results,sizeof(char*) * results_len);
							}

							free(value);
						}

						PerfCntr = NextCounter(PerfCntr);						
					}

				} else {
					//check its instances and their counters

					tableClear(instancecount);

					PerfInst = FirstInstance( PerfObj );

					for ( j=0; j < ((DWORD) (PerfObj->NumInstances)); j++) 
					{

						while (instkLen <= getInstanceKey(PerfData,PerfInst,instKey,instkLen)) 
						{
							instkLen *= 2;
							instKey = realloc(instKey,instkLen);
						}

						count = (int)tableGet(instancecount,instKey);
						while (instnLen <= getInstanceName(PerfData,instKey,count,instName,instnLen)) 
						{
							instnLen *= 2;
							instName = realloc(instName,instnLen);
						}
						tablePut(instancecount,instKey,count+1);

						while (instLen <= getInstanceID(PerfData,PerfObj,instName,instID,instLen,idhasht,FALSE)) 
						{
							instLen *= 2;
							instID = realloc(instID,instLen);
						}
				
						if (tableGet(hasht_filters,instID) != (XINT64)NULL) 
						{
							PerfCntr = FirstCounter(PerfObj);

							for ( k=0; k < PerfObj->NumCounters; k++ ) 
							{
								while (cntrLen <= getInstanceCntrID(PerfData,PerfObj,instID,PerfCntr,cntrID,cntrLen,idhasht)) 
								{
									cntrLen *= 2;
									cntrID = realloc(cntrID,cntrLen);
								}
								
								if ((tableGet(hasht_filters,cntrID) != (XINT64)NULL)&& (isBaseType(PerfCntr->CounterType)==0)) 
								{
									//this counter is ON - send its value
									PtrToCntr = (PPERF_COUNTER_BLOCK) ((PBYTE)PerfInst + PerfInst->ByteLength);

									value = checktype(cntrID,PtrToCntr,PerfCntr,PerfData,PerfObj);

									value_len = strlen(cntrID) + strlen(value) + 4;
									results[value_index] = malloc(value_len);
									_snprintf(results[value_index],value_len,"%s%c%s%c",cntrID,'\0',value,'\0');
									value_index++;

									if (value_index == results_len) 
									{
										results_len *= 2;
										results = realloc(results,sizeof(char*) * results_len);
									}

									free(value);

								}
								PerfCntr = NextCounter(PerfCntr);						
							}
						}
						PerfInst = NextInstance( PerfInst );
					}
				}
			}
			PerfObj = NextObject( PerfObj );
		}
		free(PerfData);

		tableClear(instancecount);

		tableDelete(instancecount);

		free(objID);
		free(instID);
		free(cntrID);
		free(instName);
		free(instKey);

		*numresults = value_index;

		return results;
	}
	else
	{
		*numresults = 0;

		return NULL;
	}
}

void addToTree(char*** treedatp, int* treelenp, int* treeindexp, char* pid, char* id, char* name, char* help, char* suffix) 
{
	char** treedat = *treedatp;
	int treelen = *treelenp;
	int treeindex = *treeindexp;
	int counter = 0;

	//this needs to be pid\id\name\help\suffix
	int datLen = 128;
	treedat[treeindex] = malloc(sizeof(char) * datLen);

	// Check if this is a counter or not.
	if (strchr(id, 'C') == NULL)
		counter = 0;
	else 
		counter = 1;

//logToFile("ADD %s %s %s %s %s\n",pid,id,name,"(help)",suffix);

	while (0 >  _snprintf(treedat[treeindex],datLen,"%s%c%s%c%s%c%s%c%s%c%d%c",
									pid,'\0',		//pid
									id,'\0',		//id
									name,'\0',		//name
									help,'\0',		//help
									suffix,'\0',    //suffix
									counter,'\0')) 
	{	
		datLen *=2;

		treedat[treeindex] = realloc(treedat[treeindex],sizeof(char) * datLen);
	}

	treeindex++;

	//extend tree length if necessary
	if (treeindex == treelen) {
		treelen += 20;
		treedat = realloc(treedat,sizeof(char*) * treelen);
	}
	
	*treedatp = treedat;
	*treelenp = treelen;
	*treeindexp = treeindex;
}

char* DC_CollectionGetAgentName()
{
	logToFile("Sending agent name from the collection DLL - %s\n",agent_type);
	return agent_type;
}

char** DC_CollectionGetTree( int* numstrings)
{
	char* REG_HOST;
	XINT64 getGlobalAgain;
	PPERF_DATA_BLOCK PerfData;

	getGlobalAgain = tableGet(hasht_variables,REGET_CHILDREN);

	REG_HOST = getRemoteHost();		

	logToFile("%sCollecting from host %s\n",PREFIX,REG_HOST);

	PerfData = getPerfData(REG_HOST, FALSE, NULL,getGlobalAgain);
	if (PerfData != NULL)
	{
	    PPERF_OBJECT_TYPE PerfObj;
	    PPERF_INSTANCE_DEFINITION PerfInst;
	    PPERF_COUNTER_DEFINITION PerfCntr;

		HashTable* instancecount = tableCreate();

		int treelen = 10;
		int treeindex = 0;
		char** treedat = malloc(sizeof(char*) * treelen);

		DWORD i, j, k;

		int objLen = sizeof(char) * 128;
		char* objID = malloc(objLen);

		int instLen = sizeof(char) * 128;
		char* instID = malloc(instLen);

		int cntrLen = sizeof(char) * 128;
		char* cntrID = malloc(cntrLen);

		int instnLen = sizeof(char) * 128;
		char* instName = malloc(instnLen);

		int instkLen = sizeof(char) * 128;
		char* instKey = malloc(instkLen);
		int count;

		//get the name and help arrays
		if(GetNameStrings(REG_HOST)==0)
		{
			logToFile("%sError getting performance data from the registry...",PREFIX);

			/* RKD:  Before we return we need to free the perfData object we recieved from the kernel */
			free(PerfData);

			return 0;
		}

		//iterate through everything 
		//logToFile("Object Count is %lu\n",PerfData->NumObjectTypes);
		PerfObj = FirstObject( PerfData );

		for( i=0; i < PerfData->NumObjectTypes; i++ ) 
		{	
			if (isVista)
			{
				// Out of bounds check
				if (PerfObj->ObjectNameTitleIndex > lpNamesArraySize)
				{
					PerfObj = NextObject( PerfObj );
					continue;
				}
			}

			//
			// Get the object ID
			//
			while (objLen <= getObjectID(PerfObj,objID,objLen)) 
			{
				objLen *=2;
				objID = realloc(objID,objLen);
			}

			//
			// Unseen object - trace its data
			//
			addToTree(&treedat,&treelen,&treeindex,
								"",												//pid
								objID,											//id
								lpNamesArray[PerfObj->ObjectNameTitleIndex],	//name
								lpHelpArray[PerfObj->ObjectHelpTitleIndex],		//help / desc
								"");											//suffix

			if (PerfObj->NumInstances == PERF_NO_INSTANCES) 
			{
				// just check its counters				
				PerfCntr = FirstCounter(PerfObj);

				for ( j=0; j < PerfObj->NumCounters; j++ ) 
				{
					char *t1, *t2;
					//
					// Get the counter ID
					//
					while (cntrLen <= getObjectCntrID(PerfObj,PerfCntr,cntrID,cntrLen)) 
					{
						cntrLen *= 2;
						cntrID = realloc(cntrID,cntrLen);
					}

					//
					// Unseen counter - trace its data
					//

					t1 = lpNamesArray[PerfCntr->CounterNameTitleIndex];
					t2 = lpHelpArray[PerfCntr->CounterHelpTitleIndex];

					if (t1 == NULL)
					{
						logToFile("%sSkipped Counter #%d\n",PREFIX,PerfCntr->CounterNameTitleIndex);							
						logToFile("%s\n",t2);
						continue;
					}
					else if (t1[0] == '\0')
					{
						logToFile("%sSkipped Counter #%d\n",PREFIX,PerfCntr->CounterNameTitleIndex);
						logToFile("%s\n",t2);
						continue;
					}

					addToTree(&treedat,&treelen,&treeindex,
										objID,												//pid
										cntrID,											//id
										lpNamesArray[PerfCntr->CounterNameTitleIndex],	//name
										lpHelpArray[PerfCntr->CounterHelpTitleIndex],		//help / desc
										"");											//suffix

					PerfCntr = NextCounter(PerfCntr);						
				}
			} 
			else 
			{
				//check its instances and their counters

				tableClear(instancecount);

				PerfInst = FirstInstance( PerfObj );

				for ( j=0; j < ((DWORD)(PerfObj->NumInstances)); j++) 
				{

					while (instkLen <= getInstanceKey(PerfData,PerfInst,instKey,instkLen)) 
					{
						instkLen *= 2;
						instKey = realloc(instKey,instkLen);
					}

					count = (int)tableGet(instancecount,instKey);
					while (instnLen <= getInstanceName(PerfData,instKey,count,instName,instnLen)) 
					{
						instnLen *= 2;
						instName = realloc(instName,instnLen);
					}
					tablePut(instancecount,instKey,count+1);
					
					//
					// Get the instance ID
					//
					while(instLen <= getInstanceID(PerfData,PerfObj,instName,instID,instLen,idhasht,TRUE)) 
					{
						instLen *= 2;
						instID = realloc(instID,instLen);
					}

					//
					// Unseen instance - trace its data
					//

					addToTree(&treedat,&treelen,&treeindex,
										objID,											//pid
										instID,											//id
										instName,										//name
										"",												//help / desc
										"");											//suffix
					
					PerfCntr = FirstCounter(PerfObj);

					for ( k=0; k < PerfObj->NumCounters; k++ ) 
					{
						char* t1;
						//
						// Get the counter ID
						//
						while (cntrLen <= getInstanceCntrID(PerfData,PerfObj,instID,PerfCntr,cntrID,cntrLen,idhasht)) 
						{
							cntrLen *= 2;
							cntrID = realloc(cntrID,cntrLen);
						}
					
						//
						// Unseen instance counter - trace its data
						//

						t1 = lpNamesArray[PerfCntr->CounterNameTitleIndex];

						if (t1 == NULL)
						{
							logToFile("%sSkipped Counter #%d\n",PREFIX,PerfCntr->CounterNameTitleIndex);
							continue;
						}
						else if (t1[0] == '\0')
						{
							logToFile("%sSkipped Counter #%d\n",PREFIX,PerfCntr->CounterNameTitleIndex);
							continue;
						}		

						addToTree(&treedat,&treelen,&treeindex,
											instID,											//pid
											cntrID,											//id
											lpNamesArray[PerfCntr->CounterNameTitleIndex],	//name
											lpHelpArray[PerfCntr->CounterHelpTitleIndex],	//help / desc
											"");											//suffix
							

						PerfCntr = NextCounter(PerfCntr);						
					}
					PerfInst = NextInstance( PerfInst );
				}

			}

			PerfObj = NextObject( PerfObj );
		}

		free(PerfData);
		tableDelete(instancecount);
		free(objID);
		free(instID);
		free(cntrID);
		free(instName);
		free(instKey);
		free( lpNameStrings );
		free( lpHelpStrings );
		free( (void*)lpNamesArray );
		free( (void*)lpHelpArray );
		*numstrings = treeindex;
		return treedat;
	}
	else
	{
		*numstrings = 0;
		return NULL;
	}
}


