// ShowPerfLibDlg.cpp : implementation file
//

#include "stdafx.h"
#include "ShowPerfLib.h"
#include "ShowPerfLibDlg.h"
#include "PerfSelection.h"
#include "ntreg.h"
#include "listperf.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CShowPerfLibDlg dialog

CShowPerfLibDlg::CShowPerfLibDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CShowPerfLibDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CShowPerfLibDlg)
	m_strRequest = _T("");
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	m_strService = _T("");
}

CShowPerfLibDlg::CShowPerfLibDlg(CString strService, BOOL bRefCheck, CWnd* pParent /*=NULL*/)
	: m_strService( strService ), m_bRefCheck(bRefCheck), CDialog(CShowPerfLibDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

CShowPerfLibDlg::~CShowPerfLibDlg()
{
	CloseLibrary();
	FreeLibrary( m_hLib );
}

void CShowPerfLibDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CShowPerfLibDlg)
	DDX_Control(pDX, IDC_GET_COUNTER, m_wndGetCounter);
	DDX_Control(pDX, IDC_ERRORS, m_wndErrorLog);
	DDX_Control(pDX, IDC_PERFTREE, m_wndPerfTree);
	DDX_Text(pDX, IDC_REQUEST, m_strRequest);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CShowPerfLibDlg, CDialog)
	//{{AFX_MSG_MAP(CShowPerfLibDlg)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_EN_CHANGE(IDC_REQUEST, OnChangeRequest)
	ON_BN_CLICKED(IDC_GET_COUNTER, OnGetCounter)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////	////////////////////////////////////////////////////////////////
// CShowPerfLibDlg message handlers

BOOL CShowPerfLibDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
//	SetIcon(m_hIcon, TRUE);			// Set big icon
//	SetIcon(m_hIcon, FALSE);		// Set small icon

	SetWindowText( m_strService );

	int nCol;
	nCol = m_wndErrorLog.InsertColumn(0, "Type", LVCFMT_LEFT, 75 );
	nCol = m_wndErrorLog.InsertColumn(1, "Description", LVCFMT_LEFT, 500 );

	InitPerfLibTree();

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CShowPerfLibDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CShowPerfLibDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}


/////////////////////////////////////////////////////////////////////////////
//
//	Perf Lib methods
//
/////////////////////////////////////////////////////////////////////////////

BOOL CShowPerfLibDlg::InitPerfLibTree()
{				
	BOOL bRet = TRUE;

	CWaitCursor	wc;

	if (InitService())
	{
		if ( OpenLibrary() )
		{
			bRet = GetData();
		}
	}


	return bRet;
}

BOOL CShowPerfLibDlg::InitService()
{
	BOOL bRet = TRUE;

	CNTRegistry Reg;
	WCHAR	wszKey[128];

	char	szLog[256];

	swprintf(wszKey, L"SYSTEM\\CurrentControlSet\\Services\\%S\\Performance", m_strService);

	if ( CNTRegistry::no_error == Reg.Open( HKEY_LOCAL_MACHINE, wszKey ) )
	{
		WCHAR*	wszTemp;
		Reg.GetStr(L"Library", &wszTemp);
		m_strPerfLib = wszTemp;
		delete [] wszTemp;
		sprintf(szLog, "Library Name: %s", m_strPerfLib);
		Log( PERF_INFO, szLog );

		Reg.GetStr(L"Open", &wszTemp);
		m_strOpen = wszTemp;
		delete [] wszTemp;
		sprintf(szLog, "Open Function Name: %s", m_strOpen);
		Log( PERF_INFO, szLog );

		Reg.GetStr(L"Collect", &wszTemp);
		m_strCollect = wszTemp;
		delete [] wszTemp;
		sprintf(szLog, "Collect Function Name: %s", m_strCollect);
		Log( PERF_INFO, szLog );

		Reg.GetStr(L"Close", &wszTemp);
		m_strClose = wszTemp;
		delete [] wszTemp;
		sprintf(szLog, "Close Function Name: %s", m_strClose);
		Log( PERF_INFO, szLog );
	}
	else
	{
		sprintf(szLog, "Counld not open the %S registry key", wszKey);
		Log( PERF_FAILURE, szLog );

		bRet = FALSE;
	}

	return bRet;
}

