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.OverLimitObject
s 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" />