// This is a part of the Active Template Library.
// Copyright (C) 1996-2001 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Active Template Library Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Active Template Library product.

#ifndef __ATLPERF_H__
#define __ATLPERF_H__

#pragma once

#ifndef __cplusplus
	#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#include <atlbase.h>
#include <atlcom.h>
#include <atlstr.h>
#include <atlfile.h>
#include <atlsync.h>
#include <winperf.h>
#include <atlcoll.h>

#ifndef _ATL_PERF_NOXML
#include <atlenc.h>
#include <oaidl.h>
#include <xmldomdid.h>
#include <xmldsodid.h>
#include <msxmldid.h>
#include <msxml.h>
#endif

namespace ATL
{

const DWORD ATLPERF_SIZE_MASK = 0x00000300;
const DWORD ATLPERF_TYPE_MASK = 0x00000C00;
const DWORD ATLPERF_TEXT_MASK = 0x00010000;

#ifndef ATLPERF_DEFAULT_MAXINSTNAMELENGTH
#define ATLPERF_DEFAULT_MAXINSTNAMELENGTH 64
#endif

// base class for user-defined perf objects
struct CPerfObject
{
	ULONG m_nAllocSize;
	DWORD m_dwObjectId;
	DWORD m_dwInstance;
	ULONG m_nRefCount;
	ULONG m_nInstanceNameOffset; // byte offset from beginning of PerfObject to LPWSTR szInstanceName
};

struct CPerfMapEntry
{
	DWORD m_dwPerfId;
	CString m_strName;
	CString m_strHelp;
	DWORD m_dwDetailLevel;
	BOOL m_bIsObject;

	// OBJECT INFO
	ULONG m_nNumCounters;
	LONG m_nDefaultCounter;
	LONG m_nInstanceLess; // PERF_NO_INSTANCES if instanceless
	
	// the size of the struct not counting the name and string counters
	ULONG m_nStructSize;
	
	 // in characters including the null terminator
	ULONG m_nMaxInstanceNameLen;

	ULONG m_nAllocSize;

	// COUNTER INFO
	DWORD m_dwCounterType;
	LONG m_nDefaultScale;

	// the maximum size of the string counter data in characters, including the null terminator
	// ignored if not a string counter
	ULONG m_nMaxCounterSize;

	ULONG m_nDataOffset;

	// the ids that correspond to the name and help strings stored in the registry
	UINT m_nNameId;
	UINT m_nHelpId;
};

class CPerfMon
{
public:
	~CPerfMon() throw();

	// PerfMon entry point helpers
	DWORD Open(LPWSTR lpDeviceNames) throw();
	DWORD Collect(LPWSTR lpwszValue, LPVOID* lppData, LPDWORD lpcbBytes, LPDWORD lpcObjectTypes) throw();
	DWORD Close() throw();

#ifdef _ATL_PERF_REGISTER
	// registration
	HRESULT Register(
		LPCTSTR szOpenFunc,
		LPCTSTR szCollectFunc,
		LPCTSTR szCloseFunc,
		HINSTANCE hDllInstance = _AtlBaseModule.GetModuleInstance()) throw();
	HRESULT RegisterStrings(
		LANGID wLanguage = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
		HINSTANCE hResInstance = _AtlBaseModule.GetResourceInstance()) throw();
	HRESULT RegisterAllStrings(HINSTANCE hResInstance = NULL) throw();
	HRESULT Unregister() throw();

	static BOOL CALLBACK EnumResLangProc(HINSTANCE hModule, LPCTSTR szType, LPCTSTR szName, LANGID wIDLanguage, LPARAM lParam);
#endif

	HRESULT Initialize() throw();
	void UnInitialize() throw();
	HRESULT CreateInstance(
		DWORD dwObjectId,
		DWORD dwInstance,
		LPCWSTR szInstanceName,
		CPerfObject** ppInstance) throw();
	HRESULT CreateInstanceByName(
		DWORD dwObjectId,
		LPCWSTR szInstanceName,
		CPerfObject** ppInstance) throw();

