
Introduction
This article provides system administrators with a tool to monitor COM+ applications (Remote and Local) the managed way.
Background
As a system administrator, I use the COM+ MMC on a daily basis looking for deadlocked or un-disposed components. After crawling the web for quite some time, I managed to find a small piece of C# code which helped me build this monitoring application. (The code was found in Egil Hogholt's blog, the C# version was added as a comment by Mark.)
Comonitor Architecture
The core of the application is the "COMSVCSLib.TrackerServer" object which returns info only for the local server. To overcome this problem, I decided to wrap the core code (using the TrackerServer object) in a WebService which will be installed on each of my servers. My client application uses SOAP to call the web service on a remote server (the remote server name is given as a parameter at runtime).
Understanding the ComonitorService
The ComonitorService has only one WebMethod ("GetErrors") which gets two uint parameters from the client:
- "
ResponseTimeLimit" - uint representing the response time (m\s) which the client considers as a deadlock.
- "
ObjectsLimit" - uint representing the number of un-disposed components which the client considers as an error.
[WebMethod]
[XmlInclude(typeof(Comonitor.OverLimitObject))]
public ArrayList GetErrors(uint ResponseTimeLimit,
uint ObjectsLimit)
"GetErrors" returns an ArrayList of ComonitorService.OverLimitObjects with all the problematic components.
public class OverLimitObject
{
public string PackageName;
public string ComponentName;
public uint PID;
public uint ResponseTime;
public uint Objects;
}
"OverLimitObject" contains all the necessary information received from the "COMSVCSLib.TrackerServer" object.
Using the "COMSVCSLib.TrackerServer"
The following code is the core of the Comonitor application. It uses the "COMSVCSLib.TrackerServer" object to query the COM Catalog for (only) the running components. For each component which corresponds to the given limit parameters, we create a "ComonitorService.OverLimitObject" containing the relevant data from the component's statistics.
ArrayList retVal = new ArrayList();
IntPtr clsIDDataPtr = IntPtr.Zero;
IntPtr appDataPtr = IntPtr.Zero;
COMSVCSLib.IGetAppData getAppData = null;
COMSVCSLib.TrackerServer comPlusTrackerType;
comPlusTrackerType = new COMSVCSLib.TrackerServerClass();
getAppData = (COMSVCSLib.IGetAppData)comPlusTrackerType;
uint appCount;
unsafe
{
getAppData.GetApps(out appCount, new IntPtr(&appDataPtr));
}
int appDataSize = Marshal.SizeOf(typeof(COMSVCSLib.appData));
for(int appIndex=0; appIndex<appCount; appIndex++)
{
COMSVCSLib.appData appData =
(COMSVCSLib.appData)Marshal.PtrToStructure(new
IntPtr(appDataPtr.ToInt32() + (appIndex * appDataSize)),
typeof(COMSVCSLib.appData));
uint nClsIDCount;
appDataPtr = new IntPtr();
unsafe
{
getAppData.GetAppData(appData.m_idApp, out nClsIDCount,
new IntPtr(&clsIDDataPtr));
}
int clsIDDataSize = Marshal.SizeOf(typeof(COMSVCSLib.CLSIDDATA));
for(int clsIDIndex=0; clsIDIndex<nClsIDCount; clsIDIndex++)
{
COMSVCSLib.CLSIDDATA clsIDData =
(COMSVCSLib.CLSIDDATA)Marshal.PtrToStructure(new
IntPtr(clsIDDataPtr.ToInt32() + (clsIDIndex * clsIDDataSize)),
typeof(COMSVCSLib.CLSIDDATA));
if ((clsIDData.m_cBound >= ObjectsLimit) ||
(clsIDData.m_dwRespTime >= ResponseTimeLimit))
{
OverLimitObject objOL = new
OverLimitObject(GetPackageNameByPID(appData.m_dwAppProcessId),
GetComponentNameByCLSID(clsIDData.m_clsid.ToString()),
appData.m_dwAppProcessId, clsIDData.m_dwRespTime,
clsIDData.m_cBound);
retVal.Add(objOL);
}
}
}
return retVal;
The Client
In this article, I also included a Windows client application which uses the "ComonitorService". The example client application calls the WebService on a specified computer every given time period (optional) and provides the sys admin with the capability to shut down a problematic package on the specified server. If there are COM+ packages which you don't want to be checked, such as: system application, IIS utilities etc., you can add the package names into the client's App.config inside the "NonErrorPackages" key.
<add key="NonErrorPackages" value="System Application,IIS Utilities" />