Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2202 lines
57 KiB

// 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_INL__
#define __ATLPERF_INL__
#pragma once
#ifndef __ATLPERF_H__
#error atlperf.inl requires atlperf.h to be included first
#endif
#include <atlsecurity.h>
#pragma warning(push)
#ifndef _CPPUNWIND
#pragma warning(disable: 4702) // unreachable code
#endif
namespace ATL
{
__declspec(selectany) LPCTSTR c_szAtlPerfCounter = _T("Counter");
__declspec(selectany) LPCTSTR c_szAtlPerfFirstCounter = _T("First Counter");
__declspec(selectany) LPCTSTR c_szAtlPerfLastCounter = _T("Last Counter");
__declspec(selectany) LPCTSTR c_szAtlPerfHelp = _T("Help");
__declspec(selectany) LPCTSTR c_szAtlPerfFirstHelp = _T("First Help");
__declspec(selectany) LPCTSTR c_szAtlPerfLastHelp = _T("Last Help");
__declspec(selectany) LPCWSTR c_szAtlPerfGlobal = L"Global";
__declspec(selectany) LPCTSTR c_szAtlPerfLibrary = _T("Library");
__declspec(selectany) LPCTSTR c_szAtlPerfOpen = _T("Open");
__declspec(selectany) LPCTSTR c_szAtlPerfCollect = _T("Collect");
__declspec(selectany) LPCTSTR c_szAtlPerfClose = _T("Close");
__declspec(selectany) LPCTSTR c_szAtlPerfLanguages = _T("Languages");
__declspec(selectany) LPCTSTR c_szAtlPerfMap = _T("Map");
__declspec(selectany) LPCTSTR c_szAtlPerfServicesKey = _T("SYSTEM\\CurrentControlSet\\Services");
__declspec(selectany) LPCTSTR c_szAtlPerfPerformanceKey = _T("SYSTEM\\CurrentControlSet\\Services\\%s\\Performance");
__declspec(selectany) LPCTSTR c_szAtlPerfPerfLibKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
__declspec(selectany) LPCTSTR c_szAtlPerfPerfLibLangKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\%3.3x");
inline CPerfMon::~CPerfMon() throw()
{
UnInitialize();
}
inline HRESULT CPerfMon::CreateMap(LANGID language, HINSTANCE hResInstance, UINT* pSampleRes) throw()
{
language; // unused
hResInstance; // unused
pSampleRes; // unused
return S_OK;
}
inline CPerfMapEntry& CPerfMon::_GetMapEntry(UINT nIndex) throw()
{
ATLASSERT(nIndex < _GetNumMapEntries());
return m_map[nIndex];
}
inline UINT CPerfMon::_GetNumMapEntries() throw()
{
return (UINT) m_map.GetCount();
}
inline CPerfObject* CPerfMon::_GetFirstObject(CAtlFileMappingBase* pBlock) throw()
{
ATLASSERT(pBlock != NULL);
// should never happen if Initialize succeeded
// are you checking return codes?
ATLASSERT(pBlock->GetData() != NULL);
return reinterpret_cast<CPerfObject*>(LPBYTE(pBlock->GetData()) + m_nHeaderSize);
}
inline CPerfObject* CPerfMon::_GetNextObject(CPerfObject* pInstance) throw()
{
ATLASSERT(pInstance != NULL);
return reinterpret_cast<CPerfObject*>(LPBYTE(pInstance) + pInstance->m_nAllocSize);
}
inline CAtlFileMappingBase* CPerfMon::_GetNextBlock(CAtlFileMappingBase* pBlock) throw()
{
// calling _GetNextBlock(NULL) will return the first block
DWORD dwNextBlockIndex = 0;
if (pBlock)
{
dwNextBlockIndex= _GetBlockId(pBlock) +1;
if (DWORD(m_aMem.GetCount()) == dwNextBlockIndex)
return NULL;
}
return m_aMem[dwNextBlockIndex];
}
inline CAtlFileMappingBase* CPerfMon::_AllocNewBlock(CAtlFileMappingBase* pPrev, BOOL* pbExisted /* == NULL */) throw()
{
// initialize a security descriptor to give everyone access to objects we create
CSecurityDescriptor sd;
sd.InitializeFromThreadToken();
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), sd, FALSE };
CAutoPtr<CAtlFileMappingBase> spMem;
CAtlFileMappingBase* pMem = NULL;
ATLTRY(spMem.Attach(new CAtlFileMappingBase));
if (spMem == NULL)
return NULL;
// create a unique name for the shared mem segment based on the index
DWORD dwNextBlockIndex;
if (pPrev != NULL)
dwNextBlockIndex = _GetBlockId(pPrev) +1;
else
{
// use the system allocation granularity (65536 currently. may be different in the future)
SYSTEM_INFO si;
GetSystemInfo(&si);
m_nAllocSize = si.dwAllocationGranularity;
dwNextBlockIndex = 0;
}
BOOL bExisted = FALSE;
_ATLTRY
{
CString strName;
strName.Format(_T("ATLPERF_%s_%3.3d"), GetAppName(), dwNextBlockIndex);
HRESULT hr = spMem->MapSharedMem(m_nAllocSize, strName, &bExisted, &sa);
if (FAILED(hr))
return NULL;
// save the index of this block
// don't for first block since we don't know m_nSchemaSize yet
if (dwNextBlockIndex)
_GetBlockId(spMem) = dwNextBlockIndex;
if (!bExisted)
memset(spMem->GetData(), 0, m_nAllocSize);
if (pbExisted)
*pbExisted = bExisted;
pMem = spMem;
m_aMem.Add(spMem);
OnBlockAlloc(pMem);
}
_ATLCATCHALL()
{
return NULL;
}
return pMem;
}
inline HRESULT CPerfMon::_LoadMap() throw()
{
_ATLTRY
{
HRESULT hr;
ClearMap();
DWORD* pData = LPDWORD(m_aMem[0]->GetData());
DWORD dwDataSize = *pData++; // blob size
DWORD dwNumItems = *pData++; // number of items
// see if we have name data
DWORD* pNameData = NULL;
if (dwDataSize > (2+dwNumItems*9) * sizeof(DWORD))
pNameData = pData + dwNumItems*9; // blob size and item count already skipped. skip item data
for (DWORD i=0; i<dwNumItems; i++)
{
DWORD dwIsObject = *pData++;
DWORD dwPerfId = *pData++;
DWORD dwDetailLevel = *pData++;
CString strName;
if (pNameData)
{
strName = CString(LPWSTR(pNameData+1), *pNameData);
pNameData += AtlAlignUp(sizeof(WCHAR) * *pNameData, sizeof(DWORD))/sizeof(DWORD) + 1;
}
if (dwIsObject)
{
DWORD dwDefaultCounter = *pData++;
DWORD dwInstanceLess = *pData++;
DWORD dwStructSize = *pData++;
DWORD dwMaxInstanceNameLen = *pData++;
hr = AddObjectDefinition(
dwPerfId,
strName,
NULL,
dwDetailLevel,
dwDefaultCounter,
dwInstanceLess,
dwStructSize,
dwMaxInstanceNameLen);
if (FAILED(hr))
{
ClearMap();
return hr;
}
}
else
{
DWORD dwCounterType = *pData++;
DWORD dwMaxCounterSize = *pData++;
DWORD dwDataOffset = *pData++;
DWORD dwDefaultScale = *pData++;
hr = AddCounterDefinition(
dwPerfId,
strName,
NULL,
dwDetailLevel,
dwCounterType,
dwMaxCounterSize,
dwDataOffset,
dwDefaultScale);
if (FAILED(hr))
{
ClearMap();
return hr;
}
}
DWORD dwNameId = *pData++;
DWORD dwHelpId = *pData++;
CPerfMapEntry& entry = _GetMapEntry(_GetNumMapEntries()-1);
entry.m_nNameId = dwNameId;
entry.m_nHelpId = dwHelpId;
}
return S_OK;
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
}
inline HRESULT CPerfMon::_SaveMap() throw()
{
_ATLTRY
{
// figure out how much memory we need
size_t nSize = (2 + 9*_GetNumMapEntries()) * sizeof(DWORD);
for (UINT i=0; i<_GetNumMapEntries(); i++)
{
// if any of the entries have names, they'd better all have names
CPerfMapEntry& entry = _GetMapEntry(i);
if (!entry.m_strName.IsEmpty())
nSize += sizeof(DWORD) + AtlAlignUp(sizeof(WCHAR) * entry.m_strName.GetLength(), sizeof(DWORD));
}
CHeapPtr<BYTE> blob;
if (!blob.Allocate(nSize))
return E_OUTOFMEMORY;
// start with blob size and number of items in the blob
DWORD* pCurrent = reinterpret_cast<DWORD*>(blob.m_pData);
*pCurrent++ = (DWORD) nSize; // blob size
*pCurrent++ = _GetNumMapEntries(); // number of items
for (UINT i=0; i<_GetNumMapEntries(); i++)
{
// add all the relevant runtime info to the blob for each item
CPerfMapEntry& entry = _GetMapEntry(i);
*pCurrent++ = entry.m_bIsObject;
*pCurrent++ = entry.m_dwPerfId;
*pCurrent++ = entry.m_dwDetailLevel;
if (entry.m_bIsObject)
{
*pCurrent++ = entry.m_nDefaultCounter;
*pCurrent++ = entry.m_nInstanceLess;
*pCurrent++ = entry.m_nStructSize;
*pCurrent++ = entry.m_nMaxInstanceNameLen;
}
else
{
*pCurrent++ = entry.m_dwCounterType;
*pCurrent++ = entry.m_nMaxCounterSize;
*pCurrent++ = entry.m_nDataOffset;
*pCurrent++ = entry.m_nDefaultScale;
}
*pCurrent++ = entry.m_nNameId;
*pCurrent++ = entry.m_nHelpId;
}
// add names to the blob
for (UINT i=0; i<_GetNumMapEntries(); i++)
{
// if any of the entries have names, they'd better all have names
CPerfMapEntry& entry = _GetMapEntry(i);
if (!entry.m_strName.IsEmpty())
{
// copy the len of the string (in characters) then the wide-char version of the string
// pad the string to a dword boundary
int nLen = entry.m_strName.GetLength();
*pCurrent++ = nLen;
memcpy(pCurrent, CT2CW(entry.m_strName), sizeof(WCHAR)*nLen);
pCurrent += AtlAlignUp(sizeof(WCHAR) * nLen, sizeof(DWORD))/sizeof(DWORD);
}
}
CRegKey rkApp;
CString str;
DWORD dwErr;
str.Format(c_szAtlPerfPerformanceKey, GetAppName());
dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, str);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
rkApp.SetBinaryValue(c_szAtlPerfMap, blob, *LPDWORD(blob.m_pData));
return S_OK;
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
}
inline CPerfMapEntry* CPerfMon::_FindObjectInfo(DWORD dwObjectId) throw()
{
for (UINT i=0; i<_GetNumMapEntries(); i += _GetMapEntry(i).m_nNumCounters+1)
{
CPerfMapEntry& object = _GetMapEntry(i);
if (object.m_dwPerfId == dwObjectId)
return &object;
}
return NULL;
}
inline CPerfMapEntry* CPerfMon::_FindCounterInfo(CPerfMapEntry* pObjectEntry, DWORD dwCounterId) throw()
{
ATLASSERT(pObjectEntry != NULL);
for (DWORD i=0; i<pObjectEntry->m_nNumCounters; i++)
{
CPerfMapEntry* pCounter = pObjectEntry+i+1;
if (pCounter->m_dwPerfId == dwCounterId)
return pCounter;
}
return NULL;
}
inline CPerfMapEntry* CPerfMon::_FindCounterInfo(DWORD dwObjectId, DWORD dwCounterId) throw()
{
CPerfMapEntry* pObjectEntry = _FindObjectInfo(dwObjectId);
if (pObjectEntry != NULL)
return _FindCounterInfo(pObjectEntry, dwCounterId);
return NULL;
}
inline BOOL CPerfMon::_WantObjectType(LPWSTR szValue, DWORD dwObjectId) throw(...)
{
ATLASSERT(szValue != NULL);
if (lstrcmpiW(c_szAtlPerfGlobal, szValue) == 0)
return TRUE;
CString strList(szValue);
int nStart = 0;
CString strNum = strList.Tokenize(_T(" "), nStart);
while (!strNum.IsEmpty())
{
if (_ttoi(strNum) == int(dwObjectId))
return TRUE;
strNum = strList.Tokenize(_T(" "), nStart);
}
return FALSE;
}
inline LPBYTE CPerfMon::_AllocData(LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, size_t nBytesNeeded) throw()
{
ATLASSERT(pnBytesUsed != NULL);
if (nBytesAvail < *pnBytesUsed + (ULONG) nBytesNeeded)
return NULL;
LPBYTE p = pData;
pData += nBytesNeeded;
*pnBytesUsed += (ULONG) nBytesNeeded;
return p;
}
inline DWORD& CPerfMon::_GetBlockId(CAtlFileMappingBase* pBlock) throw()
{
ATLASSERT(pBlock != NULL);
return *LPDWORD(LPBYTE(pBlock->GetData()) + m_nSchemaSize);
}
inline void CPerfMon::_FillObjectType(PERF_OBJECT_TYPE* pObjectType, CPerfMapEntry* pObjectEntry) throw()
{
ATLASSERT(pObjectType != NULL);
ATLASSERT(pObjectEntry != NULL);
pObjectType->DefinitionLength = sizeof(PERF_OBJECT_TYPE) + sizeof(PERF_COUNTER_DEFINITION) * pObjectEntry->m_nNumCounters;
pObjectType->TotalByteLength = pObjectType->DefinitionLength; // we will add the instance definitions/counter blocks as we go
pObjectType->HeaderLength = sizeof(PERF_OBJECT_TYPE);
pObjectType->ObjectNameTitleIndex = pObjectEntry->m_nNameId;
pObjectType->ObjectNameTitle = NULL;
pObjectType->ObjectHelpTitleIndex = pObjectEntry->m_nHelpId;
pObjectType->ObjectHelpTitle = NULL;
pObjectType->DetailLevel = pObjectEntry->m_dwDetailLevel;
pObjectType->NumCounters = pObjectEntry->m_nNumCounters;
pObjectType->DefaultCounter = pObjectEntry->m_nDefaultCounter;
if (pObjectEntry->m_nInstanceLess == PERF_NO_INSTANCES)
pObjectType->NumInstances = PERF_NO_INSTANCES;
else
pObjectType->NumInstances = 0; // this will be calculated as we go
pObjectType->CodePage = 0;
pObjectType->PerfTime.QuadPart = 0;
pObjectType->PerfFreq.QuadPart = 0;
}
inline void CPerfMon::_FillCounterDef(
PERF_COUNTER_DEFINITION* pCounterDef,
CPerfMapEntry* pCounterEntry,
ULONG& nCBSize
) throw()
{
ATLASSERT(pCounterDef != NULL);
ATLASSERT(pCounterEntry != NULL);
pCounterDef->ByteLength = sizeof(PERF_COUNTER_DEFINITION);
pCounterDef->CounterNameTitleIndex = pCounterEntry->m_nNameId;
pCounterDef->CounterNameTitle = NULL;
pCounterDef->CounterHelpTitleIndex = pCounterEntry->m_nHelpId;
pCounterDef->CounterHelpTitle = NULL;
pCounterDef->DefaultScale = pCounterEntry->m_nDefaultScale;
pCounterDef->DetailLevel = pCounterEntry->m_dwDetailLevel;
pCounterDef->CounterType = pCounterEntry->m_dwCounterType;
switch (pCounterEntry->m_dwCounterType & ATLPERF_SIZE_MASK)
{
case PERF_SIZE_DWORD:
pCounterDef->CounterSize = sizeof(DWORD);
break;
case PERF_SIZE_LARGE:
pCounterDef->CounterSize = sizeof(__int64);
break;
case PERF_SIZE_ZERO:
pCounterDef->CounterSize = 0;
break;
case PERF_SIZE_VARIABLE_LEN:
ATLASSERT((pCounterEntry->m_dwCounterType & ATLPERF_TYPE_MASK) == PERF_TYPE_TEXT);
if ((pCounterEntry->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE)
pCounterDef->CounterSize = (DWORD) AtlAlignUp(pCounterEntry->m_nMaxCounterSize * sizeof(WCHAR), sizeof(DWORD));
else
pCounterDef->CounterSize = (DWORD) AtlAlignUp(pCounterEntry->m_nMaxCounterSize * sizeof(char), sizeof(DWORD));
break;
}
pCounterDef->CounterOffset = sizeof(PERF_COUNTER_BLOCK) + nCBSize;
nCBSize += pCounterDef->CounterSize;
}
inline HRESULT CPerfMon::_CollectObjectType(
CPerfMapEntry* pObjectEntry,
LPBYTE pData,
ULONG nBytesAvail,
ULONG* pnBytesUsed
) throw()
{
ATLASSERT(pObjectEntry != NULL);
ATLASSERT(pnBytesUsed != NULL);
ATLASSERT(m_aMem.GetCount() != 0);
*pnBytesUsed = 0;
// write the object definition out
PERF_OBJECT_TYPE* pObjectType = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_OBJECT_TYPE*) NULL);
if (pObjectType == NULL)
return E_OUTOFMEMORY;
_FillObjectType(pObjectType, pObjectEntry);
// save a pointer to the first counter entry and counter definition.
// we'll need them when we create the PERF_COUNTER_BLOCK data
CPerfMapEntry* pCounterEntries = pObjectEntry + 1;
PERF_COUNTER_DEFINITION* pCounterDefs = reinterpret_cast<PERF_COUNTER_DEFINITION*>(pData);
ULONG nCBSize = 0; // counter block size
// write the counter definitions out
for (DWORD i=0; i<pObjectEntry->m_nNumCounters; i++)
{
CPerfMapEntry* pCounterEntry = pObjectEntry+i+1;
PERF_COUNTER_DEFINITION* pCounterDef = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_COUNTER_DEFINITION*) NULL);
if (pCounterDef == NULL)
return E_OUTOFMEMORY;
_FillCounterDef(pCounterDef, pCounterEntry, nCBSize);
}
// search for objects of the appropriate type and write out their instance/counter data
CAtlFileMappingBase* pCurrentBlock = m_aMem[0];
CPerfObject* pInstance = _GetFirstObject(pCurrentBlock);
while (pInstance && pInstance->m_nAllocSize != 0)
{
if (pInstance->m_dwObjectId == pObjectEntry->m_dwPerfId)
{
PERF_INSTANCE_DEFINITION* pInstanceDef = NULL;
if (pObjectEntry->m_nInstanceLess == PERF_NO_INSTANCES)
pObjectType->NumInstances = PERF_NO_INSTANCES;
else
{
pObjectType->NumInstances++;
// create an instance definition
pInstanceDef = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_INSTANCE_DEFINITION*) NULL);
if (pInstanceDef == NULL)
return E_OUTOFMEMORY;
pInstanceDef->ParentObjectTitleIndex = 0;
pInstanceDef->ParentObjectInstance = 0;
pInstanceDef->UniqueID = PERF_NO_UNIQUE_ID;
// handle the instance name
LPCWSTR szInstNameSrc = LPCWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset);
pInstanceDef->NameLength = (ULONG)(wcslen(szInstNameSrc)+1)*sizeof(WCHAR);
LPWSTR szInstNameDest = (LPWSTR) _AllocData(pData, nBytesAvail, pnBytesUsed, AtlAlignUp(pInstanceDef->NameLength, sizeof(DWORD)));
if (szInstNameDest == NULL)
return E_OUTOFMEMORY;
memcpy(szInstNameDest, szInstNameSrc, pInstanceDef->NameLength);
pInstanceDef->NameOffset = ULONG(LPBYTE(szInstNameDest) - LPBYTE(pInstanceDef));
pInstanceDef->ByteLength = DWORD(sizeof(PERF_INSTANCE_DEFINITION) + AtlAlignUp(pInstanceDef->NameLength, sizeof(DWORD)));
}
// create the counter block
PERF_COUNTER_BLOCK* pCounterBlock = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_COUNTER_BLOCK*) NULL);
if (pCounterBlock == NULL)
return E_OUTOFMEMORY;
pCounterBlock->ByteLength = sizeof(PERF_COUNTER_BLOCK) + nCBSize;
LPBYTE pCounterData = _AllocData(pData, nBytesAvail, pnBytesUsed, nCBSize);
if (pCounterData == NULL)
return E_OUTOFMEMORY;
for (ULONG i=0; i<pObjectType->NumCounters; i++)
{
switch (pCounterEntries[i].m_dwCounterType & ATLPERF_SIZE_MASK)
{
case PERF_SIZE_DWORD:
*LPDWORD(pCounterData+pCounterDefs[i].CounterOffset-sizeof(PERF_COUNTER_BLOCK)) =
*LPDWORD(LPBYTE(pInstance)+pCounterEntries[i].m_nDataOffset);
break;
case PERF_SIZE_LARGE:
*PULONGLONG(pCounterData+pCounterDefs[i].CounterOffset-sizeof(PERF_COUNTER_BLOCK)) =
*PULONGLONG(LPBYTE(pInstance)+pCounterEntries[i].m_nDataOffset);
break;
case PERF_SIZE_VARIABLE_LEN:
{
LPBYTE pSrc = LPBYTE(pInstance)+pObjectEntry->m_nDataOffset;
LPBYTE pDest = pCounterData+pCounterDefs[i].CounterOffset-sizeof(PERF_COUNTER_BLOCK);
if ((pCounterEntries[i].m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE)
{
ULONG nLen = (ULONG)wcslen(LPCWSTR(pSrc));
nLen = min(nLen, pCounterEntries[i].m_nMaxCounterSize-1);
wcsncpy(LPWSTR(pDest), LPCWSTR(pSrc), nLen);
((LPWSTR) pDest)[nLen] = 0;
}
else
{
ULONG nLen = (ULONG)strlen(LPCSTR(pSrc));
nLen = min(nLen, pCounterEntries[i].m_nMaxCounterSize-1);
strncpy(LPSTR(pDest), LPCSTR(pSrc), nLen);
((LPSTR) pDest)[nLen] = 0;
}
}
break;
}
}
if (pInstanceDef != NULL)
pObjectType->TotalByteLength += pInstanceDef->ByteLength;
pObjectType->TotalByteLength += sizeof(PERF_COUNTER_BLOCK) + nCBSize;
}
pInstance = _GetNextObject(pInstance);
if (pInstance->m_nAllocSize == (ULONG) -1)
{
pCurrentBlock = _GetNextBlock(pCurrentBlock);
if (pCurrentBlock == NULL)
pInstance = NULL;
else
pInstance = _GetFirstObject(pCurrentBlock);
}
}
return S_OK;
}
inline DWORD CPerfMon::Open(LPWSTR szDeviceNames) throw()
{
szDeviceNames; // unused
return Initialize();
}
inline DWORD CPerfMon::Collect(
LPWSTR szValue,
LPVOID* ppData,
LPDWORD pcbBytes,
LPDWORD pcObjectTypes
) throw()
{
_ATLTRY
{
if (m_aMem.GetCount() == 0 || m_aMem[0]->GetData() == NULL || m_lock.m_h == NULL)
{
*pcbBytes = 0;
*pcObjectTypes = 0;
return ERROR_SUCCESS;
}
// get a lock so that other threads don't corrupt the data we're collecting
CPerfLock lock(this);
if (FAILED(lock.GetStatus()))
{
*pcbBytes = 0;
*pcObjectTypes = 0;
return ERROR_SUCCESS;
}
LPBYTE pData = LPBYTE(*ppData);
ULONG nBytesLeft = *pcbBytes;
ULONG nBytesUsed;
*pcbBytes = 0;
for (UINT i=0; i<_GetNumMapEntries(); i += _GetMapEntry(i).m_nNumCounters+1)
{
CPerfMapEntry* pObjectEntry = &_GetMapEntry(i);
if (_WantObjectType(szValue, pObjectEntry->m_nNameId))
{
if (FAILED(_CollectObjectType(pObjectEntry, pData, nBytesLeft, &nBytesUsed)))
{
*pcbBytes = 0;
*pcObjectTypes = 0;
return ERROR_SUCCESS;
}
(*pcObjectTypes)++;
(*pcbBytes) += nBytesUsed;
nBytesLeft -= nBytesUsed;
pData += nBytesUsed;
}
}
*ppData = pData;
return ERROR_SUCCESS;
}
_ATLCATCHALL()
{
*pcbBytes = 0;
*pcObjectTypes = 0;
return ERROR_SUCCESS;
}
}
inline DWORD CPerfMon::Close() throw()
{
UnInitialize();
return ERROR_SUCCESS;
}
#ifdef _ATL_PERF_REGISTER
inline void CPerfMon::_AppendStrings(
LPTSTR& pszNew,
CAtlArray<CString>& astrStrings,
ULONG iFirstIndex
) throw()
{
for (UINT iString = 0; iString < astrStrings.GetCount(); iString++)
{
INT nFormatChars = _stprintf(pszNew, _T("%d"), iFirstIndex+2*iString);
pszNew += nFormatChars + 1;
_tcscpy(pszNew, astrStrings[iString]);
pszNew += astrStrings[iString].GetLength() + 1;
}
}
inline HRESULT CPerfMon::_AppendRegStrings(
CRegKey& rkLang,
LPCTSTR szValue,
CAtlArray<CString>& astrStrings,
ULONG nNewStringSize,
ULONG iFirstIndex,
ULONG iLastIndex
) throw()
{
_ATLTRY
{
// load the existing strings, add the new data, and resave the strings
ULONG nCharsOrig = 0;
ULONG nCharsNew;
DWORD dwErr;
dwErr = rkLang.QueryMultiStringValue(szValue, NULL, &nCharsOrig);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
nCharsNew = nCharsOrig + nNewStringSize;
CString strOrig;
dwErr = rkLang.QueryMultiStringValue(szValue, CStrBuf(strOrig, nCharsOrig, CStrBuf::SET_LENGTH), &nCharsOrig);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
LPCTSTR pszOrig = strOrig;
CString strNew;
CStrBuf szNew(strNew, nCharsNew, CStrBuf::SET_LENGTH);
LPTSTR pszNew = szNew;
bool bNewStringsAdded = false;
while (*pszOrig != '\0')
{
ULONG iIndex = _ttoi(pszOrig);
int nLen = (int) _tcslen(pszOrig) + 1; // get the length of the index and null
nLen += (int) _tcslen(pszOrig+nLen) + 1; // add the length of the description and null
if (!bNewStringsAdded && iIndex >= iFirstIndex)
{
_AppendStrings(pszNew, astrStrings, iFirstIndex);
bNewStringsAdded = true;
}
if (iIndex < iFirstIndex || iIndex > iLastIndex)
{
memmove(pszNew, pszOrig, nLen*sizeof(TCHAR));
pszNew += nLen;
}
pszOrig += nLen;
}
if (!bNewStringsAdded)
_AppendStrings(pszNew, astrStrings, iFirstIndex);
*pszNew++ = '\0'; // must have 2 null terminators at end of multi_sz
dwErr = rkLang.SetMultiStringValue(szValue, strNew);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
return S_OK;
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
}
inline HRESULT CPerfMon::_RemoveRegStrings(
CRegKey& rkLang,
LPCTSTR szValue,
ULONG iFirstIndex,
ULONG iLastIndex
) throw()
{
_ATLTRY
{
// load the existing strings, remove the data, and resave the strings
DWORD nChars = 0;
DWORD dwErr;
dwErr = rkLang.QueryMultiStringValue(szValue, NULL, &nChars);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
CString str;
CStrBuf szBuf(str, nChars, CStrBuf::SET_LENGTH);
dwErr = rkLang.QueryMultiStringValue(szValue, szBuf, &nChars);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
LPCTSTR pszRead = szBuf;
LPTSTR pszWrite = szBuf;
while (*pszRead != '\0')
{
ULONG iIndex = _ttoi(pszRead);
int nLen = (int) _tcslen(pszRead) + 1; // get the length of the index and null
nLen += (int) _tcslen(pszRead+nLen) + 1; // add the length of the description and null
if (iIndex < iFirstIndex || iIndex > iLastIndex)
{
memmove(pszWrite, pszRead, nLen*sizeof(TCHAR));
pszWrite += nLen;
}
pszRead += nLen;
}
*pszWrite++ = '\0'; // must have 2 null terminators at end of multi_sz
dwErr = rkLang.SetMultiStringValue(szValue, szBuf);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
return S_OK;
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
}
inline HRESULT CPerfMon::_ReserveStringRange(DWORD& dwFirstCounter, DWORD& dwFirstHelp) throw()
{
CRegKey rkApp;
CString strAppKey;
DWORD dwErr;
_ATLTRY
{
strAppKey.Format(c_szAtlPerfPerformanceKey, GetAppName());
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, strAppKey);
if (dwErr == ERROR_SUCCESS)
{
// see if we already have a sufficient range reserved
DWORD dwFirstAppCounter;
DWORD dwFirstAppHelp;
DWORD dwLastAppCounter;
DWORD dwLastAppHelp;
if (rkApp.QueryDWORDValue(c_szAtlPerfFirstCounter, dwFirstAppCounter) == ERROR_SUCCESS &&
rkApp.QueryDWORDValue(c_szAtlPerfFirstHelp, dwFirstAppHelp) == ERROR_SUCCESS &&
rkApp.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastAppCounter) == ERROR_SUCCESS &&
rkApp.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastAppHelp) == ERROR_SUCCESS &&
dwLastAppCounter-dwFirstAppCounter+2 >= DWORD(2*_GetNumMapEntries()) &&
dwLastAppHelp-dwFirstAppHelp+2 >= DWORD(2*_GetNumMapEntries()))
{
dwFirstCounter = dwFirstAppCounter;
dwFirstHelp = dwFirstAppHelp;
return S_OK;
}
}
CRegKey rkPerfLib;
dwErr = rkPerfLib.Open(HKEY_LOCAL_MACHINE, c_szAtlPerfPerfLibKey);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
if (!rkApp)
{
dwErr = rkApp.Create(HKEY_LOCAL_MACHINE, strAppKey);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
}
// figure out the counter range
DWORD dwLastCounter;
DWORD dwLastHelp;
dwErr = rkPerfLib.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkPerfLib.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwFirstCounter = dwLastCounter + 2;
dwFirstHelp = dwLastHelp + 2;
dwLastCounter += 2*_GetNumMapEntries();
dwLastHelp += 2*_GetNumMapEntries();
dwErr = rkPerfLib.SetDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkPerfLib.SetDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
// register the used counter range
dwErr = rkApp.SetDWORDValue(c_szAtlPerfFirstCounter, dwFirstCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.SetDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.SetDWORDValue(c_szAtlPerfFirstHelp, dwFirstHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.SetDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
return S_OK;
}
inline HRESULT CPerfMon::Register(
LPCTSTR szOpenFunc,
LPCTSTR szCollectFunc,
LPCTSTR szCloseFunc,
HINSTANCE hDllInstance /* == _AtlBaseModule.GetModuleInstance() */
) throw()
{
ATLASSERT(szOpenFunc != NULL);
ATLASSERT(szCollectFunc != NULL);
ATLASSERT(szCloseFunc != NULL);
CString str;
DWORD dwErr;
HRESULT hr;
hr = CreateMap(LANGIDFROMLCID(GetThreadLocale()), hDllInstance);
if (FAILED(hr))
return hr;
CString strAppKey;
_ATLTRY
{
strAppKey.Format(c_szAtlPerfPerformanceKey, GetAppName());
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
// if we're already registered, unregister so we can redo registration
_UnregisterStrings();
// reserve a range for our counter and help strings
DWORD dwFirstCounter = 0;
DWORD dwFirstHelp = 0;
_ReserveStringRange(dwFirstCounter, dwFirstHelp);
for (UINT i=0; i<_GetNumMapEntries(); i++)
{
CPerfMapEntry& entry = _GetMapEntry(i);
entry.m_nNameId = dwFirstCounter + i*2;
entry.m_nHelpId = dwFirstHelp + i*2;
}
// register the app entry points
CRegKey rkApp;
dwErr = rkApp.Create(HKEY_LOCAL_MACHINE, strAppKey);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
_ATLTRY
{
if (GetModuleFileName(hDllInstance, CStrBuf(str, MAX_PATH), MAX_PATH) == 0)
return AtlHresultFromLastError();
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
dwErr = rkApp.SetStringValue(c_szAtlPerfLibrary, str);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.SetStringValue(c_szAtlPerfOpen, szOpenFunc);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.SetStringValue(c_szAtlPerfCollect, szCollectFunc);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.SetStringValue(c_szAtlPerfClose, szCloseFunc);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.SetStringValue(c_szAtlPerfLanguages, _T(""));
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
hr = _SaveMap();
if (FAILED(hr))
return hr;
return S_OK;
}
inline HRESULT CPerfMon::RegisterStrings(
LANGID language /* = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL) */,
HINSTANCE hResInstance /* = _AtlBaseModule.GetResourceInstance() */
) throw()
{
_ATLTRY
{
CString str;
DWORD dwErr;
HRESULT hr;
CRegKey rkLang;
CRegKey rkApp;
LANGID wPrimaryLanguage = (LANGID) PRIMARYLANGID(language);
if (language == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))
{
language = LANGIDFROMLCID(GetThreadLocale());
wPrimaryLanguage = (LANGID) PRIMARYLANGID(language);
}
hr = CreateMap(language, hResInstance);
if (FAILED(hr))
return hr;
str.Format(c_szAtlPerfPerfLibLangKey, wPrimaryLanguage);
dwErr = rkLang.Open(HKEY_LOCAL_MACHINE, str);
if (dwErr == ERROR_FILE_NOT_FOUND)
return S_FALSE; // the language isn't installed on the system
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
// load list of language strings already registered
str.Format(c_szAtlPerfPerformanceKey, GetAppName());
dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, str);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
DWORD dwLangsLen = 0;
CString strLangs;
dwErr = rkApp.QueryStringValue(c_szAtlPerfLanguages, NULL, &dwLangsLen);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
CStrBuf szLangs(strLangs, dwLangsLen+4, CStrBuf::SET_LENGTH); // reserve room for adding new language
dwErr = rkApp.QueryStringValue(c_szAtlPerfLanguages, szLangs, &dwLangsLen);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwLangsLen--; // don't count '\0'
// see if this language has already been registered and if so, return
TCHAR szNewLang[5];
_stprintf(szNewLang, _T("%3.3x "), wPrimaryLanguage);
if (strLangs.Find(szNewLang) != -1)
return S_OK;
// load the strings we want to append and figure out how much extra space is needed for them
// (including up to 5-digit index values and 2 null separators)
CAtlArray<CString> astrCounters;
CAtlArray<CString> astrHelp;
ULONG nNewCounterSize = 0;
ULONG nNewHelpSize = 0;
for (UINT i=0; i<_GetNumMapEntries(); i++)
{
CPerfMapEntry& object = _GetMapEntry(i);
astrCounters.Add(object.m_strName);
nNewCounterSize += object.m_strName.GetLength() + 7;
astrHelp.Add(object.m_strHelp);
nNewHelpSize += object.m_strHelp.GetLength() + 7;
}
DWORD dwFirstCounter;
DWORD dwFirstHelp;
DWORD dwLastCounter;
DWORD dwLastHelp;
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstCounter, dwFirstCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstHelp, dwFirstHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
hr = _AppendRegStrings(rkLang, c_szAtlPerfCounter, astrCounters, nNewCounterSize, dwFirstCounter, dwLastCounter);
if (FAILED(hr))
return hr;
hr = _AppendRegStrings(rkLang, c_szAtlPerfHelp, astrHelp, nNewHelpSize, dwFirstHelp, dwLastHelp);
if (FAILED(hr))
return hr;
// add the language to the list of installed languages
_tcscpy(szLangs+dwLangsLen, szNewLang);
dwErr = rkApp.SetStringValue(c_szAtlPerfLanguages, szLangs);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
return S_OK;
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
}
inline BOOL CPerfMon::EnumResLangProc(
HINSTANCE hModule,
LPCTSTR szType,
LPCTSTR szName,
LANGID wIDLanguage,
LPARAM lParam
) throw()
{
hModule; // unused
szType; // unused
szName; // unused
CAtlArray<LANGID>* pLangs = reinterpret_cast<CAtlArray<LANGID>*>(lParam);
_ATLTRY
{
pLangs->Add(wIDLanguage);
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
return TRUE;
}
inline HRESULT CPerfMon::RegisterAllStrings(
HINSTANCE hResInstance /* = NULL */
) throw()
{
HRESULT hrReturn = S_FALSE;
HRESULT hr;
UINT nRes;
hr = CreateMap(0, NULL, &nRes);
if (FAILED(hr))
return hr;
if (nRes == 0)
return RegisterStrings(0, hResInstance);
if (hResInstance != NULL)
return _RegisterAllStrings(nRes, hResInstance);
for (int i = 0; hResInstance = _AtlBaseModule.GetHInstanceAt(i), hResInstance != NULL; i++)
{
hr = _RegisterAllStrings(nRes, hResInstance);
if (FAILED(hr))
return hr;
if (hr == S_OK)
hrReturn = S_OK;
}
return hrReturn;
}
inline HRESULT CPerfMon::_RegisterAllStrings(
UINT nRes,
HINSTANCE hResInstance
) throw()
{
HRESULT hrReturn = S_FALSE;
HRESULT hr;
CAtlArray<LANGID> langs;
if (!EnumResourceLanguages(hResInstance, RT_STRING, MAKEINTRESOURCE((nRes>>4)+1), EnumResLangProc, reinterpret_cast<LPARAM>(&langs)))
return AtlHresultFromLastError();
for (UINT i=0; i<langs.GetCount(); i++)
{
hr = RegisterStrings(langs[i], hResInstance);
if (FAILED(hr))
return hr;
if (hr == S_OK)
hrReturn = S_OK;
}
return hrReturn;
}
inline HRESULT CPerfMon::_UnregisterStrings() throw()
{
_ATLTRY
{
CString str;
HRESULT hr;
DWORD dwErr;
// unregister the PerfMon counter and help strings
CRegKey rkApp;
str.Format(c_szAtlPerfPerformanceKey, GetAppName());
dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, str);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
DWORD dwFirstAppCounter;
DWORD dwFirstAppHelp;
DWORD dwLastAppCounter;
DWORD dwLastAppHelp;
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstCounter, dwFirstAppCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstHelp, dwFirstAppHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastAppCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastAppHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
// iterate through the installed languages and delete them all
DWORD nChars = 0;
dwErr = rkApp.QueryStringValue(c_szAtlPerfLanguages, NULL, &nChars);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
CString strLangs;
dwErr = rkApp.QueryStringValue(c_szAtlPerfLanguages, CStrBuf(strLangs, nChars, CStrBuf::SET_LENGTH), &nChars);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
int nIndex = 0;
CString strLang = strLangs.Tokenize(_T(" "), nIndex);
while (!strLang.IsEmpty())
{
CRegKey rkLang;
dwErr = rkLang.Open(HKEY_LOCAL_MACHINE, CString(c_szAtlPerfPerfLibKey) + _T("\\") + strLang);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
hr = _RemoveRegStrings(rkLang, c_szAtlPerfCounter, dwFirstAppCounter, dwLastAppCounter);
if (FAILED(hr))
return hr;
hr = _RemoveRegStrings(rkLang, c_szAtlPerfHelp, dwFirstAppHelp, dwLastAppHelp);
if (FAILED(hr))
return hr;
strLang = strLangs.Tokenize(_T(" "), nIndex);
}
dwErr = rkApp.SetStringValue(c_szAtlPerfLanguages, _T(""));
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
return S_OK;
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
}
inline HRESULT CPerfMon::Unregister() throw()
{
CString str;
HRESULT hr;
DWORD dwErr;
CRegKey rkPerfLib;
CRegKey rkApp;
hr = _UnregisterStrings();
if (FAILED(hr))
return hr;
dwErr = rkPerfLib.Open(HKEY_LOCAL_MACHINE, c_szAtlPerfPerfLibKey);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
_ATLTRY
{
str.Format(c_szAtlPerfPerformanceKey, GetAppName());
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, str);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
DWORD dwLastCounter;
DWORD dwLastHelp;
DWORD dwFirstAppCounter;
DWORD dwFirstAppHelp;
DWORD dwLastAppCounter;
DWORD dwLastAppHelp;
dwErr = rkPerfLib.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkPerfLib.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstCounter, dwFirstAppCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfFirstHelp, dwFirstAppHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastCounter, dwLastAppCounter);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkApp.QueryDWORDValue(c_szAtlPerfLastHelp, dwLastAppHelp);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
// rewind the Last Help/Last Counter values if possible
if (dwLastCounter == dwLastAppCounter)
{
dwErr = rkPerfLib.SetDWORDValue(c_szAtlPerfLastCounter, dwFirstAppCounter-2);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
}
if (dwLastHelp == dwLastAppHelp)
{
dwErr = rkPerfLib.SetDWORDValue(c_szAtlPerfLastHelp, dwFirstAppHelp-2);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
}
// delete the app key
CRegKey rkServices;
rkApp.Close();
dwErr = rkServices.Open(HKEY_LOCAL_MACHINE, c_szAtlPerfServicesKey);
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
dwErr = rkServices.RecurseDeleteKey(GetAppName());
if (dwErr != ERROR_SUCCESS)
return AtlHresultFromWin32(dwErr);
return S_OK;
}
#endif
inline HRESULT CPerfMon::Initialize() throw()
{
CMutex tempLock;
CString strAppName;
HRESULT hr;
_ATLTRY
{
strAppName = GetAppName();
ATLASSERT(m_aMem.GetCount() == 0);
// initialize a security descriptor to give everyone access to objects we create
CSecurityDescriptor sd;
sd.InitializeFromThreadToken();
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), sd, FALSE };
// create a mutex to handle syncronizing access to the shared memory area
CString strMutexName;
strMutexName.Format(_T("ATLPERF_%s_LOCK"), strAppName);
tempLock.Create(&sa, FALSE, strMutexName);
if (tempLock.m_h == NULL)
return AtlHresultFromLastError();
// create a shared memory area to share data between the app being measured and the client doing the measuring
{
CMutexLock lock(tempLock);
BOOL bExisted = FALSE;
CAtlFileMappingBase* pMem;
pMem = _AllocNewBlock(NULL, &bExisted);
if (pMem == NULL)
return E_OUTOFMEMORY;
if (!bExisted)
{
// copy the map from the registry to the shared memory
CRegKey rkApp;
DWORD dwErr;
CString strAppKey;
strAppKey.Format(c_szAtlPerfPerformanceKey, GetAppName());
dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, strAppKey, KEY_READ);
if (dwErr != ERROR_SUCCESS)
{
m_aMem.RemoveAll();
return AtlHresultFromWin32(dwErr);
}
ULONG nBytes = m_nAllocSize;
dwErr = rkApp.QueryBinaryValue(c_szAtlPerfMap, pMem->GetData(), &nBytes);
if (dwErr != ERROR_SUCCESS)
{
m_aMem.RemoveAll();
return AtlHresultFromWin32(dwErr);
}
}
hr = _LoadMap();
if (FAILED(hr))
{
m_aMem.RemoveAll();
return hr;
}
m_nSchemaSize = *LPDWORD(pMem->GetData());
m_nHeaderSize = m_nSchemaSize + sizeof(DWORD);
}
m_lock.Attach(tempLock.Detach());
}
_ATLCATCHALL()
{
m_aMem.RemoveAll();
return E_OUTOFMEMORY;
}
return S_OK;
}
inline void CPerfMon::UnInitialize() throw()
{
if (m_lock.m_h != NULL)
m_lock.Close();
m_aMem.RemoveAll();
ClearMap();
}
inline HRESULT CPerfMon::_CreateInstance(
DWORD dwObjectId,
DWORD dwInstance,
LPCWSTR szInstanceName,
CPerfObject** ppInstance,
bool bByName
) throw()
{
CPerfObject* pEmptyBlock = NULL;
if (ppInstance == NULL)
return E_POINTER;
if (m_aMem.GetCount() == 0 || m_aMem[0]->GetData() == NULL || m_lock.m_h == NULL)
return E_UNEXPECTED; // Initialize must succeed before calling CreateInstance
*ppInstance = NULL;
CPerfMapEntry* pObjectEntry = _FindObjectInfo(dwObjectId);
if (pObjectEntry == NULL)
return E_INVALIDARG;
if (szInstanceName == NULL && bByName)
return E_INVALIDARG;
if (pObjectEntry->m_nInstanceLess == PERF_NO_INSTANCES &&
(dwInstance != 0 || szInstanceName != NULL))
return E_INVALIDARG;
CPerfLock lock(this);
if (FAILED(lock.GetStatus()))
return lock.GetStatus();
CAtlFileMappingBase* pCurrentBlock = m_aMem[0];
CPerfObject* pInstance = _GetFirstObject(pCurrentBlock);
ULONG nMaxInstance = 0;
ULONG nUsedSpace = 0;
// walk all of the existing objects trying to find one that matches the request
while (pInstance->m_nAllocSize != 0)
{
nUsedSpace += pInstance->m_nAllocSize;
if (pInstance->m_dwObjectId == dwObjectId)
{
nMaxInstance = max(nMaxInstance, pInstance->m_dwInstance);
// check to see if we've found the one the caller wants
if (!bByName && pInstance->m_dwInstance == dwInstance &&
(pObjectEntry->m_nInstanceLess == PERF_NO_INSTANCES || dwInstance != 0))
{
*ppInstance = pInstance;
pInstance->m_nRefCount++;
return S_OK;
}
if (bByName)
{
LPWSTR szInstName = (LPWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset));
if (wcsncmp(szInstName, szInstanceName, pObjectEntry->m_nMaxInstanceNameLen-1) == 0)
{
*ppInstance = pInstance;
pInstance->m_nRefCount++;
return S_OK;
}
}
}
if (pInstance->m_nAllocSize == pObjectEntry->m_nAllocSize && pInstance->m_dwObjectId == 0)
pEmptyBlock = pInstance;
pInstance = _GetNextObject(pInstance);
if (pInstance->m_nAllocSize == 0 &&
m_nHeaderSize + nUsedSpace + pObjectEntry->m_nAllocSize + sizeof(CPerfObject) > m_nAllocSize)
{
// we've reached the end of the block and have no room to allocate an object of this
// type. cap the block with a sentinel
pInstance->m_nAllocSize = (ULONG) -1;
}
// check for an end-of-shared-mem sentinel
if (pInstance->m_nAllocSize == (ULONG) -1)
{
nUsedSpace = 0;
CAtlFileMappingBase* pNextBlock = _GetNextBlock(pCurrentBlock);
if (pNextBlock == NULL)
{
// we've reached the last block of shared mem.
// the instance hasn't been found, so either use a
// previously freed instance block (pEmptyBlock) or allocate a new
// shared mem block to hold the new instance
if (pEmptyBlock == NULL)
{
pNextBlock = _AllocNewBlock(pCurrentBlock);
if (pNextBlock == NULL)
return E_OUTOFMEMORY;
}
else
break;
}
pCurrentBlock = pNextBlock;
pInstance = _GetFirstObject(pCurrentBlock);
}
}
// allocate a new object
if (pEmptyBlock != NULL)
pInstance = pEmptyBlock;
else
pInstance->m_nAllocSize = pObjectEntry->m_nAllocSize;
pInstance->m_dwObjectId = pObjectEntry->m_dwPerfId;
if (dwInstance == 0 && pObjectEntry->m_nInstanceLess != PERF_NO_INSTANCES)
pInstance->m_dwInstance = nMaxInstance + 1;
else
pInstance->m_dwInstance = dwInstance;
pInstance->m_nRefCount = 1;
// copy the instance name, truncate if necessary
if (pObjectEntry->m_nInstanceLess != PERF_NO_INSTANCES)
{
ULONG nNameLen = (ULONG)min(wcslen(szInstanceName), pObjectEntry->m_nMaxInstanceNameLen-1);
ULONG nNameBytes = (nNameLen+1) * sizeof(WCHAR);
pInstance->m_nInstanceNameOffset = pInstance->m_nAllocSize-nNameBytes;
memcpy(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset, szInstanceName, nNameBytes);
LPWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset)[nNameLen] = 0;
}
*ppInstance = pInstance;
return S_OK;
}
inline HRESULT CPerfMon::CreateInstance(
DWORD dwObjectId,
DWORD dwInstance,
LPCWSTR szInstanceName,
CPerfObject** ppInstance
) throw()
{
return _CreateInstance(dwObjectId, dwInstance, szInstanceName, ppInstance, false);
}
inline HRESULT CPerfMon::CreateInstanceByName(
DWORD dwObjectId,
LPCWSTR szInstanceName,
CPerfObject** ppInstance
) throw()
{
return _CreateInstance(dwObjectId, 0, szInstanceName, ppInstance, true);
}
inline HRESULT CPerfMon::ReleaseInstance(CPerfObject* pInstance) throw()
{
ATLASSERT(pInstance != NULL);
if (pInstance == NULL)
return E_INVALIDARG;
CPerfLock lock(this);
if (FAILED(lock.GetStatus()))
return lock.GetStatus();
if (--pInstance->m_nRefCount == 0)
{
pInstance->m_dwInstance = 0;
pInstance->m_dwObjectId = 0;
}
return S_OK;
}
inline HRESULT CPerfMon::LockPerf(DWORD dwTimeout /* == INFINITE */) throw()
{
if (m_lock.m_h == NULL)
return E_UNEXPECTED;
DWORD dwRes = WaitForSingleObject(m_lock.m_h, dwTimeout);
if (dwRes == WAIT_ABANDONED || dwRes == WAIT_OBJECT_0)
return S_OK;
if (dwRes == WAIT_TIMEOUT)
return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
return AtlHresultFromLastError();
}
inline void CPerfMon::UnlockPerf() throw()
{
m_lock.Release();
}
// map building routines
inline HRESULT CPerfMon::AddObjectDefinition(
DWORD dwObjectId,
LPCTSTR szObjectName,
LPCTSTR szHelpString,
DWORD dwDetailLevel,
INT nDefaultCounter,
BOOL bInstanceLess,
UINT nStructSize,
UINT nMaxInstanceNameLen) throw()
{
// must have one and only one of these
ATLASSERT(!bInstanceLess ^ !nMaxInstanceNameLen);
CPerfMapEntry entry;
entry.m_dwPerfId = dwObjectId;
_ATLTRY
{
entry.m_strName = szObjectName;
entry.m_strHelp = szHelpString;
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
entry.m_dwDetailLevel = dwDetailLevel;
entry.m_bIsObject = TRUE;
// OBJECT INFO
entry.m_nNumCounters = 0;
entry.m_nDefaultCounter = nDefaultCounter;
entry.m_nInstanceLess = bInstanceLess ? PERF_NO_INSTANCES : 0;
entry.m_nStructSize = nStructSize;
entry.m_nMaxInstanceNameLen = nMaxInstanceNameLen;
entry.m_nAllocSize = nStructSize + nMaxInstanceNameLen*sizeof(WCHAR);
// COUNTER INFO
entry.m_dwCounterType = 0;
entry.m_nDefaultScale = 0;
entry.m_nMaxCounterSize = 0;
entry.m_nDataOffset = 0;
entry.m_nNameId = 0;
entry.m_nHelpId = 0;
_ATLTRY
{
m_map.Add(entry);
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
if (_GetNumMapEntries() == 1)
m_nNumObjectTypes = 1;
else
m_nNumObjectTypes++;
return S_OK;
}
inline HRESULT CPerfMon::AddCounterDefinition(
DWORD dwCounterId,
LPCTSTR szCounterName,
LPCTSTR szHelpString,
DWORD dwDetailLevel,
DWORD dwCounterType,
ULONG nMaxCounterSize,
UINT nOffset,
INT nDefaultScale) throw()
{
for (int i=_GetNumMapEntries()-1; i>=0; i--)
{
CPerfMapEntry& object = _GetMapEntry(i);
if (object.m_bIsObject)
{
CPerfMapEntry counter;
counter.m_dwPerfId = dwCounterId;
_ATLTRY
{
counter.m_strName = szCounterName;
counter.m_strHelp = szHelpString;
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
counter.m_dwDetailLevel = dwDetailLevel;
counter.m_bIsObject = FALSE;
// OBJECT INFO
counter.m_nNumCounters = 0;
counter.m_nDefaultCounter = 0;
counter.m_nInstanceLess = 0;
counter.m_nStructSize = 0;
counter.m_nMaxInstanceNameLen = 0;
counter.m_nAllocSize = 0;
// COUNTER INFO
counter.m_dwCounterType = dwCounterType;
counter.m_nDefaultScale = nDefaultScale;
counter.m_nMaxCounterSize = nMaxCounterSize;
counter.m_nDataOffset = nOffset;
object.m_nNumCounters++;
if (counter.m_nMaxCounterSize > 0)
{
ATLASSERT(counter.m_dwCounterType & PERF_TYPE_TEXT);
object.m_nAllocSize += counter.m_nMaxCounterSize * sizeof(WCHAR);
}
counter.m_nNameId = 0;
counter.m_nHelpId = 0;
_ATLTRY
{
m_map.Add(counter);
}
_ATLCATCHALL()
{
return E_OUTOFMEMORY;
}
return S_OK;
}
}
// found no object in map! must add object BEFORE adding counter!
ATLASSERT(FALSE);
return E_UNEXPECTED;
}
inline void CPerfMon::ClearMap() throw()
{
m_map.RemoveAll();
}
#ifndef _ATL_PERF_NOXML
ATL_NOINLINE inline HRESULT CPerfMon::PersistToXML(IStream *pStream, BOOL bFirst/*=TRUE*/, BOOL bLast/*=TRUE*/) throw(...)
{
ATLASSERT(pStream != NULL);
if (pStream == NULL)
return E_INVALIDARG;
CPerfLock lock(this);
if (FAILED(lock.GetStatus()))
return ERROR_SUCCESS;
CStringA strXML;
HRESULT hr = S_OK;
ULONG nLen = 0;
if (bFirst)
{
strXML = "<?xml version=\"1.0\" ?>\r\n<perfPersist>\r\n";
hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
if (hr != S_OK)
return hr;
}
strXML.Format("\t<perfmon name=\"%s\">\r\n", CT2CA(GetAppName()));
hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
for (UINT i=0; i<_GetNumMapEntries(); i+= _GetMapEntry(i).m_nNumCounters+1)
{
CPerfMapEntry *pObjectEntry = &_GetMapEntry(i);
CPerfMapEntry *pCounterEntries = pObjectEntry+1;
CAtlFileMappingBase *pCurrentBlock = _GetNextBlock(NULL);
CPerfObject *pInstance = _GetFirstObject(pCurrentBlock);
strXML.Format("\t\t<perfObject perfid=\"%d\">\r\n",
pObjectEntry->m_dwPerfId, pObjectEntry->m_nNameId, pObjectEntry->m_nHelpId);
hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
if (hr != S_OK)
return E_FAIL;
while (pInstance && pInstance->m_nAllocSize)
{
if (pInstance->m_dwObjectId == pObjectEntry->m_dwPerfId)
{
if (pObjectEntry->m_nInstanceLess != PERF_NO_INSTANCES)
{
// handle the instance name
LPCWSTR wszInstNameSrc = LPCWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset);
int nInstLen = (int) wcslen(wszInstNameSrc);
// convert to UTF8
nLen = AtlUnicodeToUTF8(wszInstNameSrc, nInstLen, NULL, 0);
CHeapPtr<CHAR> szUTF8;
if (!szUTF8.Allocate(nLen+1))
return E_OUTOFMEMORY;
nLen = AtlUnicodeToUTF8(wszInstNameSrc, nInstLen, szUTF8, nLen);
szUTF8[nLen] = '\0';
strXML.Format("\t\t\t<instance name=\"%s\">\r\n", szUTF8);
hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
if (hr != S_OK)
return hr;
}
for (ULONG j=0; j<pObjectEntry->m_nNumCounters; j++)
{
CPerfMapEntry *pCounterEntry = pCounterEntries+j;
switch (pCounterEntry->m_dwCounterType & ATLPERF_SIZE_MASK)
{
case PERF_SIZE_DWORD:
{
strXML.Format("\t\t\t\t<counter type=\"perf_size_dword\" value=\"%d\" offset=\"%d\"/>\r\n",
*LPDWORD(LPBYTE(pInstance)+pCounterEntry->m_nDataOffset),
pCounterEntry->m_nDataOffset);
break;
}
case PERF_SIZE_LARGE:
{
strXML.Format("\t\t\t\t<counter type=\"perf_size_large\" value=\"%d\" offset=\"%d\"/>\r\n",
*PULONGLONG(LPBYTE(pInstance)+pCounterEntry->m_nDataOffset),
pCounterEntry->m_nDataOffset);
break;
}
case PERF_SIZE_VARIABLE_LEN:
{
CHeapPtr<CHAR> szUTF8;
LPBYTE pSrc = LPBYTE(pInstance)+pCounterEntry->m_nDataOffset;
if ((pCounterEntry->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE)
{
ULONG nTextLen = (ULONG)wcslen(LPCWSTR(pSrc));
// convert to UTF8
nLen = AtlUnicodeToUTF8(LPCWSTR(pSrc), nTextLen, NULL, 0);
if (!szUTF8.Allocate(nLen+1))
return E_OUTOFMEMORY;
nLen = AtlUnicodeToUTF8(LPCWSTR(pSrc), nTextLen, szUTF8, nLen);
szUTF8[nLen] = '\0';
strXML.Format("\t\t\t\t<counter type=\"perf_size_variable_len_unicode\" value=\"%s\" offset=\"%d\"/>\r\n",
szUTF8,
pCounterEntry->m_nDataOffset);
}
else
{
ULONG nTextLen = (ULONG)strlen(LPCSTR(pSrc));
if (!szUTF8.Allocate(nTextLen+1))
return E_OUTOFMEMORY;
strcpy(szUTF8, LPCSTR(pSrc));
strXML.Format("\t\t\t\t<counter type=\"perf_size_variable_len_ansi\" value=\"%s\" offset=\"%d\"/>\r\n",
szUTF8,
pCounterEntry->m_nDataOffset);
}
break;
}
default:
// error:
return E_FAIL;
}
hr = pStream->Write(strXML, strXML.GetLength(), &nLen);
if (hr != S_OK)
return hr;
}
}
if (pObjectEntry->m_nInstanceLess != PERF_NO_INSTANCES)
{
hr = pStream->Write("\t\t\t</instance>\r\n", sizeof("\t\t\t</instance>\r\n")-1, &nLen);
if (hr != S_OK)
return hr;
}
pInstance = _GetNextObject(pInstance);
if (pInstance->m_nAllocSize == (ULONG)-1)
{
pCurrentBlock = _GetNextBlock(pCurrentBlock);
if (pCurrentBlock == NULL)
pInstance = NULL;
else
pInstance = _GetFirstObject(pCurrentBlock);
}
}
hr = pStream->Write("\t\t</perfObject>\r\n", sizeof("\t\t</perfObject>\r\n")-1, &nLen);
if (hr != S_OK)
return hr;
}
hr = pStream->Write("\t</perfmon>\r\n", sizeof("\t</perfmon>\r\n")-1, &nLen);
if (hr != S_OK)
return hr;
if (hr == S_OK && bLast)
hr = pStream->Write("</perfPersist>", sizeof("</perfPersist>")-1, &nLen);
return hr;
}
// This function is very lenient with inappropriate XML
ATL_NOINLINE inline HRESULT CPerfMon::LoadFromXML(IStream *pStream) throw(...)
{
ATLASSERT(pStream != NULL);
if (pStream == NULL)
return E_INVALIDARG;
// Get a lock
CPerfLock lock(this);
if (FAILED(lock.GetStatus()))
return ERROR_SUCCESS;
CComPtr<IXMLDOMDocument> spdoc;
// load the xml
HRESULT hr = CoCreateInstance(__uuidof(DOMDocument), NULL, CLSCTX_INPROC, __uuidof(IXMLDOMDocument), (void **) &spdoc);
if (FAILED(hr))
{
return hr;
}
spdoc->put_async(VARIANT_FALSE);
CComPtr<IPersistStreamInit> spSI;
hr = spdoc->QueryInterface(&spSI);
if (hr != S_OK)
return hr;
hr = spSI->Load(pStream);
if (hr != S_OK)
return hr;
// validate that it is a perfPersist stream
CComPtr<IXMLDOMElement> spRoot;
hr = spdoc->get_documentElement(&spRoot);
if (hr != S_OK)
return hr;
CComBSTR bstrName;
hr = spRoot->get_baseName(&bstrName);
if (wcscmp(bstrName, L"perfPersist"))
return S_FALSE;
USES_CONVERSION;
// find the appropriate perfmon node
CComPtr<IXMLDOMNode> spChild;
hr = spRoot->get_firstChild(&spChild);
while (hr == S_OK)
{
bstrName.Empty();
hr = spChild->get_baseName(&bstrName);
if (hr == S_OK)
{
if (!wcscmp(bstrName, L"perfmon"))
{
bstrName.Empty();
hr = _GetAttribute(spChild, L"name", &bstrName);
if (hr == S_OK)
{
if (!_tcscmp(W2CT(bstrName), GetAppName()))
break;
}
}
}
CComPtr<IXMLDOMNode> spNext;
hr = spChild->get_nextSibling(&spNext);
spChild.Attach(spNext.Detach());
}
// there is no perfmon node in the XML for the current CPerfMon class
if (hr != S_OK)
return S_FALSE;
CComPtr<IXMLDOMNode> spPerfRoot;
spPerfRoot.Attach(spChild.Detach());
// iterate over the objects in the perfmon subtree
// this is the loop that does the real work
hr = spPerfRoot->get_firstChild(&spChild);
DWORD dwInstance = 1;
while (hr == S_OK)
{
// see if it's a perfObject
bstrName.Empty();
hr = spChild->get_baseName(&bstrName);
if (hr != S_OK || wcscmp(bstrName, L"perfObject"))
return S_FALSE;
// get the perfid
bstrName.Empty();
hr = _GetAttribute(spChild, L"perfid", &bstrName);
DWORD dwPerfId = _wtoi(bstrName);
// iterate over children
CComPtr<IXMLDOMNode> spInstChild;
hr = spChild->get_firstChild(&spInstChild);
while (hr == S_OK)
{
// see if it's a instance
bstrName.Empty();
hr = spInstChild->get_baseName(&bstrName);
if (hr != S_OK || wcscmp(bstrName, L"instance"))
return S_FALSE;
// get the instance name
bstrName.Empty();
hr = _GetAttribute(spInstChild, L"name", &bstrName);
if (hr != S_OK)
return S_FALSE;
// create the instance
// REVIEW : take a loook at the dwInstance stuff--is it acceptable?
CPerfObject *pInstance = NULL;
hr = CreateInstance(dwPerfId, dwInstance++, bstrName, &pInstance);
if (hr != S_OK)
return S_FALSE;
// iterate over the counters and set the data
CComPtr<IXMLDOMNode> spCntrChild;
hr = spInstChild->get_firstChild(&spCntrChild);
while (hr == S_OK)
{
// get the base name
bstrName.Empty();
hr = spCntrChild->get_baseName(&bstrName);
if (hr != S_OK || wcscmp(bstrName, L"counter"))
return S_FALSE;
// get the type
bstrName.Empty();
hr = _GetAttribute(spCntrChild, L"type", &bstrName);
if (hr != S_OK)
return S_FALSE;
DWORD dwType;
if (!wcscmp(bstrName, L"perf_size_dword"))
dwType = PERF_SIZE_DWORD;
else if (!wcscmp(bstrName, L"perf_size_large"))
dwType = PERF_SIZE_LARGE;
else if (!wcscmp(bstrName, L"perf_size_variable_len_ansi"))
dwType = PERF_SIZE_VARIABLE_LEN;
else if (!wcscmp(bstrName, L"perf_size_variable_len_unicode"))
dwType = PERF_SIZE_VARIABLE_LEN | PERF_TEXT_UNICODE;
else
return S_FALSE;
// get the value
bstrName.Empty();
hr = _GetAttribute(spCntrChild, L"value", &bstrName);
if (hr != S_OK)
return S_FALSE;
CComBSTR bstrOffset;
hr = _GetAttribute(spCntrChild, L"offset", &bstrOffset);
if (hr != S_OK)
return S_FALSE;
WCHAR *pStop = NULL;
DWORD dwOffset = wcstoul(bstrOffset, &pStop, 10);
if (dwType == PERF_SIZE_DWORD) // add it as a DWORD
{
DWORD dwVal = wcstoul(bstrName, &pStop, 10);
*LPDWORD(LPBYTE(pInstance)+dwOffset) = dwVal;
}
else if (dwType == PERF_SIZE_LARGE) // add it is a ULONGLONG
{
ULONGLONG qwVal = _wcstoui64(bstrName, &pStop, 10);
*PULONGLONG(LPBYTE(pInstance)+dwOffset) = qwVal;
}
else if (dwType == PERF_SIZE_VARIABLE_LEN) // add it as an ansi string
{
AtlW2AHelper(LPSTR(LPBYTE(pInstance)+dwOffset), bstrName, bstrName.Length(), ATL::_AtlGetConversionACP());
}
else // add it as a unicode string
{
memcpy(LPBYTE(pInstance)+dwOffset, bstrName, bstrName.Length()*sizeof(WCHAR));
}
CComPtr<IXMLDOMNode> spCntrNext;
hr = spCntrChild->get_nextSibling(&spCntrNext);
spCntrChild.Attach(spCntrNext.Detach());
}
CComPtr<IXMLDOMNode> spInstNext;
hr = spInstChild->get_nextSibling(&spInstNext);
spInstChild.Attach(spInstNext.Detach());
}
CComPtr<IXMLDOMNode> spNext;
hr = spChild->get_nextSibling(&spNext);
spChild.Attach(spNext.Detach());
}
return S_OK;
}
// a little utility function to retrieve a named attribute from a node
ATL_NOINLINE inline HRESULT CPerfMon::_GetAttribute(IXMLDOMNode *pNode, LPCWSTR szAttrName, BSTR *pbstrVal) throw()
{
ATLASSERT(pNode != NULL);
ATLASSERT(szAttrName != NULL);
ATLASSERT(pbstrVal != NULL);
*pbstrVal = NULL;
CComPtr<IXMLDOMNamedNodeMap> spAttrs;
HRESULT hr = pNode->get_attributes(&spAttrs);
if (hr != S_OK)
return hr;
CComPtr<IXMLDOMNode> spAttr;
hr = spAttrs->getNamedItem((BSTR) szAttrName, &spAttr);
if (hr != S_OK)
return hr;
CComVariant varVal;
hr = spAttr->get_nodeValue(&varVal);
if (hr != S_OK)
return hr;
hr = varVal.ChangeType(VT_BSTR);
if (hr != S_OK)
return hr;
*pbstrVal = varVal.bstrVal;
varVal.vt = VT_EMPTY;
return S_OK;
}
#endif // _ATL_PERF_NOXML
} // namespace ATL
#pragma warning(pop)
#endif // __ATLPERF_INL__