Not satisfied with the official way, I went looking for a simpler way. The Component Services Console displays the data so the information must be there somewhere. It is a waste of time and resources me to collect the data and calculate the call times when the information I want is already available.
As it turns out the unofficial way is a lot simpler to implement than the official way.
Step 1: include the comsvc.dll library
// ComSvcs library for internal com+ method call tracking
#import "c:\windows\system32\comsvcs.dll" exclude("IAppDomainHelper")
Step 2: get the statistics
- Get an instance of the internal com+ tracker: COMSVCSLib::IGetAppDataPtr ptrAppData(_T("{ecabafb9-7f19-11d2-978e-0000f8757e2a}"));
- Get a list of applications and lop through them: ptrAppData->GetApps(&nAppData, &appData);
- Get the application data you are interested in: calls per second, total calls, etc
- Get a list of classes in the application and loop on them, extracting the information you want: ptrAppData->GetAppData(oneApp.m_idApp, &nClsIDs, &aClsidData);
Example
This simple routine builds a XML string the brutal way with the com+ performance information (most error handling code removed for clarity)
/*
Get the statistics from the hidden COM+ interface
*/
void CTracker::getStatistics(BSTR *output)
{
const unsigned long MAX_APP_DATA = 500;
const unsigned long PUSH_RATE = 1000;
unsigned long nAppData = MAX_APP_DATA;
LPSTR lpString;
LPWSTR lpwString;
COMSVCSLib::appData *aAppData;
CString csStatistics = "";
// Get an instance of the internal com+ tracker objet
COMSVCSLib::IGetAppDataPtr ptrAppData(_T("{ecabafb9-7f19-11d2-978e-0000f8757e2a}"));
csStatistics.Append("\r\n");
// Step through the list of running application
ptrAppData->GetApps(&nAppData, &aAppData);
for (unsigned long idxApp=0; idxApp < nAppData; idxApp++)
{
unsigned long nClsIDs;
COMSVCSLib::CLSIDDATA *aClsidData;
COMSVCSLib::appData oneApp = aAppData[idxApp];
csStatistics.Append(_T("\r\n"));
UnicodeToAnsi(oneApp.m_szAppGuid, &lpString);
csStatistics.AppendFormat(_T("%s\r\n"), lpString);
CoTaskMemFree(lpString);
// Application information
csStatistics.AppendFormat(_T("%ld\r\n"), oneApp.m_idApp);
csStatistics.AppendFormat(_T("%ld\r\n"), oneApp.m_dwAppProcessId);
COMSVCSLib::APPSTATISTICS appStatistics = oneApp.m_AppStatistics;
csStatistics.AppendFormat(_T("\r\n"));
csStatistics.AppendFormat(_T("\t%ld\r\n"), appStatistics.m_cCallsPerSecond);
csStatistics.AppendFormat(_T("\t%ld\r\n"), appStatistics.m_cTotalCalls);
csStatistics.AppendFormat(_T("\t%ld\r\n"), appStatistics.m_cTotalClasses);
csStatistics.AppendFormat(_T("\t%ld\r\n"), appStatistics.m_cTotalInstances);
csStatistics.AppendFormat(_T("\r\n"));
// Get class information for this application
ptrAppData->GetAppData(oneApp.m_idApp, &nClsIDs, &aClsidData);
csStatistics.AppendFormat(_T("\r\n"));
for (unsigned long idxClass = 0 ; idxClass < nClsIDs; idxClass++)
{
COMSVCSLib::CLSIDDATA oneClass = aClsidData[idxClass];
csStatistics.AppendFormat(_T("\r\n"));
// Get the progID for the guid.
// This FAILS during recyling on Win2k3
if (!FAILED((ProgIDFromCLSID(oneClass.m_clsid,&lpwString))))
{
UnicodeToAnsi(lpwString, &lpString);
csStatistics.AppendFormat(_T("\t%s\r\n"), lpString);
CoTaskMemFree(lpString);
}
// Performance information
csStatistics.AppendFormat(_T("\t%ld\r\n"), oneClass.m_cBound);
csStatistics.AppendFormat(_T("\t%ld\r\n"), oneClass.m_cInCall);
csStatistics.AppendFormat(_T("\t%ld\r\n"), oneClass.m_cPooled);
csStatistics.AppendFormat(_T("\t%ld\r\n"), oneClass.m_cReferences);
csStatistics.AppendFormat(_T("\t%ld\r\n"), oneClass.m_dwRespTime);
csStatistics.AppendFormat(_T("\t%ld\r\n"), oneClass.m_cInCall);
csStatistics.AppendFormat(_T("\t%ld\r\n"), oneClass.m_cInCall);
csStatistics.AppendFormat(_T("\r\n"));
}
CoTaskMemFree(aClsidData);
csStatistics.AppendFormat(_T("\r\n"));
csStatistics.AppendFormat(_T("\r\n"));
}
csStatistics.Append("\r\n");
CoTaskMemFree(aAppData);
*output = csStatistics.AllocSysString();
}
That's it!
Less than a 100 lines of code to build a XML document with the call times for the running COM+ applications on the system.
This approach as several benefits over the official way:
- It is a lot simpler to implement and maintain (100 lines of C++ vs 1.500)
- I have found no memory leaks after 9 months in production
- Less resource usage. COM+ already maintains the counters, I just get them when I need them.
Keep in mind that this is an undocumented API so it may change one day in the future (Longhorn?).
Feel free to drop a few cents in the
tip jar if this post saved you time and money