	template <class T>
	HRESULT CreateInstance(
		DWORD dwInstance,
		LPCWSTR szInstanceName,
		T** ppInstance) throw()
	{
		// Ensure T derives from CPerfObject
		static_cast<CPerfObject*>(*ppInstance);
		
		return CreateInstance(
			T::kObjectId,
			dwInstance,
			szInstanceName,
			reinterpret_cast<CPerfObject**>(ppInstance)
			);
	}

	template <class T>
	HRESULT CreateInstanceByName(
		LPCWSTR szInstanceName,
		T** ppInstance) throw()
	{
		// Ensure T derives from CPerfObject
		static_cast<CPerfObject*>(*ppInstance);
		
		return CreateInstanceByName(
			T::kObjectId,
			szInstanceName,
			reinterpret_cast<CPerfObject**>(ppInstance)
			);
	}

	HRESULT ReleaseInstance(CPerfObject* pInstance) throw();
	HRESULT LockPerf(DWORD dwTimeout = INFINITE) throw();
	void UnlockPerf() throw();

	// map building routines
	HRESULT AddObjectDefinition(
		DWORD dwObjectId,
		LPCTSTR szObjectName,
		LPCTSTR szHelpString,
		DWORD dwDetailLevel,
		INT nDefaultCounter,
		BOOL bInstanceLess,
		UINT nStructSize,
		UINT nMaxInstanceNameLen = ATLPERF_DEFAULT_MAXINSTNAMELENGTH) throw();
	HRESULT AddCounterDefinition(
		DWORD dwCounterId,
		LPCTSTR szCounterName,
		LPCTSTR szHelpString,
		DWORD dwDetailLevel,
		DWORD dwCounterType,
		ULONG nMaxCounterSize,
		UINT nOffset,
		INT nDefaultScale) throw();
	void ClearMap() throw();

#ifndef _ATL_PERF_NOXML
	HRESULT PersistToXML(IStream *pStream, BOOL bFirst=TRUE, BOOL bLast=TRUE) throw(...);
	HRESULT LoadFromXML(IStream *pStream) throw(...);
#endif

protected:
	virtual LPCTSTR GetAppName() const throw() = 0;
	virtual HRESULT CreateMap(WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes = NULL) throw();
	virtual void OnBlockAlloc(CAtlFileMappingBase* /*pNewBlock*/) { }

	// implementation helpers
	LPBYTE _AllocData(LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, size_t nBytesNeeded) throw();
	template<typename T> T* _AllocStruct(LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, T*) throw()
	{
		return reinterpret_cast<T*>(_AllocData(pData, nBytesAvail, pnBytesUsed, sizeof(T)));
	}