BOOL CShowPerfLibDlg::OpenLibrary()
{
	BOOL bRet = TRUE;

	char szLog[256];

	m_hLib = LoadLibrary( (LPCSTR)m_strPerfLib );

	if ( NULL != m_hLib )
	{
		sprintf(szLog, "Loaded the %s Performance Library", m_strPerfLib);
		Log( PERF_SUCCESS, szLog );

		m_pfnOpenProc = (PM_OPEN_PROC*) GetProcAddress( m_hLib, m_strOpen );
		m_pfnCollectProc = (PM_COLLECT_PROC*) GetProcAddress( m_hLib, m_strCollect );
		m_pfnCloseProc = (PM_CLOSE_PROC*) GetProcAddress( m_hLib, m_strClose );

		bRet = ( ( NULL != m_pfnOpenProc ) &&
				 ( NULL != m_pfnCollectProc ) &&
				 ( NULL != m_pfnCloseProc ) );

		Log (( NULL != m_pfnOpenProc )?PERF_SUCCESS:PERF_FAILURE, "Get Open Procedure Address");
		Log (( NULL != m_pfnCollectProc )?PERF_SUCCESS:PERF_FAILURE, "Get Collect Procedure Address");
		Log (( NULL != m_pfnCloseProc )?PERF_SUCCESS:PERF_FAILURE, "Get Close Procedure Address");
	}
	else
	{
		sprintf(szLog, "Could not load the %s Performance Library", m_strPerfLib);
		Log( PERF_FAILURE, szLog );

		bRet = FALSE;
	}

	if ( bRet )
	{
		bRet = ( m_pfnOpenProc( NULL ) == ERROR_SUCCESS );
		Log( bRet?PERF_SUCCESS:PERF_FAILURE, "Opening Performance Library" );

		if ( bRet && m_bRefCheck )
		{
			bRet = ( m_pfnOpenProc( NULL ) == ERROR_SUCCESS );
			Log( bRet?PERF_SUCCESS:PERF_FAILURE, "Opening Performance Library" );

			if ( bRet )
			{
				bRet = ( m_pfnCloseProc() == ERROR_SUCCESS );
				Log( bRet?PERF_SUCCESS:PERF_FAILURE, "Closing Performance Library" );
			}
		}
	}

	return bRet;
}

BOOL CShowPerfLibDlg::Collect( CString strSpace )
{
	BOOL bRet = TRUE;

	int nRet = ERROR_MORE_DATA;
	char	szLog[256];

	DWORD	dwGuardSize  = 1024;
	DWORD	dwSize = 64000;

	BOOL bValid = HeapValidate( GetProcessHeap(), NULL, NULL );

	BYTE*	pSafeBuffer = new BYTE[dwSize + ( 2 * dwGuardSize )];

	memset( pSafeBuffer, 0xFFFFFFFF, dwSize + ( 2 * dwGuardSize ) );
	BYTE*	pBuffer = pSafeBuffer + dwGuardSize;
	BYTE*	pBufferPtr = NULL;

	DWORD	dwNumClasses = 0;
	DWORD	dwBufferSize = 0;

	WCHAR	wszSpace[256];

	mbstowcs( wszSpace, (LPCSTR)strSpace, 256 );

	while (ERROR_MORE_DATA == nRet)
	{
		pBufferPtr = pBuffer;
		dwBufferSize = dwSize;

		nRet = m_pfnCollectProc( wszSpace, (LPVOID*)(&pBufferPtr), &dwBufferSize, &dwNumClasses );
		sprintf(szLog, "Collect function called using %S counters", wszSpace);
		Log((nRet==ERROR_SUCCESS)?PERF_SUCCESS:((nRet==ERROR_MORE_DATA)?PERF_MORE_DATA:PERF_FAILURE), szLog );

		if (ERROR_SUCCESS == nRet)
		{
			sprintf(szLog, "Required buffer size: %d bytes", dwBufferSize);
			Log( PERF_INFO, szLog );
			sprintf(szLog, "Number of objects returned: %d", dwNumClasses);
			Log( PERF_INFO, szLog );

			try
			{
				BuildSubTree( strSpace, pBuffer, dwNumClasses );
			}
			catch(...)
			{
				AfxMessageBox("Exception thrown while processing blob");
			}
		}
		else if ( ERROR_MORE_DATA == nRet )
		{
			dwSize += 8000;

			if ( NULL != pSafeBuffer )
			{
				delete [] pSafeBuffer;
			}

			pSafeBuffer = new BYTE[dwSize + ( 2 * dwGuardSize )];
			memset( pSafeBuffer, 0xFFFFFFFF, dwSize + ( 2 * dwGuardSize ) );
		}
		else
		{
			bRet = FALSE;
		}
	}

	if ( NULL != pBuffer )
	{
		delete [] pSafeBuffer;
	}

	return bRet;
}

