// This is a part of the Active Template Library. // Copyright (C) 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 #pragma warning(push) #ifndef _CPPUNWIND #pragma warning(disable: 4702) // unreachable code #endif namespace ATL { extern __declspec(selectany) const TCHAR * const c_szAtlPerfCounter = _T("Counter"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfFirstCounter = _T("First Counter"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfLastCounter = _T("Last Counter"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfHelp = _T("Help"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfFirstHelp = _T("First Help"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfLastHelp = _T("Last Help"); extern __declspec(selectany) const WCHAR * const c_szAtlPerfGlobal = L"Global"; extern __declspec(selectany) const TCHAR * const c_szAtlPerfLibrary = _T("Library"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfOpen = _T("Open"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfCollect = _T("Collect"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfClose = _T("Close"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfLanguages = _T("Languages"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfMap = _T("Map"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfPerformance = _T("Performance"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfServicesKey = _T("SYSTEM\\CurrentControlSet\\Services\\%s"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfPerformanceKey = _T("SYSTEM\\CurrentControlSet\\Services\\%s\\Performance"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfPerfLibKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib"); extern __declspec(selectany) const TCHAR * const c_szAtlPerfPerfLibLangKey = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\%3.3x"); inline CPerfMon::CounterInfo* CPerfMon::CategoryInfo::_GetCounterInfo(UINT nIndex) throw() { ATLASSERT(nIndex < _GetNumCounters()); return &m_counters[nIndex]; } inline UINT CPerfMon::CategoryInfo::_GetNumCounters() throw() { return (UINT) m_counters.GetCount(); } 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 UINT CPerfMon::_GetNumCategoriesAndCounters() throw() { UINT nResult = _GetNumCategories(); for (UINT i=0; i<_GetNumCategories(); i++) { nResult += _GetCategoryInfo(i)->_GetNumCounters(); } return nResult; } inline CPerfMon::CategoryInfo* CPerfMon::_GetCategoryInfo(UINT nIndex) throw() { ATLASSERT(nIndex < _GetNumCategories()); return &m_categories[nIndex]; } inline UINT CPerfMon::_GetNumCategories() throw() { return (UINT) m_categories.GetCount(); } inline CPerfObject* CPerfMon::_GetFirstInstance(CAtlFileMappingBase* pBlock) { ATLENSURE(pBlock != NULL); // should never happen if Initialize succeeded // are you checking return codes? ATLASSERT(pBlock->GetData() != NULL); return reinterpret_cast(LPBYTE(pBlock->GetData()) + m_nHeaderSize); } inline CPerfObject* CPerfMon::_GetNextInstance(CPerfObject* pInstance) { ATLENSURE(pInstance != NULL); return reinterpret_cast(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 (m_aMem.GetCount() == dwNextBlockIndex) return NULL; return m_aMem[dwNextBlockIndex]; } inline CAtlFileMappingBase* CPerfMon::_OpenNextBlock(CAtlFileMappingBase* pPrev) throw() { CAutoPtr 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; } _ATLTRY { CString strName; strName.Format(_T("Global\\ATLPERF_%s_%3.3d"), GetAppName(), dwNextBlockIndex); HRESULT hr = spMem->OpenMapping(strName, m_nAllocSize, 0, FILE_MAP_READ); if (FAILED(hr)) return NULL; pMem = spMem; m_aMem.Add(spMem); } _ATLCATCHALL() { return NULL; } return pMem; } inline CAtlFileMappingBase* CPerfMon::_AllocNewBlock(CAtlFileMappingBase* pPrev, BOOL* pbExisted /* == NULL */) throw() { CAtlFileMappingBase* pMem = NULL; _ATLTRY { CSecurityAttributes sa; sa.Set(m_sd); CAutoPtr spMem; 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; CString strName; strName.Format(_T("Global\\ATLPERF_%s_%3.3d"), GetAppName(), dwNextBlockIndex); HRESULT hr = spMem->MapSharedMem(m_nAllocSize, strName, &bExisted, &sa); if (FAILED(hr)) { return NULL; } if(!bExisted) { memset(spMem->GetData(), 0, m_nAllocSize); // save the index of this block // don't for first block since we don't know m_nSchemaSize yet if (dwNextBlockIndex) { _GetBlockId(spMem) = dwNextBlockIndex; } } else { CSid owner; CDacl dacl; m_sd.GetOwner(&owner); m_sd.GetDacl(&dacl); // prevent us from using an object someone else has opened if (::SetSecurityInfo(spMem->GetHandle(), SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, const_cast(owner.GetPSID()), NULL, const_cast(dacl.GetPACL()), NULL) != ERROR_SUCCESS) { return NULL; } } if (pbExisted) { *pbExisted = bExisted; } pMem = spMem; m_aMem.Add(spMem); OnBlockAlloc(pMem); } _ATLCATCHALL() { return NULL; } return pMem; } inline HRESULT CPerfMon::_OpenAllBlocks() throw() { HRESULT hr; // if we haven't opened any yet, initialize if (m_aMem.GetCount() == 0) { CAtlFileMappingBase* pMem = _OpenNextBlock(NULL); if (pMem == NULL) return S_OK; hr = _LoadMap(LPDWORD(pMem->GetData())); if (FAILED(hr)) { m_aMem.RemoveAll(); return hr; } m_nSchemaSize = *LPDWORD(pMem->GetData()); m_nHeaderSize = m_nSchemaSize + sizeof(DWORD); m_nHeaderSize = AtlAlignUp(m_nHeaderSize,16); } // open any new blocks CAtlFileMappingBase* pMem = m_aMem[m_aMem.GetCount()-1]; while (pMem) pMem = _OpenNextBlock(pMem); return S_OK; } inline HRESULT CPerfMon::_LoadMap(DWORD* pData) throw() { _ATLTRY { HRESULT hr; ClearMap(); 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; im_nNameId = dwNameId; pCategoryInfo->m_nHelpId = dwHelpId; } 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++; CategoryInfo* pCategoryInfo = _GetCategoryInfo(_GetNumCategories()-1); CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(pCategoryInfo->_GetNumCounters()-1); pCounterInfo->m_nNameId = dwNameId; pCounterInfo->m_nHelpId = dwHelpId; } } // fill in cache data ULONG* pnCounterBlockSize = NULL; // pointer to the object's counter block size for (DWORD i=0; i<_GetNumCategories(); i++) { CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); // align at 8 bytes per Q262335 pCategoryInfo->m_nCounterBlockSize = (ULONG) AtlAlignUp(sizeof(PERF_COUNTER_BLOCK), 8); pnCounterBlockSize = &pCategoryInfo->m_nCounterBlockSize; _FillCategoryType(pCategoryInfo); for (DWORD j=0; j_GetNumCounters(); j++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j); _FillCounterDef(pCounterInfo, pnCounterBlockSize); } // align at 8 bytes per Q262335 pCategoryInfo->m_nCounterBlockSize = (ULONG) AtlAlignUp(pCategoryInfo->m_nCounterBlockSize, 8); } 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*_GetNumCategoriesAndCounters()) * sizeof(DWORD); for (UINT i=0; i<_GetNumCategories(); i++) { // if any of the entries have names, they'd better all have names CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); if (!pCategoryInfo->m_strName.IsEmpty()) { nSize += sizeof(DWORD) + AtlAlignUp(sizeof(WCHAR) * pCategoryInfo->m_strName.GetLength(), sizeof(DWORD)); for (UINT j=0; j_GetNumCounters(); j++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j); nSize += sizeof(DWORD) + AtlAlignUp(sizeof(WCHAR) * pCounterInfo->m_strName.GetLength(), sizeof(DWORD)); } } } CHeapPtr blob; if (!blob.Allocate(nSize)) return E_OUTOFMEMORY; // start with blob size and number of items in the blob DWORD* pCurrent = reinterpret_cast(blob.m_pData); memset(pCurrent, 0, nSize); *pCurrent++ = (DWORD) nSize; // blob size *pCurrent++ = _GetNumCategoriesAndCounters(); // number of items #if _SECURE_ATL size_t nSizeLast = nSize; nSize -= 2 * sizeof(DWORD); if(nSize > nSizeLast) return E_FAIL; #endif for (UINT i=0; i<_GetNumCategories(); i++) { // add all the relevant runtime info to the blob for each item CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); *pCurrent++ = TRUE; // is object *pCurrent++ = pCategoryInfo->m_dwCategoryId; *pCurrent++ = pCategoryInfo->m_dwDetailLevel; *pCurrent++ = pCategoryInfo->m_nDefaultCounter; *pCurrent++ = pCategoryInfo->m_nInstanceLess; *pCurrent++ = pCategoryInfo->m_nStructSize; *pCurrent++ = pCategoryInfo->m_nMaxInstanceNameLen; *pCurrent++ = pCategoryInfo->m_nNameId; *pCurrent++ = pCategoryInfo->m_nHelpId; #if _SECURE_ATL nSizeLast = nSize; nSize -= 9 * sizeof(DWORD); if(nSize > nSizeLast) return E_FAIL; #endif for (UINT j=0; j_GetNumCounters(); j++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j); *pCurrent++ = FALSE; // is object *pCurrent++ = pCounterInfo->m_dwCounterId; *pCurrent++ = pCounterInfo->m_dwDetailLevel; *pCurrent++ = pCounterInfo->m_dwCounterType; *pCurrent++ = pCounterInfo->m_nMaxCounterSize; *pCurrent++ = pCounterInfo->m_nDataOffset; *pCurrent++ = pCounterInfo->m_nDefaultScale; *pCurrent++ = pCounterInfo->m_nNameId; *pCurrent++ = pCounterInfo->m_nHelpId; #if _SECURE_ATL nSizeLast = nSize; nSize -= 9 * sizeof(DWORD); if(nSize > nSizeLast) return E_FAIL; #endif } } // add names to the blob for (UINT i=0; i<_GetNumCategories(); i++) { CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); // 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 = pCategoryInfo->m_strName.GetLength(); *pCurrent++ = nLen; #if _SECURE_ATL nSizeLast = nSize; nSize -= sizeof(DWORD); if(nSize > nSizeLast) return E_FAIL; #endif Checked::memcpy_s(pCurrent, nSize, CT2CW(pCategoryInfo->m_strName), sizeof(WCHAR)*nLen); pCurrent += AtlAlignUp(sizeof(WCHAR) * nLen, sizeof(DWORD))/sizeof(DWORD); #if _SECURE_ATL nSizeLast = nSize; nSize -= sizeof(WCHAR)*nLen; if(nSize > nSizeLast) return E_FAIL; #endif for (UINT j=0; j_GetNumCounters(); j++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j); // copy the len of the string (in characters) then the wide-char version of the string // pad the string to a dword boundary int nCounterLen = pCounterInfo->m_strName.GetLength(); *pCurrent++ = nCounterLen; #if _SECURE_ATL nSizeLast = nSize; nSize -= sizeof(DWORD); if(nSize > nSizeLast) return E_FAIL; #endif Checked::memcpy_s(pCurrent, nSize, CT2CW(pCounterInfo->m_strName), sizeof(WCHAR)*nCounterLen); pCurrent += AtlAlignUp(sizeof(WCHAR) * nCounterLen, sizeof(DWORD))/sizeof(DWORD); #if _SECURE_ATL nSizeLast = nSize; nSize -= sizeof(WCHAR)*nCounterLen; if(nSize > nSizeLast) return E_FAIL; #endif } } 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 CPerfMon::CategoryInfo* CPerfMon::_FindCategoryInfo(DWORD dwCategoryId) throw() { for (UINT i=0; i<_GetNumCategories(); i++) { CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); if (pCategoryInfo->m_dwCategoryId == dwCategoryId) return pCategoryInfo; } return NULL; } inline CPerfMon::CounterInfo* CPerfMon::_FindCounterInfo(CategoryInfo* pCategoryInfo, DWORD dwCounterId) { ATLENSURE(pCategoryInfo != NULL); for (DWORD i=0; i_GetNumCounters(); i++) { CounterInfo* pCounter = pCategoryInfo->_GetCounterInfo(i); if (pCounter->m_dwCounterId == dwCounterId) return pCounter; } return NULL; } inline CPerfMon::CounterInfo* CPerfMon::_FindCounterInfo(DWORD dwCategoryId, DWORD dwCounterId) throw() { CategoryInfo* pCategoryInfo = _FindCategoryInfo(dwCategoryId); if (pCategoryInfo != NULL) return _FindCounterInfo(pCategoryInfo, dwCounterId); return NULL; } inline BOOL CPerfMon::_WantCategoryType(__in_z LPWSTR szValue, __in DWORD dwCategoryId) 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(dwCategoryId)) return TRUE; strNum = strList.Tokenize(_T(" "), nStart); } return FALSE; } inline LPBYTE CPerfMon::_AllocData(LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, size_t nBytesNeeded) { ATLENSURE(pnBytesUsed != NULL); ULONG newSize = *pnBytesUsed+static_cast(nBytesNeeded); if ((newSize < *pnBytesUsed) || (newSize < (ULONG) nBytesNeeded) || (nBytesAvail < newSize)) return NULL; LPBYTE p = pData; pData += nBytesNeeded; *pnBytesUsed += (ULONG) nBytesNeeded; return p; } inline DWORD& CPerfMon::_GetBlockId(CAtlFileMappingBase* pBlock) { ATLENSURE(pBlock != NULL); return *LPDWORD(LPBYTE(pBlock->GetData()) + m_nSchemaSize); } inline void CPerfMon::_FillCategoryType(CategoryInfo* pCategoryInfo) throw() { PERF_OBJECT_TYPE& type = pCategoryInfo->m_cache; type.DefinitionLength = sizeof(PERF_OBJECT_TYPE) + sizeof(PERF_COUNTER_DEFINITION) * pCategoryInfo->_GetNumCounters(); type.TotalByteLength = type.DefinitionLength; // we will add the instance definitions/counter blocks as we go type.HeaderLength = sizeof(PERF_OBJECT_TYPE); type.ObjectNameTitleIndex = pCategoryInfo->m_nNameId; type.ObjectNameTitle = NULL; type.ObjectHelpTitleIndex = pCategoryInfo->m_nHelpId; type.ObjectHelpTitle = NULL; type.DetailLevel = pCategoryInfo->m_dwDetailLevel; type.NumCounters = pCategoryInfo->_GetNumCounters(); type.DefaultCounter = pCategoryInfo->m_nDefaultCounter; if (pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES) type.NumInstances = PERF_NO_INSTANCES; else type.NumInstances = 0; // this will be calculated as objects are processed type.CodePage = 0; type.PerfTime.QuadPart = 0; QueryPerformanceFrequency (&(type.PerfFreq)); } inline void CPerfMon::_FillCounterDef(CounterInfo* pCounterInfo, ULONG* pnCounterBlockSize) throw() { PERF_COUNTER_DEFINITION& def = pCounterInfo->m_cache; def.ByteLength = sizeof(PERF_COUNTER_DEFINITION); def.CounterNameTitleIndex = pCounterInfo->m_nNameId; def.CounterNameTitle = NULL; def.CounterHelpTitleIndex = pCounterInfo->m_nHelpId; def.CounterHelpTitle = NULL; def.DefaultScale = pCounterInfo->m_nDefaultScale; def.DetailLevel = pCounterInfo->m_dwDetailLevel; def.CounterType = pCounterInfo->m_dwCounterType; DWORD dwAlignOfCounter=0; switch (pCounterInfo->m_dwCounterType & ATLPERF_SIZE_MASK) { case PERF_SIZE_DWORD: def.CounterSize = sizeof(DWORD); dwAlignOfCounter = sizeof(DWORD); break; case PERF_SIZE_LARGE: def.CounterSize = sizeof(__int64); dwAlignOfCounter = sizeof(__int64); break; case PERF_SIZE_ZERO: def.CounterSize = 0; dwAlignOfCounter = 0; break; case PERF_SIZE_VARIABLE_LEN: ATLASSERT((pCounterInfo->m_dwCounterType & ATLPERF_TYPE_MASK) == PERF_TYPE_TEXT); if ((pCounterInfo->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE) { def.CounterSize = (DWORD) AtlAlignUp(pCounterInfo->m_nMaxCounterSize * sizeof(WCHAR), sizeof(DWORD)); } else { def.CounterSize = (DWORD) AtlAlignUp(pCounterInfo->m_nMaxCounterSize * sizeof(char), sizeof(DWORD)); } break; } *pnCounterBlockSize = AtlAlignUp(*pnCounterBlockSize, dwAlignOfCounter); def.CounterOffset = *pnCounterBlockSize; *pnCounterBlockSize += def.CounterSize; } inline HRESULT CPerfMon::_CollectInstance( CategoryInfo* pCategoryInfo, LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, CPerfObject* _pInstance, PERF_OBJECT_TYPE* pObjectType, PERF_COUNTER_DEFINITION* pCounterDefs ) throw() { DWORD dwInstance = _pInstance->m_dwInstance; // grab a snapshot of the object USES_ATL_SAFE_ALLOCA; CPerfObject* pInstance = (CPerfObject*) _ATL_SAFE_ALLOCA(_pInstance->m_nAllocSize, _ATL_SAFE_ALLOCA_DEF_THRESHOLD); if (pInstance == NULL) { return E_OUTOFMEMORY; } Checked::memcpy_s(pInstance, _pInstance->m_nAllocSize, _pInstance, _pInstance->m_nAllocSize); // if it was changed or deleted between when we first saw it and when we copied // it, then forget about whatever happens to be there for this collection period if (pInstance->m_dwCategoryId != pCategoryInfo->m_dwCategoryId || dwInstance != pInstance->m_dwInstance || pInstance->m_nRefCount == 0) return S_OK; // we have a copy of something that claims to be the object type we're expecting // put it into the data blob PERF_INSTANCE_DEFINITION* pInstanceDef = NULL; if (pCategoryInfo->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); // align at 8 bytes per Q262335 ULONG nNameAlloc = (ULONG) AtlAlignUp(pInstanceDef->NameLength, 8); LPWSTR szInstNameDest = (LPWSTR) _AllocData(pData, nBytesAvail, pnBytesUsed, nNameAlloc); if (szInstNameDest == NULL) return E_OUTOFMEMORY; Checked::memcpy_s(szInstNameDest, nNameAlloc, szInstNameSrc, pInstanceDef->NameLength); pInstanceDef->NameOffset = ULONG(LPBYTE(szInstNameDest) - LPBYTE(pInstanceDef)); pInstanceDef->ByteLength = DWORD(sizeof(PERF_INSTANCE_DEFINITION) + nNameAlloc); } // create the counter block + data LPBYTE pCounterData = _AllocData(pData, nBytesAvail, pnBytesUsed, pCategoryInfo->m_nCounterBlockSize); if (pCounterData == NULL) return E_OUTOFMEMORY; // fill in the counter block header for the data PERF_COUNTER_BLOCK* pCounterBlock = (PERF_COUNTER_BLOCK*) pCounterData; pCounterBlock->ByteLength = pCategoryInfo->m_nCounterBlockSize; // fill in the data for (ULONG i=0; iNumCounters; i++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(i); PERF_COUNTER_DEFINITION& def = pCounterDefs[i]; LPBYTE pSrc = LPBYTE(pInstance)+pCounterInfo->m_nDataOffset; LPBYTE pDest = pCounterData+def.CounterOffset; switch (pCounterInfo->m_dwCounterType & ATLPERF_SIZE_MASK) { case PERF_SIZE_DWORD: *LPDWORD(pDest) = *LPDWORD(pSrc); break; case PERF_SIZE_LARGE: *(ULONGLONG*)(pDest) = *(ULONGLONG*)(pSrc); break; case PERF_SIZE_VARIABLE_LEN: if ((pCounterInfo->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE) { LPCWSTR szSrc = reinterpret_cast(pSrc); LPWSTR szDest = reinterpret_cast(pDest); size_t nLen = __min(wcslen(szSrc), pCounterInfo->m_nMaxCounterSize-1); Checked::wcsncpy_s(szDest, pCounterInfo->m_nMaxCounterSize-1, szSrc, nLen); szDest[nLen] = 0; } else { LPCSTR szSrc = reinterpret_cast(pSrc); LPSTR szDest = reinterpret_cast(pDest); size_t nLen = __min(strlen(szSrc), pCounterInfo->m_nMaxCounterSize-1); Checked::strncpy_s(szDest, pCounterInfo->m_nMaxCounterSize-1, szSrc, nLen); szDest[nLen] = 0; } break; } } if (pInstanceDef != NULL) pObjectType->TotalByteLength += pInstanceDef->ByteLength; pObjectType->TotalByteLength += pCounterBlock->ByteLength; return S_OK; } inline HRESULT CPerfMon::_CollectInstance( CategoryInfo* pCategoryInfo, LPBYTE& pData, ULONG nBytesAvail, ULONG* pnBytesUsed, PERF_OBJECT_TYPE* pObjectType, PERF_COUNTER_DEFINITION* pCounterDefs ) throw() { // specialization to collect an instanceless object with no instance data ATLASSERT(pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES); pObjectType->NumInstances = PERF_NO_INSTANCES; // create the counter block + data LPBYTE pCounterData = _AllocData(pData, nBytesAvail, pnBytesUsed, pCategoryInfo->m_nCounterBlockSize); if (pCounterData == NULL) return E_OUTOFMEMORY; // fill in the counter block header for the data PERF_COUNTER_BLOCK* pCounterBlock = (PERF_COUNTER_BLOCK*) pCounterData; pCounterBlock->ByteLength = pCategoryInfo->m_nCounterBlockSize; // fill in the data for (ULONG i=0; iNumCounters; i++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(i); PERF_COUNTER_DEFINITION& def = pCounterDefs[i]; LPBYTE pDest = pCounterData+def.CounterOffset; switch (pCounterInfo->m_dwCounterType & ATLPERF_SIZE_MASK) { case PERF_SIZE_DWORD: *LPDWORD(pDest) = 0; break; case PERF_SIZE_LARGE: *PULONGLONG(pDest) = 0; break; case PERF_SIZE_VARIABLE_LEN: if ((pCounterInfo->m_dwCounterType & ATLPERF_TEXT_MASK) == PERF_TEXT_UNICODE) memset(pDest, 0, pCounterInfo->m_nMaxCounterSize*sizeof(WCHAR)); else memset(pDest, 0, pCounterInfo->m_nMaxCounterSize*sizeof(CHAR)); break; } } pObjectType->TotalByteLength += pCounterBlock->ByteLength; return S_OK; } inline HRESULT CPerfMon::_CollectCategoryType( CategoryInfo* pCategoryInfo, LPBYTE pData, ULONG nBytesAvail, ULONG* pnBytesUsed ) throw() { ATLENSURE_RETURN(pCategoryInfo != NULL); ATLASSERT(pnBytesUsed != NULL); // write the object definition out PERF_OBJECT_TYPE* pObjectType = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_OBJECT_TYPE*) NULL); if (pObjectType == NULL) return E_OUTOFMEMORY; Checked::memcpy_s(pObjectType, sizeof(PERF_OBJECT_TYPE), &pCategoryInfo->m_cache, sizeof(PERF_OBJECT_TYPE)); // save a pointer to the first counter entry and counter definition. // we'll need them when we create the PERF_COUNTER_BLOCK data PERF_COUNTER_DEFINITION* pCounterDefs = reinterpret_cast(pData); // write the counter definitions out for (DWORD i=0; i_GetNumCounters(); i++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(i); PERF_COUNTER_DEFINITION* pCounterDef = _AllocStruct(pData, nBytesAvail, pnBytesUsed, (PERF_COUNTER_DEFINITION*) NULL); if (pCounterDef == NULL) return E_OUTOFMEMORY; Checked::memcpy_s(pCounterDef, sizeof(PERF_COUNTER_DEFINITION), &pCounterInfo->m_cache, sizeof(PERF_COUNTER_DEFINITION)); } // search for objects of the appropriate type and write out their instance/counter data bool bGotInstance = false; CAtlFileMappingBase* pCurrentBlock = _GetNextBlock(NULL); if (pCurrentBlock != NULL) { CPerfObject* pInstance = _GetFirstInstance(pCurrentBlock); while (pInstance && pInstance->m_nAllocSize != 0) { if (pInstance->m_dwCategoryId == pCategoryInfo->m_dwCategoryId) { bGotInstance = true; HRESULT hr = _CollectInstance(pCategoryInfo, pData, nBytesAvail, pnBytesUsed, pInstance, pObjectType, pCounterDefs); if (FAILED(hr)) return hr; } pInstance = _GetNextInstance(pInstance); if (pInstance->m_nAllocSize == (ULONG) -1) { pCurrentBlock = _GetNextBlock(pCurrentBlock); if (pCurrentBlock == NULL) pInstance = NULL; else pInstance = _GetFirstInstance(pCurrentBlock); } } } if (pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES && !bGotInstance) { // we have an instanceless (singleton) object with no data. send zeroed data HRESULT hr = _CollectInstance(pCategoryInfo, pData, nBytesAvail, pnBytesUsed, pObjectType, pCounterDefs); if (FAILED(hr)) return hr; } return S_OK; } inline DWORD CPerfMon::Open(LPWSTR szDeviceNames) throw() { (szDeviceNames); // unused return 0; } inline DWORD CPerfMon::Collect( __in_z LPWSTR szValue, __deref_inout_bcount(*pcbBytes) LPVOID* ppData, __inout LPDWORD pcbBytes, __inout LPDWORD pcObjectTypes ) throw() { _ATLTRY { if (FAILED(_OpenAllBlocks())) { *pcbBytes = 0; *pcObjectTypes = 0; return ERROR_SUCCESS; } LPBYTE pData = LPBYTE(*ppData); ULONG nBytesLeft = *pcbBytes; *pcbBytes = 0; if (_GetNumCategories() == 0) { // nothing is providing data. we need to load the map directly // from the registry in order to provide category/counter data CRegKey rkApp; DWORD dwErr; CString strAppKey; strAppKey.Format(c_szAtlPerfPerformanceKey, GetAppName()); dwErr = rkApp.Open(HKEY_LOCAL_MACHINE, strAppKey, KEY_READ); if (dwErr != ERROR_SUCCESS) { *pcbBytes = 0; *pcObjectTypes = 0; return ERROR_SUCCESS; } ULONG nBytes = 0; dwErr = rkApp.QueryBinaryValue(c_szAtlPerfMap, NULL, &nBytes); if (dwErr != ERROR_SUCCESS) { *pcbBytes = 0; *pcObjectTypes = 0; return ERROR_SUCCESS; } CHeapPtr buf; if (!buf.Allocate((nBytes+3)/4)) { *pcbBytes = 0; *pcObjectTypes = 0; return ERROR_SUCCESS; } dwErr = rkApp.QueryBinaryValue(c_szAtlPerfMap, buf, &nBytes); if (dwErr != ERROR_SUCCESS) { *pcbBytes = 0; *pcObjectTypes = 0; return ERROR_SUCCESS; } if (FAILED(_LoadMap(buf))) { *pcbBytes = 0; *pcObjectTypes = 0; return ERROR_SUCCESS; } } for (UINT i=0; i<_GetNumCategories(); i++) { CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); if (_WantCategoryType(szValue, pCategoryInfo->m_nNameId)) { ULONG nBytesUsed = 0; HRESULT hr = _CollectCategoryType(pCategoryInfo, pData, nBytesLeft, &nBytesUsed); if (hr == E_OUTOFMEMORY) { *pcbBytes = 0; *pcObjectTypes = 0; return ERROR_MORE_DATA; } else if (FAILED(hr)) { *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 #pragma warning (push) #pragma warning(disable : 4996) inline void CPerfMon::_AppendStrings( LPTSTR& pszNew, CAtlArray& 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; } } #pragma warning (pop) inline HRESULT CPerfMon::_AppendRegStrings( CRegKey& rkLang, LPCTSTR szValue, CAtlArray& 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) { #if _SECURE_ATL LPTSTR pszOld =pszNew; #endif _AppendStrings(pszNew, astrStrings, iFirstIndex); bNewStringsAdded = true; #if _SECURE_ATL ULONG nCharsNewLast = nCharsNew; nCharsNew -= ULONG(pszNew-pszOld); if(nCharsNew > nCharsNewLast) { return E_FAIL; } #endif } if (iIndex < iFirstIndex || iIndex > iLastIndex) { Checked::memmove_s(pszNew, nCharsNew, 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); DWORD nMaxLen = nChars*sizeof(TCHAR); 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) { Checked::memmove_s(pszWrite, nMaxLen , pszRead, nLen*sizeof(TCHAR)); #if _SECURE_ATL UINT nMaxLenLast = nMaxLen; nMaxLen -= nLen*sizeof(TCHAR); if(nMaxLen > nMaxLenLast) return E_FAIL; #endif 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; } DWORD nNumStrings = _GetNumCategoriesAndCounters(); 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 >= 2*nNumStrings && dwLastAppHelp-dwFirstAppHelp+2 >= 2*nNumStrings) { 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*nNumStrings; dwLastHelp += 2*nNumStrings; 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)){ hr = CreateMap(LANGIDFROMLCID(1033), 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; hr = _ReserveStringRange(dwFirstCounter, dwFirstHelp); if (FAILED(hr)) return hr; DWORD dwCurrentName = dwFirstCounter; DWORD dwCurrentHelp = dwFirstHelp; for (UINT i=0; i<_GetNumCategories(); i++) { CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); pCategoryInfo->m_nNameId = dwCurrentName; dwCurrentName += 2; pCategoryInfo->m_nHelpId = dwCurrentHelp; dwCurrentHelp += 2; for (UINT j=0; j_GetNumCounters(); j++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j); pCounterInfo->m_nNameId = dwCurrentName; dwCurrentName += 2; pCounterInfo->m_nHelpId = dwCurrentHelp; dwCurrentHelp += 2; } } // register the app entry points CRegKey rkApp; dwErr = rkApp.Create(HKEY_LOCAL_MACHINE, strAppKey); if (dwErr != ERROR_SUCCESS) return AtlHresultFromWin32(dwErr); _ATLTRY { DWORD dwFLen = GetModuleFileName(hDllInstance, CStrBuf(str, MAX_PATH), MAX_PATH); if( dwFLen == 0 ) return AtlHresultFromLastError(); else if( dwFLen == MAX_PATH ) return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); } _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; // if the dll is disabled, reenable it since we just reregistered it rkApp.DeleteValue(_T("Disable Performance Counters")); 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)) { //First try current thread locale language = LANGIDFROMLCID(GetThreadLocale()); wPrimaryLanguage = (LANGID) PRIMARYLANGID(language); } str.Format(c_szAtlPerfPerfLibLangKey, wPrimaryLanguage); dwErr = rkLang.Open(HKEY_LOCAL_MACHINE, str); if (dwErr == ERROR_FILE_NOT_FOUND) { // failed using current thread, so try default system lcid language = GetSystemDefaultLangID(); wPrimaryLanguage = (LANGID) PRIMARYLANGID(language); 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); hr = CreateMap(language, hResInstance); if (FAILED(hr)) return hr; // 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); ULONG nLangsBuffSize = dwLangsLen+4; CStrBuf szLangs(strLangs, nLangsBuffSize, 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]; #if _SECURE_ATL _sntprintf_s(szNewLang, _countof(szNewLang), _countof(szNewLang)-1, _T("%3.3x "), wPrimaryLanguage); #else _sntprintf(szNewLang, sizeof(szNewLang)/ sizeof(TCHAR), _T("%3.3x "), wPrimaryLanguage); #endif 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 astrCounters; CAtlArray astrHelp; ULONG nNewCounterSize = 0; ULONG nNewHelpSize = 0; for (UINT i=0; i<_GetNumCategories(); i++) { CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); astrCounters.Add(pCategoryInfo->m_strName); astrHelp.Add(pCategoryInfo->m_strHelp); for (UINT j=0; j_GetNumCounters(); j++) { CounterInfo* pCounterInfo = pCategoryInfo->_GetCounterInfo(j); astrCounters.Add(pCounterInfo->m_strName); astrHelp.Add(pCounterInfo->m_strHelp); } } for (size_t i=0; i* pLangs = reinterpret_cast*>(lParam); _ATLTRY { pLangs->Add(wIDLanguage); } _ATLCATCHALL() { return FALSE; } return TRUE; } inline HRESULT CPerfMon::RegisterAllStrings( HINSTANCE hResInstance /* = NULL */ ) throw() { HRESULT hrReturn = S_FALSE; HRESULT hr; UINT nRes; hr = CreateMap(0, hResInstance, &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 langs; if (!EnumResourceLanguages(hResInstance, RT_STRING, MAKEINTRESOURCE((nRes>>4)+1), EnumResLangProc, reinterpret_cast(&langs))) return AtlHresultFromLastError(); for (UINT i=0; i(self.GetPSID()), NULL, const_cast(dacl.GetPACL()), NULL) != ERROR_SUCCESS) return E_FAIL; } // now set up the dacl for creating shared memory segments and store it dacl.AddAllowedAce(Sids::Interactive(), GENERIC_READ); m_sd.SetDacl(dacl); // 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(LPDWORD(pMem->GetData())); if (FAILED(hr)) { m_aMem.RemoveAll(); return hr; } m_nSchemaSize = *LPDWORD(pMem->GetData()); m_nHeaderSize = m_nSchemaSize + sizeof(DWORD); m_nHeaderSize = AtlAlignUp(m_nHeaderSize,16); } 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 dwCategoryId, DWORD dwInstance, LPCWSTR szInstanceName, CPerfObject** ppInstance, bool bByName ) throw() { CPerfObject* pEmptyBlock = NULL; if (ppInstance == NULL) return E_POINTER; CAtlFileMappingBase* pCurrentBlock = _GetNextBlock(NULL); if (pCurrentBlock == NULL || pCurrentBlock->GetData() == NULL || m_lock.m_h == NULL) return E_UNEXPECTED; // Initialize must succeed before calling CreateInstance *ppInstance = NULL; CategoryInfo* pCategoryInfo = _FindCategoryInfo(dwCategoryId); if (pCategoryInfo == NULL) return E_INVALIDARG; if (szInstanceName == NULL && bByName) return E_INVALIDARG; if (pCategoryInfo->m_nInstanceLess == PERF_NO_INSTANCES && (dwInstance != 0 || szInstanceName != NULL)) return E_INVALIDARG; CPerfLock lock(this); if (FAILED(lock.GetStatus())) return lock.GetStatus(); CPerfObject* pInstance = _GetFirstInstance(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_dwCategoryId == dwCategoryId) { nMaxInstance = __max(nMaxInstance, pInstance->m_dwInstance); // check to see if we've found the one the caller wants if (!bByName && pInstance->m_dwInstance == dwInstance && (pCategoryInfo->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, pCategoryInfo->m_nMaxInstanceNameLen-1) == 0) { *ppInstance = pInstance; pInstance->m_nRefCount++; return S_OK; } } } if (pInstance->m_nAllocSize == pCategoryInfo->m_nAllocSize && pInstance->m_nRefCount == 0) pEmptyBlock = pInstance; pInstance = _GetNextInstance(pInstance); if (pInstance->m_nAllocSize == 0 && m_nHeaderSize + nUsedSpace + pCategoryInfo->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 = _GetFirstInstance(pCurrentBlock); } } // allocate a new object if (pEmptyBlock != NULL) pInstance = pEmptyBlock; else pInstance->m_nAllocSize = pCategoryInfo->m_nAllocSize; if (dwInstance == 0 && pCategoryInfo->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 (pCategoryInfo->m_nInstanceLess != PERF_NO_INSTANCES) { ULONG nNameLen = (ULONG)__min(wcslen(szInstanceName), pCategoryInfo->m_nMaxInstanceNameLen-1); ULONG nNameBytes = (nNameLen+1) * sizeof(WCHAR); pInstance->m_nInstanceNameOffset = pInstance->m_nAllocSize-nNameBytes; Checked::memcpy_s(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset, pInstance->m_nAllocSize-pInstance->m_nInstanceNameOffset, szInstanceName, nNameBytes); LPWSTR(LPBYTE(pInstance)+pInstance->m_nInstanceNameOffset)[nNameLen] = 0; } // copy the CategoryId last: it won't be collected until this is set pInstance->m_dwCategoryId = pCategoryInfo->m_dwCategoryId; *ppInstance = pInstance; return S_OK; } inline HRESULT CPerfMon::CreateInstance( DWORD dwCategoryId, DWORD dwInstance, LPCWSTR szInstanceName, CPerfObject** ppInstance ) throw() { return _CreateInstance(dwCategoryId, dwInstance, szInstanceName, ppInstance, false); } inline HRESULT CPerfMon::CreateInstanceByName( DWORD dwCategoryId, LPCWSTR szInstanceName, CPerfObject** ppInstance ) throw() { return _CreateInstance(dwCategoryId, 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_dwCategoryId = 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::AddCategoryDefinition( DWORD dwCategoryId, LPCTSTR szCategoryName, LPCTSTR szHelpString, DWORD dwDetailLevel, INT nDefaultCounter, BOOL bInstanceLess, UINT nStructSize, UINT nMaxInstanceNameLen) throw() { // must have one and only one of these ATLASSERT(!bInstanceLess ^ !nMaxInstanceNameLen); // get the things that can fail out of the way first CString strName; CString strHelp; _ATLTRY { strName = szCategoryName; strHelp = szHelpString; } _ATLCATCHALL() { return E_OUTOFMEMORY; } if (!m_categories.SetCount(m_categories.GetCount()+1)) { return E_OUTOFMEMORY; } // category has been added, set the data CategoryInfo* pCategoryInfo = _GetCategoryInfo(_GetNumCategories()-1); pCategoryInfo->m_dwCategoryId = dwCategoryId; pCategoryInfo->m_dwDetailLevel = dwDetailLevel; pCategoryInfo->m_nDefaultCounter = nDefaultCounter; pCategoryInfo->m_nInstanceLess = bInstanceLess ? PERF_NO_INSTANCES : 0; pCategoryInfo->m_nStructSize = nStructSize; pCategoryInfo->m_nMaxInstanceNameLen = nMaxInstanceNameLen; pCategoryInfo->m_nAllocSize = nStructSize + nMaxInstanceNameLen*sizeof(WCHAR); pCategoryInfo->m_strName = strName; pCategoryInfo->m_strHelp = strHelp; pCategoryInfo->m_nNameId = 0; pCategoryInfo->m_nHelpId = 0; return S_OK; } inline HRESULT CPerfMon::AddCounterDefinition( DWORD dwCounterId, LPCTSTR szCounterName, LPCTSTR szHelpString, DWORD dwDetailLevel, DWORD dwCounterType, ULONG nMaxCounterSize, UINT nOffset, INT nDefaultScale) throw() { // must add category BEFORE adding counter! ATLASSERT(_GetNumCategories() > 0); CounterInfo counter; counter.m_dwCounterId = dwCounterId; _ATLTRY { counter.m_strName = szCounterName; counter.m_strHelp = szHelpString; } _ATLCATCHALL() { return E_OUTOFMEMORY; } counter.m_dwDetailLevel = dwDetailLevel; counter.m_dwCounterType = dwCounterType; counter.m_nDefaultScale = nDefaultScale; counter.m_nMaxCounterSize = nMaxCounterSize; counter.m_nDataOffset = nOffset; counter.m_nNameId = 0; counter.m_nHelpId = 0; // add the counter to the category CategoryInfo* pCategoryInfo = _GetCategoryInfo(_GetNumCategories()-1); _ATLTRY { pCategoryInfo->m_counters.Add(counter); } _ATLCATCHALL() { return E_OUTOFMEMORY; } if (counter.m_nMaxCounterSize > 0) { ATLASSERT(counter.m_dwCounterType & PERF_TYPE_TEXT); pCategoryInfo->m_nAllocSize += counter.m_nMaxCounterSize * sizeof(WCHAR); } return S_OK; } inline HRESULT CPerfMon::RegisterCategory( WORD wLanguage, HINSTANCE hResInstance, UINT* pSampleRes, DWORD dwCategoryId, UINT nNameString, UINT nHelpString, DWORD dwDetail, BOOL bInstanceless, UINT nStructSize, UINT nMaxInstanceNameLen, INT nDefaultCounter) throw() { if (pSampleRes) *pSampleRes = nNameString; CString strName; CString strHelp; _ATLTRY { if (!strName.LoadString(hResInstance, nNameString, wLanguage) || !strHelp.LoadString(hResInstance, nHelpString, wLanguage)) { return E_FAIL; } } _ATLCATCHALL() { return E_OUTOFMEMORY; } return RegisterCategory( wLanguage, hResInstance, pSampleRes, dwCategoryId, strName, strHelp, dwDetail, bInstanceless, nStructSize, nMaxInstanceNameLen, nDefaultCounter); } inline HRESULT CPerfMon::RegisterCategory( WORD /* wLanguage */, HINSTANCE /* hResInstance */, UINT* /* pSampleRes */, DWORD dwCategoryId, LPCTSTR szNameString, LPCTSTR szHelpString, DWORD dwDetail, BOOL bInstanceless, UINT nStructSize, UINT nMaxInstanceNameLen, INT nDefaultCounter) throw() { return AddCategoryDefinition( dwCategoryId, szNameString, szHelpString, dwDetail, nDefaultCounter, bInstanceless, nStructSize, nMaxInstanceNameLen); } inline HRESULT CPerfMon::RegisterCounter( WORD wLanguage, HINSTANCE hResInstance, DWORD dwCounterId, UINT nNameString, UINT nHelpString, DWORD dwDetail, DWORD dwCounterType, ULONG nMaxCounterSize, UINT nOffset, INT nDefaultScale) throw() { CString strName; CString strHelp; _ATLTRY { if (!strName.LoadString(hResInstance, nNameString, wLanguage) || !strHelp.LoadString(hResInstance, nHelpString, wLanguage)) { return E_FAIL; } } _ATLCATCHALL() { return E_OUTOFMEMORY; } return RegisterCounter( wLanguage, hResInstance, dwCounterId, strName, strHelp, dwDetail, dwCounterType, nMaxCounterSize, nOffset, nDefaultScale); } inline HRESULT CPerfMon::RegisterCounter( WORD /* wLanguage */, HINSTANCE /* hResInstance */, DWORD dwCounterId, LPCTSTR szNameString, LPCTSTR szHelpString, DWORD dwDetail, DWORD dwCounterType, ULONG nMaxCounterSize, UINT nOffset, INT nDefaultScale) throw() { return AddCounterDefinition( dwCounterId, szNameString, szHelpString, dwDetail, dwCounterType, nMaxCounterSize, nOffset, nDefaultScale); } inline void CPerfMon::ClearMap() throw() { m_categories.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 = "\r\n\r\n"; hr = pStream->Write(strXML, strXML.GetLength(), &nLen); if (hr != S_OK) return hr; } strXML.Format("\t\r\n", CT2CA(GetAppName())); hr = pStream->Write(strXML, strXML.GetLength(), &nLen); for (UINT i=0; i<_GetNumCategories(); i++) { CategoryInfo* pCategoryInfo = _GetCategoryInfo(i); CAtlFileMappingBase *pCurrentBlock = _GetNextBlock(NULL); CPerfObject *pInstance = _GetFirstInstance(pCurrentBlock); strXML.Format("\t\t\r\n", pCategoryInfo->m_dwCategoryId, pCategoryInfo->m_nNameId, pCategoryInfo->m_nHelpId); hr = pStream->Write(strXML, strXML.GetLength(), &nLen); if (hr != S_OK) return E_FAIL; while (pInstance && pInstance->m_nAllocSize) { if (pInstance->m_dwCategoryId == pCategoryInfo->m_dwCategoryId) { if (pCategoryInfo->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 int nLength = AtlUnicodeToUTF8(wszInstNameSrc, nInstLen, NULL, 0); CHeapPtr szUTF8; if ((nLength < 0) || (nLength+1\r\n", szUTF8, pInstance->m_dwInstance); hr = pStream->Write(strXML, strXML.GetLength(), &nLen); if (hr != S_OK) return hr; } for (UINT j=0; j_GetNumCounters(); j++) { CounterInfo *pCounterInfo = pCategoryInfo->_GetCounterInfo(j); switch (pCounterInfo->m_dwCounterType & ATLPERF_SIZE_MASK) { case PERF_SIZE_DWORD: { strXML.Format("\t\t\t\t\r\n", *LPDWORD(LPBYTE(pInstance)+pCounterInfo->m_nDataOffset), pCounterInfo->m_nDataOffset); break; } case PERF_SIZE_LARGE: { strXML.Format("\t\t\t\t\r\n", *PULONGLONG(LPBYTE(pInstance)+pCounterInfo->m_nDataOffset), pCounterInfo->m_nDataOffset); break; } case PERF_SIZE_VARIABLE_LEN: { CHeapPtr szUTF8; LPBYTE pSrc = LPBYTE(pInstance)+pCounterInfo->m_nDataOffset; if ((pCounterInfo->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\r\n", szUTF8, pCounterInfo->m_nDataOffset); } else { ULONG nTextLen = (ULONG)strlen(LPCSTR(pSrc)); if (!szUTF8.Allocate(nTextLen+1)) return E_OUTOFMEMORY; Checked::strcpy_s(szUTF8, nTextLen+1, LPCSTR(pSrc)); strXML.Format("\t\t\t\t\r\n", szUTF8, pCounterInfo->m_nDataOffset); } break; } default: // error: return E_FAIL; } hr = pStream->Write(strXML, strXML.GetLength(), &nLen); if (hr != S_OK) return hr; } if (pCategoryInfo->m_nInstanceLess != PERF_NO_INSTANCES) { hr = pStream->Write("\t\t\t\r\n", sizeof("\t\t\t\r\n")-1, &nLen); if (hr != S_OK) return hr; } } pInstance = _GetNextInstance(pInstance); if (pInstance->m_nAllocSize == (ULONG)-1) { pCurrentBlock = _GetNextBlock(pCurrentBlock); if (pCurrentBlock == NULL) pInstance = NULL; else pInstance = _GetFirstInstance(pCurrentBlock); } } hr = pStream->Write("\t\t\r\n", sizeof("\t\t\r\n")-1, &nLen); if (hr != S_OK) return hr; } hr = pStream->Write("\t\r\n", sizeof("\t\r\n")-1, &nLen); if (hr != S_OK) return hr; if (hr == S_OK && bLast) hr = pStream->Write("", sizeof("")-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 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 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 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; // find the appropriate perfmon node CComPtr 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(CW2CT(bstrName), GetAppName())) break; } } } CComPtr 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 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); 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 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; // get the instance id bstrName.Empty(); hr = _GetAttribute(spChild, L"id", &bstrName); if (hr != S_OK) return S_FALSE; DWORD dwInstance = _wtoi(bstrName); // create the instance 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 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 { Checked::memcpy_s(LPBYTE(pInstance)+dwOffset, pInstance->m_nAllocSize-dwOffset, bstrName, bstrName.Length()*sizeof(WCHAR)); } CComPtr spCntrNext; hr = spCntrChild->get_nextSibling(&spCntrNext); spCntrChild.Attach(spCntrNext.Detach()); } CComPtr spInstNext; hr = spInstChild->get_nextSibling(&spInstNext); spInstChild.Attach(spInstNext.Detach()); } CComPtr 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() { ATLENSURE_RETURN(pNode != NULL); ATLASSERT(szAttrName != NULL); ATLENSURE_RETURN(pbstrVal != NULL); *pbstrVal = NULL; CComPtr spAttrs; HRESULT hr = pNode->get_attributes(&spAttrs); if (hr != S_OK) return hr; CComPtr 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 #if defined(_ATL_PERF_REGISTER) & !defined(_ATL_PERF_NOEXPORT) 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( ATLPERF_FUNCID_OPEN ), _T( ATLPERF_FUNCID_COLLECT ), _T( ATLPERF_FUNCID_CLOSE ), 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; } #endif // defined(_ATL_PERF_REGISTER) & !defined(_ATL_PERF_NOEXPORT) } // namespace ATL #pragma warning(pop) #endif // __ATLPERF_INL__