Monday, April 06, 2009

How to get COM+ call times from VB and VBScript

How to get COM+ call times from VB and VBScript
Posted on Tuesday, April 12, 2005 5:39 PM
Original Article : http://www.egilh.com/blog/archive/2005/04/12/655.aspx

I posted earlier on how to get the com+ call times the unofficial way. Someone asked for a VB example so I put together a com+ call time toolkit that contains the com+ call time tracker and a VBScript example client. You can do a lot of neat stuff with it:

* Use it a ASP page to monitor the call time of your components live
* Use Cacti to show how average call time changes during the day.
* Use the COM+ Admin API to automatically shut down/recycle components with high call times

Installation

* Download the egilh Com+ Tracker
* Extract the files
* Register the DLL in COM+ or run regsvr32 EgilhComTracker.dll in the directory where you extracted the files

Usage
The egilh.ComTracker object only has one method: string getStatistics()

It returns a xml formatted string that contains information about all the running applications. The XML has the following layout:
/applications
/application
/classes
/class

Example:





{98CDBB6E-3CAF-46F2-ABE6-EABECD6AA4EB}

10

1792



0

0

1

1







Test.Sleep.1

1

0

-1

1

120

0

0









Test client
The following VB code shows the call times for all classes registered in com+ on the local machine.

Dim oTracker 'As Object

Dim sResult 'As String

Dim oDOM 'As DOMDocument30

Dim oNode 'As MSXML2.IXMLDOMNode



'Get the com+ call times

Set oTracker = CreateObject("egilh.ComTracker")

sResult = oTracker.getStatistics()

Set oTracker = Nothing



'Display call times

Set oDOM = CreateObject("MSXML2.DomDocument.3.0")

oDOM.loadXML (sResult)

For Each oNode In oDOM.selectNodes("/applications/application/classes/class")

WScript.echo oNode.selectSingleNode("progID").Text & _

" call time: " & oNode.selectSingleNode("responseTime").Text

Next




Feel free to drop a few cents in the tip jar if this post saved you time and money

Feedback
# re: How to get COM+ call times from VB and VBScript
5/3/2005 7:52 PM by Brian Skiles

You rock. You totally rock. I've been looking all day for information on how to get the call time to COM+ components programatically, and you've created the perfect tool. This is the only place I've found any helpful information, and it went beyond helpful to the point that it does everything I need. Thank you very much for creating this component.


# re: How to get COM+ call times from VB and VBScript
5/3/2005 10:29 PM by Egil Hogholt

Glad you found what you were looking for!

Let me know if you have suggestions for improving the component or if you find any bugs.


# re: How to get COM+ call times from VB and VBScript
5/5/2005 5:03 PM by Brian Skiles

I have no suggestions, as the XML document is the perfect return value and it has all of the information I need. I've been using it for a couple of days and it's working perfectly. Thanks again!



# re: COM Call Timers: the official way
5/12/2005 9:41 AM by /egilh




# re: How to get COM+ call times from VB and VBScript
7/9/2005 11:25 PM by Renato

How can i get the application name from the guid??


# re: How to get COM+ call times from VB and VBScript
7/28/2005 9:27 PM by Egil Hogholt

Sorry for the delay in getting back to you. Work as been very busy after coming back from vacation.

I never faced the problem before as the COMAdminCatalog APIs I use for recycling etc accept the GUID -OR- the application name.

You can use the following VBSCript to get the application name from the GUID:
Dim Catalog
Set Catalog = CreateObject("COMAdmin.COMAdminCatalog")

' Get list of COM+ applications
Set Applications = Catalog.GetCollection("Applications")
Applications.Populate

'List all GUIDs/Names
For Each AppObject In Applications
WScript.Echo AppObject.Key & "=" & AppObject.Name
Next


# re: How to get COM+ call times from VB and VBScript
8/4/2005 5:14 PM by David

Is It possible execute vb script from a host and get COM+ parameters from another host in the same domain?

Thanks, your tracker is good.


# re: How to get COM+ call times from VB and VBScript
8/5/2005 9:31 AM by Egil Hogholt

Everything is possible!

If you use VB instead of VBScript you can do it very easily. Just pass the optional "ServerName" argument. For example:
Set oTracker = CreateObject("egilh.ComTracker", "Server1")


The WScript.CreateObject() method does not accept a machine name so it is more difficult. If you only have one remote machine, you can do this:
- right click on the com+ package with egilh.ComTracker and choose "Export…"
- choose "Application proxy"
- install the generated proxy application on you machine
Note that this only works for -one- remote machine

There are other workarounds as well:
- Create an ASP page on each machine that calls egilh.ComTracker. Call the asp file from you pc using msxml and parse the output
- Schedule a script to run on each machine that writes to a file (or db). You can then read the generated data via the network
- Copy a vbscript file on each machine and use WSHController.CreateScript("cmd line", "machine name") to run the script remotely from your PC.

The simplest solution is to write a small application in VB6 or another "serious" language that allows you to create remote objects on the machine you specify



# re: How to get COM+ call times from VB and VBScript
8/9/2005 10:32 AM by David

I use VBScript to run Comtracker so I choose your second option: create a proxy application on client host to get call times.

When I access to Com+ application on client console I do'nt see any call to the components when in the sever host they are running. In client console call parameters are empty and components seem no run when I'm sure that they are been called in server host. On client host, "egilh.ComTracker" parses only components under "System Application" group that are running but doesn't give information about components in "my application".

I know there are other solutions (you show them in last post) but I prefer do'nt write on any file or database.

Thanks for your attention.


# re: How to get COM+ call times from VB and VBScript
8/9/2005 11:33 AM by Egil Hogholt

It seems like a configuration issue.

The comtracker must be installed in a COM+ "server application" on the server:
- Right click comtracker on the server in Component Services
- In the "Activation" tab. Select the "Server application" option as the "Activation type"
- In the Identity tab. Make sure it does not use the "interactive user" but one of the following:
Win2k3: System account, Local Service
Win2k: Use a valid domain user and password

Can you please double check that you created an "Application Proxy" and not a standard install package?
- Right click the comtracker on your PC
- Go to the "Activation" tab
- Verify that the "Remote Server name" contains the name of the server



# Blog anniversary
8/22/2005 11:06 PM by /egilh




# re: How to get COM+ call times from VB and VBScript
10/10/2005 6:48 PM by jtmoney

is it possible to automatically restart a component if it's call time reaches a certain value?


# re: How to get COM+ call times from VB and VBScript
10/11/2005 9:42 AM by Egil Hogholt

There is no built in way in Windows/COM+ to do this but you can do it with the comtracker:
- use egilh.comtracker to get the call times
- use the COM+ Admin APIs to shut down or recycle applications with high call times

The COM+ Admin API can be used from an NT service that polls the comtracker or you can schedule a VB Script to run every X minutes.

I will post an article later today on how to do this.


# How to shut down COM applications with high call times with VB and VBScript
10/11/2005 11:06 AM by /egilh




# re: How to get COM+ call times from VB and VBScript
10/12/2005 2:26 PM by John Ludring

When I run it, it says "getStatistics not supported by object". Any ideas?


# re: How to get COM+ call times from VB and VBScript
10/12/2005 2:44 PM by John Ludring

When i try to register your .dll, it just goes into a loop and tries to continueously register it, any ideas?


# re: How to get COM+ call times from VB and VBScript
10/12/2005 3:22 PM by John Ludring

Can you post the source code for the .dll?


# re: How to get COM+ call times from VB and VBScript
10/12/2005 3:56 PM by Egil Hogholt

John; which Operating System are you using and how did you register the comtracker?

Let me know if the following steps work for you:
- Add a new Application with the "Component Services" console:
- Name: Comtracker
- Type: Server Application
- User: "Local Service" on Win2k3. Specify a user + password on Win2k
- Add egilhComtracker.dll to the new application

The complete source code for the DLL is not available. All the info you need can be found in this post: http://www.egilh.com/blog/archive/2005/02/25/557.aspx


# re: How to get COM+ call times from VB and VBScript
11/15/2005 10:04 PM by dio