	CPerfMapEntry& _GetMapEntry(UINT nIndex) throw();
	UINT _GetNumMapEntries() throw();
	CPerfObject* _GetFirstObject(CAtlFileMappingBase* pBlock) throw();
	CPerfObject* _GetNextObject(CPerfObject* pInstance) throw();
	CAtlFileMappingBase* _GetNextBlock(CAtlFileMappingBase* pBlock) throw();
	CAtlFileMappingBase* _AllocNewBlock(CAtlFileMappingBase* pPrev, BOOL* pbExisted = NULL) throw();
	DWORD& _GetBlockId(CAtlFileMappingBase* pBlock) throw();
	CPerfMapEntry* _FindObjectInfo(DWORD dwObjectId) throw();
	CPerfMapEntry* _FindCounterInfo(CPerfMapEntry* pObjectEntry, DWORD dwCounterId) throw();
	CPerfMapEntry* _FindCounterInfo(DWORD dwObjectId, DWORD dwCounterId) throw();
	BOOL _WantObjectType(LPWSTR lpwszValue, DWORD dwPerfId) throw(...);
	void _FillObjectType(PERF_OBJECT_TYPE* pObjectType, CPerfMapEntry* pObjectEntry) throw();
	void _FillCounterDef(
		PERF_COUNTER_DEFINITION* pCounterDef,
		CPerfMapEntry* pCounterEntry,
		ULONG& nCBSize) throw();
	HRESULT _CollectObjectType(
		CPerfMapEntry* pObjectEntry,
		LPBYTE pData,
		ULONG nBytesAvail,
		ULONG* pnBytesUsed) throw();
	HRESULT _LoadMap() throw();
	HRESULT _SaveMap() throw();
	HRESULT _GetAttribute(
		IXMLDOMNode *pNode, 
		LPCWSTR szAttrName, 
		BSTR *pbstrVal) throw();
	HRESULT CPerfMon::_CreateInstance(
		DWORD dwObjectId,
		DWORD dwInstance,
		LPCWSTR szInstanceName,
		CPerfObject** ppInstance,
		bool bByName) throw();

#ifdef _ATL_PERF_REGISTER
	void _AppendStrings(
		LPTSTR& pszNew,
		CAtlArray<CString>& astrStrings,
		ULONG iFirstIndex
		) throw();
	HRESULT _AppendRegStrings(
		CRegKey& rkLang,
		LPCTSTR szValue,
		CAtlArray<CString>& astrStrings,
		ULONG nNewStringSize,
		ULONG iFirstIndex,
		ULONG iLastIndex) throw();
	HRESULT _RemoveRegStrings(
		CRegKey& rkLang,
		LPCTSTR szValue,
		ULONG iFirstIndex,
		ULONG iLastIndex) throw();
	HRESULT _ReserveStringRange(DWORD& dwFirstCounter, DWORD& dwFirstHelp) throw();
	HRESULT _UnregisterStrings() throw();
	HRESULT _RegisterAllStrings(UINT nRes, HINSTANCE hResInstance) throw();
#endif
private:
	CAtlArray<CPerfMapEntry> m_map;
	CAutoPtrArray<CAtlFileMappingBase> m_aMem;
	CMutex m_lock;
	ULONG m_nAllocSize;
	ULONG m_nHeaderSize;
	ULONG m_nSchemaSize;
	ULONG m_nNumObjectTypes;
};

class CPerfLock
{
public:
	CPerfLock(CPerfMon* pPerfMon, DWORD dwTimeout = INFINITE) throw()
	{
		ATLASSERT(pPerfMon != NULL);
		m_pPerfMon = pPerfMon;
		m_hrStatus = m_pPerfMon->LockPerf(dwTimeout);
	}

	~CPerfLock() throw()
	{
		if (SUCCEEDED(m_hrStatus))
			m_pPerfMon->UnlockPerf();
	}

