//============================================================================
// File: PERFSNAP.CPP
// Author: Matt Pietrek
// From: Microsoft Systems Journal, "Under the Hood", March 1996
//============================================================================

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winperf.h>
#include <stdlib.h>
#include <tchar.h>
#pragma hdrstop
#include "titledb.h"
#include "perfsnap.h"
#include "makeptr.h"

PBYTE CPerfSnapshot::c_pBuffer = NULL;
DWORD CPerfSnapshot::c_cbBufferSize = 0;

CPerfSnapshot::CPerfSnapshot(
	CPerfTitleDatabase * pCounterTitles )
{
	m_pPerfDataHeader = 0;
	m_pCounterTitles = pCounterTitles;
}

CPerfSnapshot::~CPerfSnapshot( void )
{
	DisposeSnapshot();
}

BOOL
CPerfSnapshot::TakeSnapshot( PCTSTR pszSnapshotItems )
{
	DisposeSnapshot();  // Clear out any current snapshot

	// Convert the input string (e.g., "Process") into the form required
	// by the HKEY_PERFORMANCE_DATA key (e.g., "232")

	TCHAR szConvertedItemNames[ 256 ];
	if ( !ConvertSnapshotItemName( pszSnapshotItems, szConvertedItemNames,
								   sizeof(szConvertedItemNames) ) )
		return FALSE;

	DWORD cbAllocSize = 0;
	LONG retValue;

	while ( 1 ) // Loop until we get the data, or we fail unexpectedly
	{
		retValue = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
									szConvertedItemNames, 0, 0,
									c_pBuffer, &c_cbBufferSize );

		if ( retValue == ERROR_SUCCESS )    // We apparently got the snapshot
		{
			m_pPerfDataHeader = (PPERF_DATA_BLOCK)c_pBuffer;

			// Verify that the signature is a unicode "PERF"
			if ( memcmp( m_pPerfDataHeader->Signature, L"PERF", 8 ) )
				break;

			return TRUE;
		}
		else if ( retValue != ERROR_MORE_DATA ) // Anything other failure
			break;                              // code means something
										        // bad happened, so bail out.

		// If we get here, our buffer wasn't big enough.  Delete it and
		// try again with a bigger buffer.
		delete [] c_pBuffer;

		// The new buffer size will be 4096 bytes bigger than the larger
		// of: 1) The previous allocation size, or 2) The size that the
		// RegQueryValueEx call said was necessary.
		if ( c_cbBufferSize > cbAllocSize )
			cbAllocSize = c_cbBufferSize + 4096;
		else
			cbAllocSize += 4096;

		// Allocate a new, larger buffer in preparation to try again.
		c_pBuffer = new BYTE[ cbAllocSize ];
		if ( !c_pBuffer )
			break;

		c_cbBufferSize = cbAllocSize;
	}

	// If we get here, the RegQueryValueEx failed unexpectedly.
	return FALSE;
}

void
CPerfSnapshot::DisposeSnapshot( void )
{
	m_pPerfDataHeader = 0;
}

void
CPerfSnapshot::CleanUp( void )
{
	delete [] c_pBuffer;
	c_pBuffer = 0;
	c_cbBufferSize = 0;
}

DWORD
CPerfSnapshot::GetNumObjectTypes( void )
{
	return m_pPerfDataHeader ? m_pPerfDataHeader->NumObjectTypes: 0;
}

BOOL
CPerfSnapshot::GetSystemName( PTSTR pszSystemName, DWORD nSize )
{
	if ( !m_pPerfDataHeader )   // If no snapshot data, bail out.
		return FALSE;

	// Make sure the input buffer size is big enough
	if ( nSize < m_pPerfDataHeader->SystemNameLength )
		return FALSE;

	// Make a unicode string point to the system name string
	// that's stored in the PERF_DATA_BLOCK
	LPWSTR lpwszName = MakePtr( LPWSTR, m_pPerfDataHeader,
								m_pPerfDataHeader->SystemNameOffset );

	#ifdef UNICODE  // Copy the PERF_DATA_BLOCK string to the input buffer
	lstrcpy( pszSystemName, lpwszName );
	#else
	wcstombs( pszSystemName, lpwszName, nSize );
	#endif

	return TRUE;
}

PVOID
CPerfSnapshot::GetPostHeaderPointer( void )
{
	// Returns a header to the first byte following the PERF_DATA_BLOCK
	// (including the variable length system name string at the end)
	return m_pPerfDataHeader ?
			MakePtr(PVOID, m_pPerfDataHeader,m_pPerfDataHeader->HeaderLength)
			: 0;
}

BOOL
CPerfSnapshot::ConvertSnapshotItemName( PCTSTR pszIn,
										PTSTR pszOut, DWORD nSize )
{
	if ( IsBadStringPtr( pszIn, 0xFFFFFFFF ) )
		return FALSE;


	DWORD objectID = m_pCounterTitles->GetIndexFromTitleString(pszIn);

	if ( objectID )
		pszOut += wsprintf( pszOut, TEXT("%u "), objectID );
	else
		pszOut += wsprintf( pszOut, TEXT("%s "), pszIn );

	return TRUE;
}