Hi Egil,
After looking and looking and looking all over the web I finally found your blog (10x god!!!)
Great code !!!

(I am using the C# code which was given in one of the comments...)
Is there a way to create AppData of a remote server ? (using c# and without installing the dll on the remote server)

again - thank you for your code (and to your friend in microsoft...hhh)




# re: How to get COM+ call times from VB and VBScript
11/15/2005 10:28 PM by Egil Hogholt

Hi Dio, I am glad you found the code useful!

You have to install the DLL (my pre-compiled DLL in this post or the .NET version) on -each- machine where you want to collect the data. You can then call the component from a central data collection machine using DCOM or other methods for calling remote components. The DLL talks to the local COM+ environment to get the call time information so it -must- run on the machine where you collect the data.

I am afraid I have to disappoint a second time: I -wish- someone in Microsoft gave me this information as it would have saved me a lot of hard work. I spent a lot of time hunting in the SDKs and the undocumented entry points in the com+ DLLs before I found what I needed to get the call time information.


# re: How to get COM+ call times from VB and VBScript
12/30/2005 3:47 PM by Erickson

Great!!! I search this information for a week. Thank you! But, I tried to use your function in a console application compiled in Visual C++ 6.0/Windows XP SP2 and whenever I run application I receive the message "abnormal program termination". Is There anything I want to configure in Visual C++ 6.0, any Dll to import?

Thank you again.


# re: How to get COM+ call times from VB and VBScript
12/31/2005 10:25 AM by Egil Hogholt

Does the VB Script example above work? If it works, there is a problem in your client program. I will send you a mail offline to see if we can resolve the problem.


# re: How to get COM+ call times from VB and VBScript
2/1/2006 5:35 PM by Egil Hogholt

Problem solved; the code was missing CoInitialize() which is required when calling COM+ objects like the COM+ Tracker


# re: COM Call Timers: the official way
2/1/2006 6:40 PM by /egilh




# egilhComTracker: Free component to track com call times with
2/2/2006 8:23 PM by /egilh




# re: How to get COM+ call times from VB and VBScript
2/20/2006 6:05 AM by BD

hi there i not that good with scripting but could someone show me how to get call times for specific Program ID or method calls.


# re: How to get COM+ call times from VB and VBScript
2/20/2006 8:32 AM by Egil Hogholt

You can only get call times per object, not for the individual methods in that object.

The com+ tracker returns information about all running applications. You can use the script in the post above and modify the loop like this to filter a particular prog id:

For Each oNode In oDOM.selectNodes("/applications/application/classes/class")
If "my.progid" = LCase(oNode.selectSingleNode("progID").Text) then
WScript.echo oNode.selectSingleNode("progID").Text & _
" call time: " & oNode.selectSingleNode("responseTime").Text
End If
Next


# re: How to get COM+ call times from VB and VBScript
2/22/2006 11:56 AM by BD

Thank you so much that was what i was after, not familiar with XML and didn't know the syntaxes to parse out a particular program's call time.

Once again thank you


# re: How to get COM+ call times from VB and VBScript
2/22/2006 11:31 PM by BD

Sorry to bother again, could you be able to provide an example vb script to only retrieve data from a particular application. Just say if Application GUID = xxxxxxx-xxxx-xxxx-xxxxx-xx-xxxx then get all all the response times for each component of that application.



thanks
BD



# re: How to get COM+ call times from VB and VBScript
2/23/2006 9:55 PM by Egil Hogholt

Even simpler.

If you only want -one- application, you can modify the loop from this:
For Each oNode In oDOM.selectNodes("/applications/application/classes/class")

To this:
For Each oNode In oDOM.selectNodes("/applications/application[@guid='{xxxxxxx-xxxx-xxxx-xxxxx-xx-xxxx}']/classes/class")

NB! XML is case sensitive so you must spell the GUID correctly.



# re: How to get COM+ call times from VB and VBScript
2/24/2006 12:40 AM by BD

Cool thanks heaps, didn't really know how to put in the correct xpath better start learning some XML basics. thanks again


# re: How to get COM+ call times from VB and VBScript
5/30/2006 1:26 PM by Igor

Hi Egil,
first, Thank's for You for very good and useful tool,
and, my question:
may this tool (egilhCOMTracker.dll) be cause for: routinely (1-2 times per day),
com+ statistics from mmc (component services) not show..
com+ applications are runing (our user are working ..:-),
but statistics is not show..
Restart system application lead to correct this problem (on some time..)
Our system Win2003EE (withot SP1) and we are runing egilhCOMTracker.vbs
sufficiently frequently (1 time per 5 seconds - for our com+ monitoring system)
For Win2000 machines we are not have similar problems..
May be a com+ 1.5 bug ?
Yours sincerely,
Igor Matukin, Russia.



# re: How to get COM+ call times from VB and VBScript
5/30/2006 10:16 PM by Egil Hogholt

I have not seen this problem before myself but I will check and let you know ASAP.


# re: How to get COM+ call times from VB and VBScript
5/31/2006 8:26 AM by Igor

Hi Egil,
Thank's, we are download and install for test environment
Windows Server 2003 Post-Service Pack 1 COM+ 1.5 Hotfix Rollup Package 8
(http://support.microsoft.com/?kbid=912818
http://download.microsoft.com/download/a/a/b/aab2fc8c-7fb1-4b6a-a4ce-5a77f174af57/WindowsServer2003-KB912818-v3-x86-ENU.exe)
In KB 910730 (Hotfix Rollup Package 7) see .. FIX: Statistics may not display correctly for some COM+ applications on a computer that is running Windows Server 2003..
FIX: A COM+ application stops responding, and desktop icons and the taskbar may not appear as expected on a computer that is running Windows Server 2003..
..end etc.
Yours sincerely,
Igor Matukin, Russia.


# re: How to get COM+ call times from VB and VBScript
5/31/2006 9:58 AM by Egil Hogholt

Looks like it should fix your problem. Please let me know if it works or not.

Thanks,
Egil


# re: How to get COM+ call times from VB and VBScript
8/15/2006 3:49 PM by Andy Jones

Is there a way of amending the app to get the call times from another system? Ie, run it on my local machine but get the values of another system on the same network?


# re: How to get COM+ call times from VB and VBScript
8/16/2006 12:18 PM by Egil Hogholt

You can collect remote data with the existing ComTracker. First you have to install the ComTracker on all the machines you want to collect data from. Then you can use DCOM to create the object on the remote machine(s).

The syntax depends on which programming language you use. With VB, you can write it like this:
Set oTracker = CreateObject("egilh.ComTracker", "Server1")

The object then runs and returns data from the remote machine.


Let me know if that is an acceptable solution for you or if I should extend the egilh.ComTracker with a getRemoteStatistics("ServerName") method.



# re: How to get COM+ call times from VB and VBScript
8/22/2006 10:54 AM by Ardian


I am trying tu use your component from a remote server (Set oTracker = CreateObject("egilh.ComTracker", "Server1")
) but this call doesn't work. Any idea to help me?
thank you.


# re: How to get COM+ call times from VB and VBScript
8/22/2006 11:39 AM by Egil Hogholt

What error message do you get?


# re: How to get COM+ call times from VB and VBScript
8/22/2006 1:02 PM by Ardian


The error is:
test.vbs(7, 1) Microsoft VBScript runtime error: ActiveX component can't create object: 'Egilh.ComTracker'



# re: How to get COM+ call times from VB and VBScript
8/22/2006 1:50 PM by Egil Hogholt

Did you register the egilh.ComTracker component on both machines?

The component will run on the remote machine but it must be installed locally as well so DCOM knows which methods it implements.



# re: How to get COM+ call times from VB and VBScript
8/22/2006 2:04 PM by Ardian


yes the component is registered in both machines, and dcom is enabled. My account is administrator on both machines. Locally all is working fine on both machines.


# re: How to get COM+ call times from VB and VBScript
8/22/2006 3:42 PM by Egil Hogholt

Hm...
Some more questions to narrow down the problem
- are you able to launch other components registered on the remote machine?
- is the component registered in "Component services" or did you register it with regsrv32?
- if it is in component services; does the application hosting the component start when you call it from the remote machine?






# re: How to get COM+ call times from VB and VBScript
8/22/2006 4:30 PM by Ardian


-yes i can create other objects in the remote server.
-the component is registered with regsvr32 command.
-i tried to call it under the "component services", the application hosting the component doesn't started.
-i am using win 2000 servers for my test.
-error in the event viewer:
DCOM got error "Class not registered " from the computer server1 when attempting to activate the server:
{8D7E7FDC-4D16-48DF-BDC9-B2C0493E36BF}


# re: How to get COM+ call times from VB and VBScript
8/22/2006 5:19 PM by Egil Hogholt

It looks like the component is registered with two different class IDs which is strange since it is the same DLL.

Can you look in the registry on -both- machines to see which component uses the class ID {8D7E7FDC-4D16-48DF-BDC9-B2C0493E36BF}?
You should find it under HKEY_CLASSES_ROOT\CLSID\

It could be a registration problem; i.e. it is registered in "component services" on the client but not on the server. If so, the following steps on both machines should fix it:
- remove the component from "component services"
- run regsvr32 /u egilhComTracker.dll
- run regsvr32 egilhComTracker.dll

If you want to use "component services" you have to follow the steps earlier in this post and generate an "application proxy".


# re: How to get COM+ call times from VB and VBScript
9/6/2006 4:41 PM by cyboman

Hey, thanks for doing this. I noticed that on my system (Win2000 server) when I regsvr32 the dll I get

"LoadLibrary("egilhComTracker.dll") failed - Access is denied."

I tried it on another system and it works though.

Also, when I load it in to com services on my system I get.

"The DLL could not be loaded. Check to make sure all required application runtime files and other dependent DLLs are available in the component DLL's directory or the system path."

Any idea. Thanks.





# re: How to get COM+ call times from VB and VBScript
9/6/2006 10:49 PM by Egil Hogholt

The first error sounds like a security error. Can you please verify that you have administrator rights on the Win2k machine where you try to register the component?

The problem could also be that some of the the C++ run time dlls required by the component is missing. Can you please check if you have the following files in \windows\system32 directory?
atl71.dll
msvcr71.dll
msvcp71.dll



# re: How to get COM+ call times from VB and VBScript
9/15/2006 10:20 PM by cyboman

Thanks for responding. I do have those files and I am in the administrator group on this computer. I should also mention that I don't have this problem with the DLL files I compile with VB. I was able to get this dll to register on another computer however. My computer isn't exactly in great health (I have to reboot it every day) so maybe I will just develop my solution on a different machine. Thanks.

COM+ Call Timers: the unofficial way

COM+ Call Timers: the unofficial way
Posted on Friday, February 25, 2005 5:32 PM
Original Article at : http://www.egilh.com/blog/archive/2005/02/25/557.aspx
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);
o Get the application data you are interested in: calls per second, total calls, etc
o 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 < oneapp =" aAppData[idxApp];" appstatistics =" oneApp.m_AppStatistics;">GetAppData(oneApp.m_idApp, &nClsIDs, &aClsidData);

csStatistics.AppendFormat(_T("\r\n"));

for (unsigned long idxClass = 0 ; idxClass < oneclass =" aClsidData[idxClass];" output =" csStatistics.AllocSysString();">
/// Summary description for ComMonitor.
///
public class ComMonitor
{
static public void GetData()
{
// Get an instance of the internal com+ tracker objet
Type comPlusTrackerType = Type.GetTypeFromCLSID(new System.Guid("{ecabafb9-7f19-11d2-978e-0000f8757e2a}"));
COMSVCSLib.IGetAppData getAppData = (COMSVCSLib.IGetAppData)Activator.CreateInstance(comPlusTrackerType);

// Step through the list of running application
uint appCount;
IntPtr appDataPtr = new IntPtr();
unsafe
{
getAppData.GetApps(out appCount, new IntPtr(&appDataPtr));
}
int appDataSize = Marshal.SizeOf(typeof(COMSVCSLib.appData));
for(int appIndex=0; appIndex> 8);
}
string s= System.Text.UnicodeEncoding.Unicode.GetString(buf);
return s;
}
}
}


# re: COM+ Call Timers: the unofficial way
6/3/2005 4:09 PM by Mark

Whoops, forgot to Release the getAppData COM object. Add this at the end:

Marshal.ReleaseComObject(getAppData);


# re: COM+ Call Timers: the unofficial way
6/6/2005 9:45 AM by Egil Hogholt

Thanks for sharing the C# version!



# re: COM+ Call Timers: the unofficial way
7/8/2005 11:50 PM by Renato

Im trying to compile this c# source usingo this command line:

csc /r:\WINNT\system32\comsvcs.dll test.cs

But im getting this error:

fatal error CS0009: Metadata file 'c:\WINNT\system32\comsvcs.dll' could not be
opened -- 'There isn't metadata in the memory or stream'

does anyone could help me?


# re: COM+ Call Timers: the unofficial way
7/26/2005 8:48 AM by Muhammad Rizwan

Egil Hogholt u seems to be an angel for us. You have solved my assignment. You are great.

Thanks,

Rizwan


# re: Renato problem
7/26/2005 9:05 AM by Muhammad Rizwan

Renato ,
First of all you have to you use Microsoft "TlbImp.exe" tool to create "Interop.COMSVCSLib.dll" file from "comsvcs.dll" file.

Then u can compile with Visual Studio .NET 2003 Command Prompt like that:

csc /unsafe /reference:Interop.COMSVCSLib.dll /out:TestExe.exe test.cs

I hope it helps you clarify ur thoughts.

Regards,
Rizwan
Ultimus Inc.


# re: COM+ Call Timers: the unofficial way
7/26/2005 9:55 PM by Egil Hogholt

Thanks Rizwan for sharing the solution to the C# issue.

I am up to my neck in work after my vacation (http://www.egilh.com/blog/archive/2005/07/17/1171.aspx) so I still have to catch up with all the comments and request for help.


# re: COM+ Call Timers: the unofficial way
9/22/2005 8:29 PM by Renato

Thanks, Muhammad... i tried to add a reference to COMSVCSLib with visual studio .net and it worked fine... and im using the toguether with COMAdminCatalog to get the application names...

Buuuuttt now there is a new question: im getting a few counters, like response time, with 4294967295 (full usigned long).... whats it mean? can you help me?

thanks...

Renato


# re: COM+ Call Timers: the unofficial way
9/23/2005 10:18 AM by Egil Hogholt

Can you post the entire XML fragment of the counter?

4294967295 is the unsigned value of -1 which I believe is an error value. Do you always see the value 4294967295 for the same counter or do you only see it once in a while, for example when a package is being recycled?


# How to shut down COM applications with high call times with VB and VBScript
10/11/2005 11:06 AM by /egilh




# How to recycle com applications with high call times using .NET
10/11/2005 12:06 PM by /egilh




# re: How to get COM call times from VB and VBScript
10/12/2005 4:56 PM by /egilh




# How to track down performance problems in COM applications
10/26/2005 4:41 PM by /egilh




# re: COM+ Call Timers: the unofficial way
12/12/2005 11:02 PM by Iddo

After reading this great page I decided to make a little tool based on the C# source code.
I posted the entire solution and a little article at CodeProject web site.
"Comonitor - A COM+ Monitor" - http://www.codeproject.com/csharp/ComonitorProject.asp
Thank you Egil for your great code which inspired me to create this tool.

Iddo


# re: COM+ Call Timers: the unofficial way
12/13/2005 7:26 PM by Egil Hogholt

Way cool!

The following posts show how to recycle or shut down applications for those that want to implement their own version:
- VBScript: http://www.egilh.com/blog/archive/2005/10/11/1328.aspx
- .NET: http://www.egilh.com/blog/archive/2005/10/11/1331.aspx


# egilhComTracker: Free component to track com call times with
2/2/2006 8:23 PM by /egilh




# egilhComTracker: Free component to track com call times with
2/3/2006 8:43 AM by /egilh




# re: COM+ Call Timers: the unofficial way
2/6/2006 10:45 PM by Igor

First, I want to thank Egil and Mark for sharing their great code. Mark's C# code is almost what I need.
I want to monitor call times on a series of remote servers where COM+ applications are installed. I found a few suggestions on the related thread ("How to get COM+ call times from VB and VBScript"), but I don't feel any of those would work for me. What I need is some way to connect to a remote server without doing anything on the server itself. After all, COM+ explorer is capable of connecting to remote servers and getting all information that you can see there for your local computer. When you use ComAdmin classes and want to connect to a remote server, one line of C#

new ComAdmin.ComAdminCatalog().Connect(servername);

(after you added a reference to comadmin.dll) brings you there. I expect to have something similar in comcvcs.dll, but I cannot find it.

Thank you for any suggestion,
Igor


# re: COM+ Call Timers: the unofficial way
2/7/2006 3:58 PM by Egil Hogholt

Comsvcs is a COM object so you can call it on a remote machine using DCOM. Pass the server name you want to reach as an argument to GetTypeFromProgID() or GetTypeFromCLSID()

Modify the C# code above like this:
Type comPlusTrackerType = Type.GetTypeFromCLSID(
new System.Guid("{ecabafb9-7f19-11d2-978e-0000f8757e2a}"),
"myserver");



# re: COM+ Call Timers: the unofficial way
2/8/2006 1:54 AM by Igor

Thanks a lot, Egil. That was fast and to the point, as simple as I hoped, and, the main thing, perfectly working as expected.

This is what I finally got. I took Mark's C## code and made the following changes:
- I added Main function that gets the server name from the command line, which should now be [ server=]. If server parameter is omitted, the local computer is implied. Otherwise, the application tries to connect to the remote computer.
- I added some exception handling that would most likely come into play when the server name is misspelled.
- Using COMAdmin objects, I got the names of the applications and the progIDs of the components. That makes it much handier to browse the output.
- I had to make a light fix to GetString method and a few other lines to make them work for me.

To compile the code, you need to allow, as Mark indicated, unsafe code and to add references to the 2 COM objects: comsvcs.dll and comadmin.dll.

using System;
using System.Runtime.InteropServices;
using COMSVCSLib;
using COMAdmin;

namespace ptdpc
{
public class ComMonitor
{
static public void GetData(string server)
{
try
{
// Get an instance of the internal com+ tracker object
Type comPlusTrackerType = Type.GetTypeFromCLSID(new System.Guid(
"{ecabafb9-7f19-11d2-978e-0000f8757e2a}"),server);
IGetAppData getAppData = (IGetAppData)Activator.CreateInstance(comPlusTrackerType);

ICOMAdminCatalog catalog = (ICOMAdminCatalog)new COMAdminCatalogClass();

if (server != null)
{
catalog.Connect(server);
}
ICatalogCollection apps = (ICatalogCollection)catalog.GetCollection("Applications");
apps.Populate();

// Step through the list of running application
uint appCount;
IntPtr appDataPtr = new IntPtr();
unsafe
{
getAppData.GetApps(out appCount, new IntPtr(&appDataPtr));
}
int appDataSize = Marshal.SizeOf(typeof(COMSVCSLib.appData));
for(int appIndex=0; appIndex> 8);
}
return System.Text.UnicodeEncoding.Unicode.GetString(buf).TrimEnd('\0');
}

[STAThread]
static void Main(string[] args)
{
const string prefix = "server=";
string server = null;
foreach (string arg in args)
{
if (arg.StartsWith(prefix))
{
server = arg.Substring(prefix.Length);
break;
}
}
GetData(server);
}

static ICatalogObject FindCatalogObjectByValue(ICatalogCollection coll, string name, string val)
{
ICatalogObject iObj = null;
foreach (ICatalogObject obj in coll)
{
if (obj.get_Value(name).ToString() == val)
{
iObj = obj;
break;
}
}
return iObj;
}
}
}



# re: COM+ Call Timers: the unofficial way
2/8/2006 7:21 AM by Egil Hogholt

Cool.

Thanks for sharing the updated code.


# re: COM+ Call Timers: the unofficial way
3/14/2006 5:32 PM by David

This is great info. Exactly what I was looking for. Does anyone know of any issues using the COMPlusTrackerType on Windows 2000 Server? I have used ComAdmin.dll and had to import the type library separately for windows 2000 vs. 2003.

Any help is greatly appreciated.


# re: COM+ Call Timers: the unofficial way
3/14/2006 8:08 PM by Egil Hogholt

I have not tested the code on Win2k. Let me know if you have problems, and I will test it on a Win2k virtual machine.


# re: COM+ Call Timers: the unofficial way
3/14/2006 8:59 PM by David

Hi Egil. Thanks for the reply.

After submitting my post I went ahead and tested it on Win2k. XP & 2003 work with no issues but when attempting to run on Win2k I'm getting the following error "Value cannot be null. Parameter name: type"






# re: COM+ Call Timers: the unofficial way
3/15/2006 8:00 PM by Egil Hogholt

Hi David.
I was at a conference today so I did not have a chance to test it on Win2k. I will try it on Win2k tomorrow to see if the GUID has changed or if it is not supported.

The official way [http://www.egilh.com/blog/archive/2005/02/24/552.aspx] works on Windows 2000 for sure. The problems I encountered with the official approach could be bugs in my code or due to the extreme load on the machines.



# re: COM+ Call Timers: the unofficial way
3/15/2006 10:20 PM by David

Thanks Egil. Just to make sure you are aware, I used the c# code under .Net framework 1.1 on all 3 platforms.


# re: COM+ Call Timers: the unofficial way
3/16/2006 10:42 AM by Egil Hogholt

I tried the egilhComTracker [http://www.egilh.com/blog/articles/ComTracker.aspx] on Win2k (SP4) and it works like a charm as I use the old com interfaces that are win2k compatible.

I used the code by Igor as a base and did the following:
- copied the following files from a Win2k system to my Win2k3 dev machine to a win2k directory; winnt\system32\com\comadmin.dll + winnt\system32\comsvcs.dll
- ran tlbimp win2k\comsvcs.dll
- ran tlbimp win2k\comadmin.dll
- added a reference to the generated COMAdmin.dll and COMSVCSLib.dll in my Visual Studio 2003 project
- build
- deploy the program, COMAdmin.dll and COMSVCSLib.dll

The same program works fine on Win2k and Win2k3. The interop type libraries generated from win2k work fine on win2k3 but not the other way around.

Please let me know if it works or not for you. Leave your e-mail address in the contact form http://www.egilh.com/blog/contact.aspx if you have problems making it work.


# re: COM Call Timers: the official way
3/21/2006 4:05 PM by /egilh




# re: COM+ Call Timers: the unofficial way
6/14/2006 1:21 PM by Filippo

Great code, thanks for sharing.
I ran the original code and all I get is a list (incomplete) of the components in the "System applications" application.

ptrAppData->GetApps(&nAppData, &aAppData) return just that. I thought about a permissioning issue but using COMAdmin in C# lists all my applications...

Any hint ?

Thanks again for sharing.


# re: COM+ Call Timers: the unofficial way
6/14/2006 1:52 PM by Egil Hogholt

The code only returns the objects that are active. COMAdmin on the other hand returns all configured applications.


# re: COM+ Call Timers: the unofficial way
6/14/2006 3:19 PM by Filippo

Ah, I see... Thanks for replying me.

Active means at least a component in the application is running I guess. I want to check for components that breaks and have an high call time, but I guess if the parent application is not there it means that any component is not running hence it's ok so far....

Great code again !


# re: COM+ Call Timers: the unofficial way
2/26/2007 7:03 PM by leeh

This is good stuff. I'm new to COM, but I was able to get the C++ code working on my local computer. Now I'd like to get it working remotely, similar to the way the COM+ MMC lets you see apps on other servers. I believe this may be easier to do using the C# example, but I need something that I can call from VB6. Any suggestions?

Many Thanks!


# re: COM+ Call Timers: the unofficial way
2/26/2007 9:12 PM by Egil Hogholt

You can easily collect data from remote servers in VB6 by:
- installing the free com+ tracker [http://www.egilh.com/blog/articles/ComTracker.aspx] on the server machines
- collecting data centrally by calling the object on the remote machine: Set oTracker = CreateObject("egilh.ComTracker", "Server1")

The com+ tracker runs on, and returns data from, the remote machine.


# re: COM+ Call Timers: the unofficial way
3/6/2007 9:20 AM by nat

Thanks for this great solution.

I have this scenario:
I've a Com+ application made by a lot of com objects. I'd like to track the shutdown of this application, particularly I'd like to log the call time of the objects before the shutdown, so I'm thinking about the IComAppEvents implementation, in particulary the OnApp(forced)shutdown, in wich I'm thinking to invoke the getStatistics of ComTracker. I've read about COM+ event, but I've not understood how to apply it to the IComAppEvents. Any suggestion?


# re: COM+ Call Timers: the unofficial way
3/6/2007 4:21 PM by Anil

Hi Egilh... what an useful stuff... thanx a lot....

I've deployed it as windows application... I wraped this code into a function which returns me an arraylist with all the COM+ stats in local/remote machine...it works fine as windows app... but the problem occured when I tried to make it as C# class library... when I tried to build its throwing following error...

"Assembly generation failed -- Referenced assembly 'Interop.COMSVCSLib' does not have a strong name"

can u please help me on this...




# re: COM+ Call Timers: the unofficial way
3/6/2007 8:16 PM by Egil Hogholt

Hi Anil.
You must sign the COM interop assembly for COMSVCSLib.

This Microsoft KB article explains how to do it in C# and VB.NET:
http://support.microsoft.com/kb/313666



# re: COM+ Call Timers: the unofficial way
3/6/2007 8:25 PM by Egil Hogholt

Hi nat.
Your approach would give you the call times at the exact moment when the application is shut down. But it is fairly complex to do unless you are a skilled C++ programmer and you are familiar with Loosely Coupled Events. This post [http://www.egilh.com/blog/archive/2005/02/24/552.aspx] gives an introduction to the topic. Combined with the COM+ Spy example in the Platform SDK you should be able to do what you want.

I suggest a simpler approach unless you need the exact call times; call the getStatistics method every X seconds to get a snapshot of the situation. The data will not be 100% accurate but it should be good enough for most uses.



# re: COM+ Call Timers: the unofficial way
4/16/2007 10:21 AM by Anil

Hi Egil,
thnx a lot for ur previous reply 'n sry for delayed response.

I have this scenario:

I have implemented this (getting COM statistics) as a COM+ component (a .net class library-C#, registered to COM+) along with few other methods to get the list of all configured COM+ applications on a remote server and to start & stop selected COM+ application. 'm calling these methods from a
coldfusion (.cfm) page, by creating an instance of that component.

Everything worked fine when I deployed it on XP.
But when I deployed it on Windows 2003 server, the object count of this component kept on increasing on every method call and never reduced till I did shut down / restart COM+ application.

I'm releasing every object / comobject 'm creating in the class (COM+ component)
using marshal.releasecomobject() in finally block of each method, I even tried forcing GC in the finally block.

'm releasing instance of the component in the coldfusion page also.

it is COM+ 1.5 'm using, there is version difference in Component Services of XP machine (Version: 03.00.00.4414) and Windows 2003 machine (Version: 2001.12.4720.1830).

'm not able figure out the cause for this increase in object count.
Is there anything that holds the reference of these com objects, which has to be taken care of??

'n one more thing I have to mention here,
I've created interops (RCW) for COMSVCS.dll and COMADMIN.dll using "tlbimp" and 've put these in GAC,
other wise coldfusion is unable to execute those methods.

can u please help me on this..........



# re: COM+ Call Timers: the unofficial way
4/16/2007 11:28 AM by Egil Hogholt

If I have understood correctly; coldfusion calls your component registered in COM+ and the object count of this component keeps rising.

Try to add these two calls in the finally block of the methods registered in COM+:
ContextUtil.DeactivateOnReturn = true;
ContextUtil.SetComplete();

It tells COM+ that your component is done so it can deactivate your component.



# re: COM+ Call Timers: the unofficial way
5/14/2007 9:07 PM by Pete

Hi !

I was looking over Mark's code and I think that would be what I need... but I need it in VB.NET, do you think the convertion is possible ? I've been trying to translate it, and the bug I'm at is on that line :

Dim comPlusTrackerType As Type
comPlusTrackerType = Type.GetTypeFromCLSID(New System.Guid("{ecabafb9-7f19-11d2-978e-0000f8757e2a}"))

Dim getAppData As COMSVCSLib.IGetAppData
getAppData = CType(Activator.CreateInstance(comPlusTrackerType), COMSVCSLib.IGetAppData)

Dim appCount As System.UInt32
Dim appDataPtr As IntPtr

Dim aAppData As IntPtr
aAppData = IntPtr.Zero
==> getAppData.GetApps(appCount, aAppData)

I get the following error message :

An unhandled exception of type 'System.Runtime.InteropServices.COMException' occurred in comtrackervbnet.dll

Additional information: A null reference pointer was passed to the stub.



can you help me out ? I don't quite get how those objects work ...







# re: COM+ Call Timers: the unofficial way
5/17/2007 9:01 PM by Pete

I think I found out what's wrong (there are a lot of things but...) the main problem is on that part

Dim appDataPtr As IntPtr
Dim pAppData() As comsvcslib.CAppData
Dim aAppData As New IntPtr
Dim gh As GCHandle = GCHandle.Alloc(aAppData, GCHandleType.Pinned)
Dim AddrOfaAppData As IntPtr = gh.AddrOfPinnedObject()

getAppData.GetApps(appCount, AddrOfaAppData)


I got it into the debugger, and ... well... normally the value of aAppData should be modified after the GetApps call, but it is not, and it stays 0 .... so basically the Marshalling is done with a pointer that starts at 0 in the memory and Marshal.PtrToStructure is converting "garbagelike" memory to the apps/classes, that's why I don't get any info ...

any idea on how to modify it to work ? (at least I saved you the trouble of finding this problem.... ahah)


# re: COM+ Call Timers: the unofficial way
5/18/2007 9:29 AM by Egil Hogholt

This code works fine in C#
unsafe
{
IntPtr intPtrAppDataPtr = new IntPtr(&appDataPtr);
getAppData.GetApps(out appCount, intPtrAppDataPtr);
}

The VB-like code doesn't work in C# either:
GCHandle gh = GCHandle.Alloc(appDataPtr, GCHandleType.Pinned);
IntPtr intPtrAppDataPtr = gh.AddrOfPinnedObject();
getAppData.GetApps(out appCount, intPtrAppDataPtr);

I will have a look this weekend and let you know what I find


# re: COM+ Call Timers: the unofficial way
5/18/2007 12:05 PM by Egil Hogholt

Hi Pete.
I think I found the problem:
Dim appCount As System.UInt32
Dim appDataPtr As New IntPtr

Dim gh As GCHandle = GCHandle.Alloc(appDataPtr, GCHandleType.Pinned)
Dim intPtrAppDataPtr As IntPtr = gh.AddrOfPinnedObject()
getAppData.GetApps(appCount, intPtrAppDataPtr)

'This is the magic line: it reads that data back from the pinned pointer
appDataPtr = New IntPtr(Marshal.ReadInt32(intPtrAppDataPtr))




# re: COM+ Call Timers: the unofficial way
5/18/2007 2:24 PM by Pete

whoa, thanks Egil ! you're a god !
I found some other mistakes, but now it works completely (herE's the full code below) I commented out some of the stats I don't need to reduce the string size, you can always uncomment them !

Imports System
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.EnterpriseServices
Imports interop

'
' Summary description for ComMonitor.
'


Public Class ComMonitor : Inherits ServicedComponent

Public Function GetData() As String

' Get an instance of the internal com+ tracker objet
Dim comPlusTrackerType As Type
Dim getAppData As comsvcslib.IGetAppData
Dim appCount As System.UInt32
Dim appDataPtr As IntPtr
Dim pAppData() As comsvcslib.CAppData
Dim aAppData As New IntPtr
Dim appDataSize As Integer
Dim csStatistics As StringBuilder
Dim appIndex As Integer
csStatistics = New StringBuilder

Dim gh As GCHandle = GCHandle.Alloc(aAppData, GCHandleType.Pinned)
Dim AddrOfaAppData As IntPtr = gh.AddrOfPinnedObject()

comPlusTrackerType = Type.GetTypeFromCLSID(New System.Guid("{ecabafb9-7f19-11d2-978e-0000f8757e2a}"))
getAppData = CType(Activator.CreateInstance(comPlusTrackerType), comsvcslib.IGetAppData)
getAppData.GetApps(appCount, AddrOfaAppData)
aAppData = New IntPtr(Marshal.ReadInt32(AddrOfaAppData))
appDataSize = Marshal.SizeOf(GetType(comsvcslib.appData))

csStatistics.Append("")

For appIndex = 0 To System.Convert.ToInt64(appCount)

Dim appData As comsvcslib.appData

appData = Marshal.PtrToStructure(New IntPtr(aAppData.ToInt32() + System.Convert.ToInt32((appIndex * appDataSize))), GetType(comsvcslib.appData))

' Application information
csStatistics.Append("")
csStatistics.Append("" & GetPackageNameByPID(appData.m_dwAppProcessId) & "")
'csStatistics.Append("" + GetString(appData.m_szAppGuid) + "")
csStatistics.Append("" & System.Convert.ToInt64(appData.m_idApp) & "")
csStatistics.Append("" & System.Convert.ToInt64(appData.m_dwAppProcessId) & "")

'csStatistics.Append("")
'csStatistics.Append("" + appData.m_AppStatistics.m_cCallsPerSecond.ToString() + "")
'csStatistics.Append("" + appData.m_AppStatistics.m_cTotalCalls.ToString() + "")
'csStatistics.Append("" + appData.m_AppStatistics.m_cTotalClasses.ToString() + "")
'csStatistics.Append("" + appData.m_AppStatistics.m_cTotalInstances.ToString() + "")
'csStatistics.Append("
")

Dim nClsIDCount As UInt32
Dim clsIDDataPtr As IntPtr
Dim gh1 As GCHandle = GCHandle.Alloc(clsIDDataPtr, GCHandleType.Pinned)
Dim AddrOfclsIDDataPtr As IntPtr = gh.AddrOfPinnedObject()

getAppData.GetAppData(appData.m_idApp, nClsIDCount, AddrOfclsIDDataPtr)
clsIDDataPtr = New IntPtr(Marshal.ReadInt32(AddrOfclsIDDataPtr))

Dim clsIDDataSize As Integer

clsIDDataSize = Marshal.SizeOf(GetType(comsvcslib.CLSIDDATA))

csStatistics.Append("")

Dim clsIDIndex As Integer
For clsIDIndex = 0 To System.Convert.ToInt64(nClsIDCount) - 1

Dim clsIDData As comsvcslib.CLSIDDATA

clsIDData = Marshal.PtrToStructure(New IntPtr(clsIDDataPtr.ToInt64() + (clsIDIndex * clsIDDataSize)), GetType(comsvcslib.CLSIDDATA))

csStatistics.Append("")
csStatistics.Append("" & GetComponentNameByCLSID(clsIDData.m_clsid.ToString()) & "")
csStatistics.Append("" & clsIDData.m_clsid.ToString() & "")
'csStatistics.Append("" & clsIDData.m_cBound.ToString() & "")
'csStatistics.Append("" + clsIDData.m_cInCall.ToString() + "")
'csStatistics.Append("" + clsIDData.m_cPooled.ToString() + "")
'csStatistics.Append("" + clsIDData.m_cReferences.ToString() + "")
csStatistics.Append("" & System.Convert.ToInt64(clsIDData.m_dwRespTime) & "")
'csStatistics.Append("" + clsIDData.m_cCallsCompleted.ToString() + "")
'csStatistics.Append("" + clsIDData.m_cCallsFailed.ToString() + "")
csStatistics.Append("
")
Next

csStatistics.Append("
")
csStatistics.Append("
")
Marshal.FreeCoTaskMem(clsIDDataPtr)
gh1.Free()
Next
csStatistics.Append("
")
Marshal.FreeCoTaskMem(aAppData)
Marshal.ReleaseComObject(getAppData)
gh.Free()

Return csStatistics.ToString()
End Function
Function GetPackageNameByPID(ByVal PID As UInt32) As String

Dim retVal As String
Dim GrpObj As comsvcslib.MtsGrp
Dim dcomType As Type
Dim dcomObj As Object
Dim obj As Object
Dim eventObj As comsvcslib.COMEvents
Dim i As Integer
retVal = ""
GrpObj = Nothing
dcomType = Type.GetTypeFromProgID("mts.MtsGrp")
dcomObj = Activator.CreateInstance(dcomType)
GrpObj = DirectCast(dcomObj, comsvcslib.MtsGrp)
obj = Nothing
eventObj = Nothing
For i = 0 To GrpObj.Count - 1

GrpObj.Item(i, obj)
eventObj = DirectCast(obj, comsvcslib.COMEvents)
If (eventObj.GetProcessID() = System.Convert.ToInt64(PID)) Then
retVal = eventObj.PackageName
GetPackageNameByPID = retVal
End If
Marshal.ReleaseComObject(obj)
obj = Nothing
Marshal.ReleaseComObject(eventObj)
eventObj = Nothing
Next

Marshal.ReleaseComObject(dcomObj)
dcomObj = Nothing
GetPackageNameByPID = "UNKNOWN"

End Function

Function GetComponentNameByCLSID(ByVal CLSID As String) As String
Dim compName As String
Dim RK As Microsoft.Win32.RegistryKey
Dim oRegValue As Object
compName = ""
RK = Microsoft.Win32.Registry.ClassesRoot
RK = RK.OpenSubKey("CLSID\\{" + CLSID + "}\\ProgID")
oRegValue = RK.GetValue("")
If oRegValue = Nothing Then
compName = "UNKNOWN"
Else
compName = oRegValue.ToString()
End If
Return compName
End Function

Function GetString(ByVal arr() As UInt16) As String
Dim buf(arr.Length * 2) As Byte
Dim i As Integer
Dim s As String
Dim pos As Integer
For i = 0 To arr.Length - 1
buf(i * 2) = CByte(Convert.ToInt32(arr(i)) And 255)
buf(i * 2 + 1) = CByte(System.Convert.ToInt64(arr(i)) >> 8)
Next
s = System.Text.UnicodeEncoding.Unicode.GetString(buf)
pos = s.IndexOf("\0", 1, s.Length - 1)
If (pos > 0) Then
s = s.Remove(pos, s.Length - pos)
End If
GetString = s
End Function
End Class


You are a life saver !

thanks a lot again !


# re: COM+ Call Timers: the unofficial way
5/18/2007 2:31 PM by Pete

PS : I don't know if you can, but I would delete the post I did with the code that doesn't work, just so no one gets fooled... hehe


# re: COM+ Call Timers: the unofficial way
5/18/2007 3:43 PM by Egil Hogholt

I have deleted the old VB.NET code that didn't work.

Thanks for sharing the code.


# re: COM+ Call Timers: the unofficial way
2/13/2008 4:34 PM by Pete

Hello Egil !

it's been quite a while since I posted, but I was wondering something about the code of this function....

What components does it check exactly ?

Does it check all the components and packages under the COM+ Applications folder in the Component Services window ?

Or is it only then ones under the System Applications package ?

Or is it only the components that there are data available ?

I'm having people that want to know the package name of the classes that are getting call times over a set threshold... so I need to change the DLL I think... do I ?




# re: COM+ Call Timers: the unofficial way
2/13/2008 7:46 PM by Egil Hogholt

Hi Pete.
I will do my best to answer your questions below. Please let me know if I forgot something.

The Com+ tracker checks all the RUNNING applications under the COM+ Applications folder in the Component Services window. So you may see few/no data if you run it on a test machine.

There is no need to modify the DLL to get the package name. The DLL returns the GUID of the application which you can use with the ComAdmin to do all sorts of interesting stuff like automatically shutting down the component.

VBScript example: http://www.egilh.com/blog/archive/2005/10/11/1328.aspx
.NET example: http://www.egilh.com/blog/archive/2005/10/11/1331.aspx



# re: COM+ Call Timers: the unofficial way
4/9/2008 7:35 PM by Creez1

Are there definitions for the counters: Bound, Calls Completed, Calls Failed, In Call, Pooled, References, Response Times. Especially Bound and References. I am being tasked with monitoring our COM+ apps and I'm entirely clear what each counter is and where to establish thrsholds.


# re: COM+ Call Timers: the unofficial way
6/16/2008 2:59 AM by Noppadon

Hi,

I have a problem on getting com+ information on remote computer (UATSERVER1) using this command.

Set oTracker = CreateObject("egilh.ComTracker", "UATSERVER1")

It shows the following error.

Script: C:\Backup\Script\test.vbs
Line: 8
Char: 1
Error: ActiveX component can't create object: 'Egilh.ComTracker'
Code: 800A01AD
Source: Microsoft VBScript runtime error

I've already registered the egilhComTracker.dll on both client machine and on server (UATSERVER1). Calling the above command at the server itself is OK but failed on calling from other computer. Please help.

Best Regards,
Noppadon S.


# re: COM+ Call Timers: the unofficial way
6/27/2008 10:43 AM by Egil Hogholt

A couple of questions:
- which operating systems are you using on the machines
- how did you register the component?

My guess is that you cannot create the object because of security settings. DCOM settings are stricter in Win2k3 than Win2k for example so everything works locally but you encounter problems when you try to create an object remotely



# re: COM+ Call Timers: the unofficial way
6/30/2008 3:14 AM by Noppadon

Hi,

- The server (UATSERVER1) is Windows 2000 Advanced Server and the client trying to call the component from server is Windows XP. (creating object locally at server is OK but creating server's object remotely from client is failed)

- I'm using regsvr32 D:\script\egilhComTracker.dll command to register the component.

Which security setting should I check?

Thank you very much.


# re: COM+ Call Timers: the unofficial way
6/30/2008 2:19 PM by Egil Hogholt

You can review the security settings by running dcomcnfg.exe on the server.

I don't have access to a win2k machine, but if I remember correctly there are "Access Permissions" and "Launch and Activation Permissions.

The account that creates the component on the XP machine must have the rights to launch and access the component on the server.


# re: COM+ Call Timers: the unofficial way
7/22/2008 10:32 PM by chandresh

Hi,

I am looking for something in VB, also looking for to monitor on the remote system, without installing anything on the server.

Is this possible, or are there any ways to do the same.

Eagerly waiting for the reply.

If anyone know do forward me the way.

Thanks,
Chandresh


# re: COM+ Call Timers: the unofficial way
7/23/2008 8:29 AM by Egil Hogholt

You can do this in VB.NET but not in older versions of VB. Just create the comsvcslib.IGetAppData object on the remote machine and you should be OK.

I can update the com tracker to support getting data from remote machines if you think it would be useful.


# re: COM+ Call Timers: the unofficial way
7/23/2008 2:05 PM by chandresh

Thanks for your reply,

I am looking for something, but not too sure how to begin,
I wanted to have the entire configuration information of the com & dcom component & a way to restore them back.

I am not sure if someone is changing the same.

Is this possible to track and record them.

I am new to the field of programming.

Thanks once again for all your help and support.


# re: COM+ Call Timers: the unofficial way
7/23/2008 3:13 PM by Egil Hogholt

Does this help: http://msdn.microsoft.com/en-us/library/ms680546(VS.85).aspx

It lets you export the compoent with all the settings so you can install it later on the same machine or a different machine.

This article explains how you can do it programatically: http://msdn.microsoft.com/en-us/library/ms688248.aspx


# How to get calltime property of component (Com+ 1.5) ? keyongtech
1/22/2009 5:47 AM by Pingback/TrackBack

How to get calltime property of component (Com+ 1.5) ? keyongtech


VT's Zone

COM+ Call Timers: the official way
Posted on Thursday, February 24, 2005 9:15 PM
As the title suggests, there are more than one way to get access to the COM+ call timers. This post gives and overview of how to get the call times using the official Microsoft COM+ Instrumentation APIs.
Why mess with the COM+ call timers?
I had a serious problem with components accessing an Oracle DB using OLEDB. In certain cases we queries never ended. Query timeouts are not supported in the Oracle OLEDB nor Microsoft OLEDB provider and changing the resource constraints in Oracle did not improve the situation. If the query went in tilt, it stayed in tilt -forever-. That is a bad, bad, thing in a system with hundreds of calls per second.. We tracked down the problem to a specific case: the Oracle stored procedure never returned if the stored procedure "header" was OK but the stored procedure "body" was invalid. The stored procedure call waited forever for the the stored procedure to get recompiled. In normal situation it recompiled by itself but there were cases with linked DBs etc where the Oracle stored procedure body stayed invalid until someone recompiled it by hand.
We tried asynchronous queries but they did not fix the situation. If the query was stuck, it stayed stuck and the abort method hung as well. We found one way that did work: use a separate thread for the query and kill the thread if it took to much time. The query did not timeout anymore but the memory leaks were enormous when we brutally terminated the thread.
The only fix I found was to implement COM+ recycling on steroids. Monitor the call times and shut down (recycle on Win2k3) the com+ package if the call times got too high. The stored procedure was still broken but the rest of the system continued working. No memory leak problems either as the com+ process was shut down in an orderly manner.
How to get the com+ call timers - the official way
The Component Services Console displays all sort of useful information like the number of objects in call, average call time etc. But, there is no official way to get the com+ call timers directly. You have to do the dirty work yourself and calculate the average call times.
The COM+ Spy example in the \Samples\Com\Administration\Spy in the Platform SDK shows how to get all the COM+ information you desire. Below I show the basic steps but please download the Platform SDK for a complete working example.
The COM+ internal information is made available via Loosely Coupled Events. You subscribe to one or more COM+ events and implement the corresponding interface to get notified. There are a whole set of COM+ Instrumentation Interfaces. The important ones for COM+ call tracking are:
• IComAppEvents: OnAppActivation, OnAppShutdown, OnAppForceShutdown
• IComMethodEvents : OnMethodCall, OnMethodReturn, OnMethodException
They allow you to monitor COM+ application activation/shutdown and method call/return and provide you with all you need to calculate the call time. I used COM+ Spy as a skeleton and choose the following approach to tracking the call times:
• Implement a tracking object (CMethodMon in the example below) for each com+ application you want to monitor. The class:
o Implements the IComMethodsEvent interface to track call times
o Implements the IComAppEvents interface to clear the current calls when an application shuts down
o Subscribes to the IComMethodsEvent and IComAppEvents
o Has one timer that fires every X seconds (configurable). The timer:
? Calculates the average call time (highestCalltimes below)
? Take appropriate action if the call times are too high (shut down component etc)

IComMethodsEvents
This is THE interface for call time tracking. It is called when a method is called or returns and when an exception occurs. You get a lot of information in the COMSVCSEVENTINFO structure like the process ID, current time stamp etc.
Keeping track is quite simple:
• Create a stack for each activate object (OID stack):
• Push call information on the OID stack in OnMethodCall. In my case I only care about the call time so I only push the performance counter
• Pop from the OID stack in OnMethodReturn
// Stack definition (only contains the time stamp as that's all I care about in this app)
typedef map TimeMap;
imeMap m_map;

STDMETHODIMP CMethodMon::OnMethodCall (COMSVCSEVENTINFO * pInfo, ULONG64 oid, REFCLSID cid, REFIID rid, ULONG iMeth)
{
EnterCriticalSection(&m_csMapLock);
TimeStack * pStack = m_map[oid];
if (!pStack)
{
pStack = new TimeStack;
m_map[oid] = pStack;
}
pStack -> push_front(pInfo->perfCount);
LeaveCriticalSection(&m_csMapLock);
return S_OK;
}

STDMETHODIMP CMethodMon::OnMethodReturn (COMSVCSEVENTINFO * pInfo, ULONG64 oid, REFCLSID cid, REFIID rid, ULONG iMeth, HRESULT hr)
{
TimeStack * pStack = m_map[oid];
pStack -> pop_front();

// Remove the entry for the oid if it's call stack is empty
if (pStack -> empty())
{
delete pStack;
m_map.erase(oid);
}
return S_OK;
}

IComAppEvents
Only the OnAppShutdown event is of interest in the IComAppEvents:
STDMETHODIMP CMethodMon::OnAppShutdown(COMSVCSEVENTINFO * pInfo, GUID guidApp)
{
ClearCallTimes();
return S_OK;
}
void CMethodMon::ClearCallTimes()
{
EnterCriticalSection(&m_csMapLock);

// Clear all the call times
TimeMap::iterator iter;
for (iter = m_map.begin( ); iter != m_map.end( ); iter++ )
{
TimeStack *pStack = (TimeStack *) (*iter).second;

while (!pStack->empty())
{
pStack -> pop_front();
}
}
m_map.clear();
LeaveCriticalSection(&m_csMapLock);
}
Calculating the highest call time is straightforward:
unsigned long CMethodMon::highestCallTime(void)
{

long lHighestCallTime = 0;
LONGLONG PerformanceFrequency;
QueryPerformanceFrequency((LARGE_INTEGER *)&PerformanceFrequency);

LONGLONG lNow;
QueryPerformanceCounter((LARGE_INTEGER *) &lNow);

EnterCriticalSection(&m_csMapLock);

// Step through all the oid's currently in call
TimeMap::iterator iter;
for (iter = m_map.begin( ); iter != m_map.end( ); iter++ )
{
TimeStack *pStack = (TimeStack *) (*iter).second;

// Get the LAST element in the call stack as it will be the oldest element
if (pStack)
{
LONGLONG lOldest = pStack->back();
unsigned long lThisCallTime = (unsigned long)((1000*(lNow - lOldest))/
PerformanceFrequency);

if (lThisCallTime > lHighestCallTime)
{
lHighestCallTime = lThisCallTime;
}
}
}

LeaveCriticalSection(&m_csMapLock);

return lHighestCallTime;
}

There are known issues with the implementation above. Some error handling code has been removed and it does not take into account the fact that the PerformanceCounter will wrap sooner or later.

Issues with the official approach
The component gets called for each COM+ call (in the applications you monitor). Interesting in itself but you have to be a 110% sure you don't introduce deadlocks, memory leaks or anything else that affects the stability of the system.
I used the official approach for more than a year. It worked like a charm for weeks on end, but at random times I would get an enormous memory leak (>1GB) in the Windows RPC sub system on Win2k (I have not tried it on Win2k3). The leak may have been due to another process making heavy use of using COM+ notifications, a bug in my code or something else. It only happened when the machine was under heavy stress (100% CPU, and more than 1000 notifications per second for a few hours) so my -wild- guess is that other tasks got higher priority and the notifications continued to queue up until the system went in tilt.
The solution is not trivial to implement and requires the developer to know C++, ATL, COM admin APIs as well as Loosely Coupled Events. Finding skilled C++ COM+ programmers is hard so I decided to look for an easier to maintain solution. I will post the unofficial way I found tomorrow. It is a lot simpler and drops the source code from 1.500 lines of C++ to less than 150.

________________________________________
Feel free to drop a few cents in the tip jar if this post saved you time and money
Feedback
# re: COM+ Call Timers: the official way
5/12/2005 8:41 AM by Egil Hogholt

Several people have asked me for the source code or a DLL that implement the com+ call time tracking. The official way is complex and my implementation has memory leaks so I recommend using the unofficial way. These post have more info:

* COM+ Call Timers: the unofficial way (http://www.egilh.com/blog/archive/2005/02/25/557.aspx): source code in C++ of the com+ call time tracker function

* How to get COM+ call times from VB and VBScript (http://www.egilh.com/blog/archive/2005/04/12/655.aspx): free DLL that implements the unofficial way with example VB Script client

# re: COM+ Call Timers: the official way
2/1/2006 7:17 AM by Hai Son

Dear,
I'm an amateur COM Developer. I don't know where to start your code. Could you please to send me a sample project or tell me how to do.
Best Regards


# re: COM+ Call Timers: the official way
2/1/2006 5:40 PM by Egil Hogholt

I suggest that you start with the following post: "How to get COM+ call times from VB and VBScript"
http://www.egilh.com/blog/archive/2005/04/12/655.aspx

It has all the code and info you need to get the com+ call timers.

# re: COM+ Call Timers: the official way
2/2/2006 2:48 AM by Hai Son

Dear
I want to do something when user shutdown Com application by component service. Should I do something in function OnAppShutdown? I don't know how to insert function OnAppShutdown and how to make funtion OnAppShutdown run when user shut down Com application

# How can I implement OnAppShutdown?
2/2/2006 2:57 AM by Hai Son

Dear
I want to do something when user shutdown Com application by component service. Should I do something in function OnAppShutdown? I don't know how to insert function OnAppShutdown and how to make funtion OnAppShutdown run when user shut down Com application
What kind of ATL project should I create? What kind of ATL Object should I insert? What property of ATL Object should I set? Please show me all step before implement function OnAppShutdown.

Best Regard.

# re: COM+ Call Timers: the official way
2/2/2006 11:01 AM by Egil Hogholt

Correct.

You have to implement the IComAppEvents interface and do something useful in OnAppShutdown(). Subscribe to the IComAppEvents for the application(s) you are interested in.

This guide on MSDN explains how COM+ events work and how to use them in your application:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/html/1e0570ae-9099-465a-9133-72aa7d574932.asp

# egilhComTracker: Free component to track com call times with
2/2/2006 9:22 PM by /egilh



# re: COM+ Call Timers: the official way
2/3/2006 3:50 AM by Hai Son

Dear

.Net supported a effective way to handle COM+ Events.
Is there any ATL solution in Visual C++ 6.0?

# re: COM+ Call Timers: the official way
2/3/2006 7:40 AM by Egil Hogholt

ATL supports COM+ events. You have to create a multi threaded DLL and implement the interfaces described above.

Writing a full COM+ tutorial in the comments is difficult, so please leave your e-mail address in the contact form http://www.egilh.com/blog/contact.aspx if you need more information.

# re: COM Call Timers: the unofficial way
3/15/2006 9:01 PM by /egilh



# re: COM+ Call Timers: the official way
3/20/2006 12:02 PM by Mark

Hi Egil,
Is there a way to provide the COM+ Call
Timers as a remoting server/EnterpriseServices component .It throws a BadImageException on
account of "unsafe" keyword.

Regards

# re: COM+ Call Timers: the official way
3/21/2006 3:04 PM by Egil Hogholt

I guess you are referring to the "unofficial" .NET implementation http://www.egilh.com/blog/archive/2005/02/25/557.aspx as the official version is only available in C++

Are you sure you get a BadImageException because of the unsafe keyword? You normally get a BadImageException if you try to register a DLL with the wrong version of regsvcs. A DLL compiled with .NET 1.1 must be registered with regsvcs from .NET 1.1

# re: COM Call Timers: the unofficial way
3/6/2007 9:25 PM by /egilh



# re: COM+ Call Timers: the official way
3/28/2007 8:18 PM by Pedro Antunes

Hi Egil, Congratulations for your Page.

Do you know if we can implement this code in VB6? Do you have a translation of this code in VB or Delphi?

Thank you so much!

# re: COM+ Call Timers: the official way
3/28/2007 8:39 PM by Egil Hogholt

No, you cannot implement this in VB6. I am not sure about Delphi .

I suggest that you use the free COM+ tracker DLL which you can use from "any" language, VB, VBScript, Delphi, .NET, ...