BOOL CShowPerfLibDlg::GetData()
{
	BOOL bRet = TRUE;
	char	szSpace[16];

	for ( int i = 0; (i < 2) && bRet; i++ )
	{
		if ( 0 == i )
		{
			strcpy (szSpace, "Global");
		}
		else
		{
			strcpy (szSpace, "Costly");
		}

		bRet = Collect( szSpace );
	}

	return bRet;
}

BOOL CShowPerfLibDlg::BuildSubTree( CString strSpace, BYTE* pBlob, DWORD dwNumObjects)
{
	BOOL bRet = TRUE;

	HTREEITEM hSpace = m_wndPerfTree.InsertItem( strSpace );

// Object

	PERF_OBJECT_TYPE* pObj = (PERF_OBJECT_TYPE*)pBlob;

	for ( DWORD dwObj = 0; dwObj < dwNumObjects; dwObj++ )
	{
		CString str;

		char* szName;
		m_TitleLibrary.GetName(pObj->ObjectNameTitleIndex, &szName);

		char szClassName[256];
		sprintf(szClassName, "%s Class", szName );
		HTREEITEM hClass = m_wndPerfTree.InsertItem( szClassName, hSpace );

		str.Format("TotalByteLength: %d", pObj->TotalByteLength);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("DefinitionLength: %d", pObj->DefinitionLength);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("HeaderLength: %d", pObj->HeaderLength);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("ObjectNameTitleIndex: %d", pObj->ObjectNameTitleIndex);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("ObjectHelpTitleIndex: %d", pObj->ObjectHelpTitleIndex);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("DetailLevel: %d", pObj->DetailLevel);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("NumCounters: %d", pObj->NumCounters);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("DefaultCounter: %d", pObj->DefaultCounter);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("NumInstances: %d", pObj->NumInstances);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("CodePage: %d", pObj->CodePage);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("PerfTime: %d", pObj->PerfTime);
		m_wndPerfTree.InsertItem( str, hClass );
		str.Format("PerfFreq: %d", pObj->PerfFreq);
		m_wndPerfTree.InsertItem( str, hClass );

// Counters

		PERF_COUNTER_DEFINITION* pCtrDef = (PERF_COUNTER_DEFINITION*)((DWORD)pObj + pObj->HeaderLength);

		for ( DWORD dwCtr = 0; dwCtr < pObj->NumCounters; dwCtr++ )
		{
			char szCounterName[256];

			m_TitleLibrary.GetName( pCtrDef->CounterNameTitleIndex, &szName );
			sprintf(szCounterName, "%s Counter", szName);

			HTREEITEM hCtr = m_wndPerfTree.InsertItem( szCounterName, hClass );

			str.Format("ByteLength: %d", pCtrDef->ByteLength);
			m_wndPerfTree.InsertItem( str, hCtr );
			str.Format("CounterNameTitleIndex: %d", pCtrDef->CounterNameTitleIndex);
			m_wndPerfTree.InsertItem( str, hCtr );
			str.Format("CounterHelpTitleIndex: %d", pCtrDef->CounterHelpTitleIndex);
			m_wndPerfTree.InsertItem( str, hCtr );
			str.Format("DefaultScale: %d", pCtrDef->DefaultScale);
			m_wndPerfTree.InsertItem( str, hCtr );
			str.Format("DetailLevel: %d", pCtrDef->DetailLevel);
			m_wndPerfTree.InsertItem( str, hCtr );
			str.Format("CounterType: 0x%X (%s Base)", pCtrDef->CounterType, ( PERF_COUNTER_BASE == (pCtrDef->CounterType & 0x00070000))?"Is a ":"Is not a ");
			m_wndPerfTree.InsertItem( str, hCtr );
			str.Format("CounterSize: %d", pCtrDef->CounterSize);
			m_wndPerfTree.InsertItem( str, hCtr );
			str.Format("CounterOffset: %d", pCtrDef->CounterOffset);
			m_wndPerfTree.InsertItem( str, hCtr );

			pCtrDef = (PERF_COUNTER_DEFINITION*)((DWORD)pCtrDef + pCtrDef->ByteLength);
		}

// Instances

		PERF_INSTANCE_DEFINITION* pInstDef = (PERF_INSTANCE_DEFINITION*)((DWORD)pObj + pObj->DefinitionLength);

		for ( LONG lInstance = 0; lInstance < pObj->NumInstances; lInstance++ )
		{
			char szInstanceName[256];
			sprintf(szInstanceName, "%S Instance", (WCHAR*)((DWORD)pInstDef + pInstDef->NameOffset));
			HTREEITEM hInst = m_wndPerfTree.InsertItem( szInstanceName, hClass );

			str.Format("ByteLength: %d", pInstDef->ByteLength);
			m_wndPerfTree.InsertItem( str, hInst );
			str.Format("ParentObjectTitleIndex: %d", pInstDef->ParentObjectTitleIndex);
			m_wndPerfTree.InsertItem( str, hInst );
			str.Format("ParentObjectInstance: %d", pInstDef->ParentObjectInstance);
			m_wndPerfTree.InsertItem( str, hInst );
			str.Format("UniqueID: %d", pInstDef->UniqueID);
			m_wndPerfTree.InsertItem( str, hInst );
			str.Format("NameOffset: %d", pInstDef->NameOffset);
			m_wndPerfTree.InsertItem( str, hInst );
			str.Format("NameLength: %d", pInstDef->NameLength);
			m_wndPerfTree.InsertItem( str, hInst );

// Instance Counter Data

			PERF_COUNTER_BLOCK* pCtrBlock = (PERF_COUNTER_BLOCK*)((DWORD)pInstDef + pInstDef->ByteLength);
			BYTE* pCurrCtr = (BYTE*)pCtrBlock;

			PERF_COUNTER_DEFINITION* pCtrDef = (PERF_COUNTER_DEFINITION*)((DWORD)pObj + pObj->HeaderLength);

			for ( DWORD dwCtr = 0; dwCtr < pObj->NumCounters; dwCtr++ )
			{
				char*	szName;
				m_TitleLibrary.GetName( pCtrDef->CounterNameTitleIndex, &szName );

				switch (pCtrDef->CounterSize)
				{
				case 0:
					{
						str.Format("%s data: <zero length>", szName );
						m_wndPerfTree.InsertItem( str, hInst );
					}break;
				case 4:
					{
						str.Format("%s data: %d", szName, (DWORD)(*(pCurrCtr + pCtrDef->CounterOffset)));
						m_wndPerfTree.InsertItem( str, hInst );
					}break;
				case 8:
					{
						str.Format("%s data: %I64d", szName, (__int64)(*(pCurrCtr + pCtrDef->CounterOffset)));
						m_wndPerfTree.InsertItem( str, hInst );
					}break;

				default:
					{
						str.Format("%s data: <unhandled>", szName );
						m_wndPerfTree.InsertItem( str, hInst );
					}break;
				}

				pCtrDef = (PERF_COUNTER_DEFINITION*)((DWORD)pCtrDef + pCtrDef->ByteLength);
			}


			pInstDef = (PERF_INSTANCE_DEFINITION*)((DWORD)pCtrBlock + pCtrBlock->ByteLength);
		}

		pObj = (PERF_OBJECT_TYPE*)((DWORD)pObj + pObj->TotalByteLength);
	}

	return bRet;
}