	HRESULT GetStatus() const throw()
	{
		return m_hrStatus;
	}

private:
	CPerfMon* m_pPerfMon;
	HRESULT m_hrStatus;
};

// empty definition just for ease of use with code wizards, etc.
#define BEGIN_PERFREG_MAP()

// empty definition just for ease of use with code wizards, etc.
#define END_PERFREG_MAP()

#if !defined(_ATL_PERF_REGISTER) | defined(_ATL_PERF_NOEXPORT)
#define PERFREG_ENTRY(className)
#endif

#ifdef _ATL_PERF_REGISTER
#define BEGIN_PERF_MAP(AppName) \
	private: \
		LPCTSTR GetAppName() const throw() { return AppName; } \
		HRESULT CreateMap(WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes = NULL) throw() \
		{ \
			CPerfMon* pPerf = this; \
			wLanguage; \
			hResInstance; \
			if (pSampleRes) \
				*pSampleRes = 0; \
			CString strName; \
			CString strHelp; \
			HRESULT hr; \
			ClearMap();

#define BEGIN_COUNTER_MAP(objectclass) \
	public: \
		typedef objectclass _PerfCounterClass; \
		static HRESULT CreateMap(CPerfMon* pPerf, WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes) throw() \
		{ \
			wLanguage; \
			hResInstance; \
			pSampleRes; \
			CString strName; \
			CString strHelp; \
			HRESULT hr; \
			hr = RegisterObject(pPerf, wLanguage, hResInstance, pSampleRes); \
			if (FAILED(hr)) \
				return hr;

#define DECLARE_PERF_OBJECT_EX(dwObjectId, namestring, helpstring, detail, instanceless, structsize, maxinstnamelen, defcounter) \
		static HRESULT RegisterObject(CPerfMon* pPerf, WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes) throw() \
		{ \
			CString strName; \
			CString strHelp; \
			HRESULT hr; \
			_ATLTRY \
			{ \
				if (IS_INTRESOURCE(namestring)) \
				{ \
					ATLASSERT(IS_INTRESOURCE(helpstring)); \
					if (pSampleRes) \
						*pSampleRes = (UINT) (UINT_PTR) namestring; \
					if (hResInstance && !strName.LoadString(hResInstance, (UINT) (UINT_PTR) namestring, wLanguage)) \
						return E_FAIL; \
					if (hResInstance && !strHelp.LoadString(hResInstance, (UINT) (UINT_PTR) helpstring, wLanguage)) \
						return E_FAIL; \
				} \
				else \
				{ \
					ATLASSERT(!IS_INTRESOURCE(helpstring)); \
					strName = (LPCTSTR) namestring; \
					strHelp = (LPCTSTR) helpstring; \
				} \
			} \
			_ATLCATCHALL() \
			{ \
				return E_FAIL; \
			} \
			hr = pPerf->AddObjectDefinition(dwObjectId, strName, strHelp, detail, defcounter, instanceless, (ULONG) structsize, maxinstnamelen); \
			if (FAILED(hr)) \
				return hr; \
			return S_OK; \
		} \
		/* NOTE: put a semicolon after your call to DECLARE_PERF_OBJECT*(...) */ \
		/* this is needed for the code wizards to parse things properly */ \
		static const DWORD kObjectId = dwObjectId

#define CHAIN_PERF_OBJECT(objectclass) \
			hr = objectclass::CreateMap(pPerf, wLanguage, hResInstance, pSampleRes); \
			if (FAILED(hr)) \
				return hr;

// CAssertValidField ensures that the member variable that's being passed to
// DEFINE_COUNTER[_EX] is the proper type. only 32-bit integral types can be used with
// PERF_SIZE_DWORD and only 64-bit integral types can be used with PERF_SIZE_LARGE
template< DWORD t_dwSize >
class CAssertValidField
{
};

template<>
class CAssertValidField< PERF_SIZE_DWORD >
{
public:
	template< class C > static void AssertValidFieldType( ULONG C::* ) throw() { }
	template< class C > static void AssertValidFieldType( LONG C::* ) throw() { }
};

template<>
class CAssertValidField< PERF_SIZE_LARGE >
{
public:
	template< class C > static void AssertValidFieldType( ULONGLONG C::* p ) throw() { }
	template< class C > static void AssertValidFieldType( LONGLONG C::* p ) throw() { }
};

#define DEFINE_COUNTER_EX(member, dwCounterId, namestring, helpstring, detail, countertype, maxcountersize, defscale) \
			CAssertValidField< (countertype) & ATLPERF_SIZE_MASK >::AssertValidFieldType( &_PerfCounterClass::member ); \
			_ATLTRY \
			{ \
				if (IS_INTRESOURCE(namestring)) \
				{ \
					ATLASSERT(IS_INTRESOURCE(helpstring)); \
					if (hResInstance && !strName.LoadString(hResInstance, (UINT) (UINT_PTR) namestring, wLanguage)) \
						return E_FAIL; \
					if (hResInstance && !strHelp.LoadString(hResInstance, (UINT) (UINT_PTR) helpstring, wLanguage)) \
						return E_FAIL; \
				} \
				else \
				{ \
					ATLASSERT(!IS_INTRESOURCE(helpstring)); \
					strName = (LPCTSTR) namestring; \
					strHelp = (LPCTSTR) helpstring; \
				} \
			} \
			_ATLCATCHALL() \
			{ \
				return E_FAIL; \
			} \
			hr = pPerf->AddCounterDefinition(dwCounterId, strName, strHelp, detail, countertype, maxcountersize, (ULONG) offsetof(_PerfCounterClass, member), defscale); \
			if (FAILED(hr)) \
				return hr;

#define END_PERF_MAP() \
			return S_OK; \
		}

#define END_COUNTER_MAP() \
			return S_OK; \
		}

// define _ATL_PERF_NOEXPORT if you don't want to use the PERFREG map and don't want these
// functions exported from your DLL
#ifndef _ATL_PERF_NOEXPORT

// Perf register map stuff
// this is for ease of integration with the module attribute and for the 
// perfmon wizard

#pragma data_seg(push)
#pragma data_seg("ATLP$A")
__declspec(selectany) CPerfMon * __pperfA = NULL;
#pragma data_seg("ATLP$Z") 
__declspec(selectany) CPerfMon * __pperfZ = NULL;
#pragma data_seg("ATLP$C")
#pragma data_seg(pop)

ATL_NOINLINE inline HRESULT RegisterPerfMon(HINSTANCE hDllInstance = _AtlBaseModule.GetModuleInstance()) throw() 
{
	CPerfMon **ppPerf = &__pperfA; 
	HRESULT hr = S_OK; 
	while (ppPerf != &__pperfZ) 
	{ 
		if (*ppPerf != NULL) 
		{ 
			hr = (*ppPerf)->Register(_T("_OpenPerfMon"), _T("_CollectPerfMon"), _T("_ClosePerfMon"), hDllInstance);
			if (FAILED(hr)) 
				return hr; 
			hr = (*ppPerf)->RegisterAllStrings(hDllInstance);
			if (FAILED(hr)) 
				return hr; 
		} 
		ppPerf++; 
	} 
	return S_OK; 
} 

ATL_NOINLINE inline HRESULT UnregisterPerfMon() throw() 
{ 
	CPerfMon **ppPerf = &__pperfA; 
	HRESULT hr = S_OK; 
	while (ppPerf != &__pperfZ) 
	{ 
		if (*ppPerf != NULL) 
		{ 
			hr = (*ppPerf)->Unregister(); 
			if (FAILED(hr)) 
				return hr; 
		} 
		ppPerf++; 
	} 
	return S_OK; 
} 

extern "C" ATL_NOINLINE inline DWORD __declspec(dllexport) WINAPI OpenPerfMon(LPWSTR lpDeviceNames) throw()
{
	CPerfMon **ppPerf = &__pperfA;
	DWORD dwErr = 0;
	while (ppPerf != &__pperfZ)
	{
		if (*ppPerf != NULL)
		{
			dwErr = (*ppPerf)->Open(lpDeviceNames);
			if (dwErr != 0)
				return dwErr;
		}
		ppPerf++;
	}
	return 0;
}

extern "C" ATL_NOINLINE inline DWORD __declspec(dllexport) WINAPI CollectPerfMon(LPWSTR lpwszValue, LPVOID* lppData,
	LPDWORD lpcbBytes, LPDWORD lpcObjectTypes) throw()
{
	DWORD dwOrigBytes = *lpcbBytes;
	DWORD dwBytesRemaining = *lpcbBytes;
	CPerfMon **ppPerf = &__pperfA;
	DWORD dwErr = 0;
	while (ppPerf != &__pperfZ)
	{
		if (*ppPerf != NULL)
		{
			dwErr = (*ppPerf)->Collect(lpwszValue, lppData, lpcbBytes, lpcObjectTypes);
			if (dwErr != 0)
				return dwErr;
			dwBytesRemaining -= *lpcbBytes;
			*lpcbBytes = dwBytesRemaining;
		}
		ppPerf++;
	}
	*lpcbBytes = dwOrigBytes - dwBytesRemaining;
	return 0;
}

extern "C" ATL_NOINLINE inline DWORD __declspec(dllexport) WINAPI ClosePerfMon() throw()
{
	CPerfMon **ppPerf = &__pperfA;
	while (ppPerf != &__pperfZ)
	{
		if (*ppPerf != NULL)
		{
			(*ppPerf)->Close();
		}
		ppPerf++;
	}
	return 0;
}

// this class handles integrating the registration with CComModule
class _CAtlPerfSetFuncPtr
{
public:
	_CAtlPerfSetFuncPtr()
	{
		_pPerfRegFunc = RegisterPerfMon;
		_pPerfUnRegFunc = UnregisterPerfMon;
	}
};

extern "C" { __declspec(selectany) _CAtlPerfSetFuncPtr g_atlperfinit; }
#pragma comment(linker, "/INCLUDE:_g_atlperfinit")

#if defined(_M_IX86)
#define PERF_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:___pperf_" #class))
#elif defined(_M_IA64)
#define PERF_ENTRY_PRAGMA(class) __pragma(comment(linker, "/include:__pperf_" #class))
#else
#error Unknown Platform. define PERF_ENTRY_PRAGMA
#endif

#define PERFREG_ENTRY(className) \
	className __perf_##className; \
	extern "C" __declspec(allocate("ATLP$C")) CPerfMon * __pperf_##className = \
		static_cast<CPerfMon*>(&__perf_##className); \
	PERF_ENTRY_PRAGMA(className)

#endif // _ATL_PERF_NOEXPORT

#else // _ATL_PERF_REGISTER

#define BEGIN_PERF_MAP(AppName) \
	private: \
		LPCTSTR GetAppName() const throw() { return AppName; }
#define BEGIN_COUNTER_MAP(objectclass)

#define DECLARE_PERF_OBJECT_EX(dwObjectId, namestring, helpstring, detail, instanceless, structsize, maxinstnamelen, defcounter) \
		/* NOTE: put a semicolon after your call to DECLARE_PERF_OBJECT*(...) */ \
		/* this is needed for the code wizards to parse things properly */ \
		static const DWORD kObjectId = dwObjectId
#define CHAIN_PERF_OBJECT(objectclass)
#define DEFINE_COUNTER_EX(member, dwCounterId, namestring, helpstring, detail, countertype, maxcountersize, defscale)

#define END_PERF_MAP()
#define END_COUNTER_MAP()

#endif // _ATL_PERF_REGISTER

#define DECLARE_PERF_OBJECT(objectclass, dwObjectId, namestring, helpstring, defcounter) \
	DECLARE_PERF_OBJECT_EX(dwObjectId, namestring, helpstring, PERF_DETAIL_NOVICE, 0, sizeof(objectclass), ATLPERF_DEFAULT_MAXINSTNAMELENGTH, defcounter)
#define DECLARE_PERF_OBJECT_NO_INSTANCES(objectclass, dwObjectId, namestring, helpstring, defcounter) \
	DECLARE_PERF_OBJECT_EX(dwObjectId, namestring, helpstring, PERF_DETAIL_NOVICE, PERF_NO_INSTANCES, sizeof(objectclass), 0, defcounter)

#define DEFINE_COUNTER(member, namestring, helpstring, countertype, defscale) \
	DEFINE_COUNTER_EX(member, 0, namestring, helpstring, PERF_DETAIL_NOVICE, countertype, 0, defscale)

} // namespace ATL

#include <atlperf.inl>
#endif // __ATLPERF_H__