BOOL CShowPerfLibDlg::CloseLibrary()
{
	BOOL bRet = TRUE;

	DWORD dwRet = m_pfnCloseProc();

/*	if ( ERROR_SUCCESS != dwRet )
		bRet = FALSE;

	Log( bRet?PERF_SUCCESS:PERF_FAILURE, "Closing Performance Library" );
*/
	return bRet;
}

void CShowPerfLibDlg::Log( DWORD dwState, CString strDesc )
{
	CString strState;
	int nIndex = -1;
	BOOL bErr;

	switch( dwState )
	{
	case PERF_SUCCESS:
		{
			strState = "Success";
		}break;
	case PERF_INFO:
		{
			strState = "Information";
		}break;
	case PERF_MORE_DATA:
		{
			strState = "More Data";
		}break;
	case PERF_WARNING:
		{
			strState = "Warning";
		}break;
	case PERF_FAILURE:
		{
			strState = "Failure";
		}break;
	default:
		{
			strState = "Unknown";
		}break;
	};

	nIndex = m_wndErrorLog.InsertItem( m_wndErrorLog.GetItemCount() , strState );
	bErr = m_wndErrorLog.SetItemText( nIndex, 1, strDesc );

}

void CShowPerfLibDlg::OnChangeRequest() 
{

}

void CShowPerfLibDlg::OnGetCounter() 
{
	UpdateData();

	Collect( m_strRequest );

	m_strRequest.Empty();
	UpdateData( FALSE );
}