How to get COM+ call times from VB and VBScript
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:
<applications>
<application>
<guid>{98CDBB6E-3CAF-46F2-ABE6-EABECD6AA4EB}</guid>
<ID>10</ID>
<processID>1792</processID>
<statistics>
<callsPerSecond>0</callsPerSecond>
<totalCalls>0</totalCalls>
<totalClasses>1</totalClasses>
<totalInstances>1</totalInstances>
</statistics>
<classes>
<class>
<progID>Test.Sleep.1</progID>
<bound>1</bound>
<inCall>0</inCall>
<pooled>-1</pooled>
<references>1</references>
<responseTime>120</responseTime>
<callsCompleted>0</callsCompleted>
<callsFailed>0</callsFailed>
</class>
</classes>
</application>
</applications>
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
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.
ReplyDeleteGlad you found what you were looking for!
ReplyDeleteLet me know if you have suggestions for improving the component or if you find any bugs.
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!
ReplyDeleteHi Brian, could you help me to do that ?
DeleteHow can i get the application name from the guid??
ReplyDeleteSorry for the delay in getting back to you. Work as been very busy after coming back from vacation.
ReplyDeleteI 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
Is It possible execute vb script from a host and get COM+ parameters from another host in the same domain?
ReplyDeleteThanks, your tracker is good.
Everything is possible!
ReplyDeleteIf 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
I use VBScript to run Comtracker so I choose your second option: create a proxy application on client host to get call times.
ReplyDeleteWhen 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.
It seems like a configuration issue.
ReplyDeleteThe 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
is it possible to automatically restart a component if it's call time reaches a certain value?
ReplyDeleteThere is no built in way in Windows/COM+ to do this but you can do it with the comtracker:
ReplyDelete- 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.
When I run it, it says "getStatistics not supported by object". Any ideas?
ReplyDeleteWhen i try to register your .dll, it just goes into a loop and tries to continueously register it, any ideas?
ReplyDeleteCan you post the source code for the .dll?
ReplyDeleteHi Egil,
ReplyDeleteAfter 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)
Hi Dio, I am glad you found the code useful!
ReplyDeleteYou 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.
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?
ReplyDeleteThank you again.
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.
ReplyDeleteProblem solved; the code was missing CoInitialize() which is required when calling COM+ objects like the COM+ Tracker
ReplyDeletehi there i not that good with scripting but could someone show me how to get call times for specific Program ID or method calls.
ReplyDeleteYou can only get call times per object, not for the individual methods in that object.
ReplyDeleteThe 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
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.
ReplyDeleteOnce again thank you
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.
ReplyDeletethanks
BD
Even simpler.
ReplyDeleteIf 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.
Cool thanks heaps, didn't really know how to put in the correct xpath better start learning some XML basics. thanks again
ReplyDeleteHi Egil,
ReplyDeletefirst, 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.
I have not seen this problem before myself but I will check and let you know ASAP.
ReplyDeleteLooks like it should fix your problem. Please let me know if it works or not.
ReplyDeleteThanks,
Egil
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?
ReplyDeleteYou 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).
ReplyDeleteThe 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.
ReplyDeleteI 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.
What error message do you get?
ReplyDelete
ReplyDeleteThe error is:
test.vbs(7, 1) Microsoft VBScript runtime error: ActiveX component can't create object: 'Egilh.ComTracker'
Did you register the egilh.ComTracker component on both machines?
ReplyDeleteThe component will run on the remote machine but it must be installed locally as well so DCOM knows which methods it implements.
ReplyDeleteyes 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.
Hm...
ReplyDeleteSome 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?
ReplyDelete-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}
It looks like the component is registered with two different class IDs which is strange since it is the same DLL.
ReplyDeleteCan 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".
Hey, thanks for doing this. I noticed that on my system (Win2000 server) when I regsvr32 the dll I get
ReplyDelete"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.
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?
ReplyDeleteThe 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
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.
ReplyDeleteHi ,
ReplyDeleteI am not able to download the dll egilh Com+ Tracker (egilh.ComTracker)
Could You please Help me and give the correct location from where i can download
Thanks
If you're wondering how to fix this error msvcr71.dll, then I advise you to use this http://fix4dll.com/msvcr71_dll site. It will help to solve it. One of the few sites that is pleasant to work with.
ReplyDeleteHow to get the processID of these components, something like below :
ReplyDelete' If Call time exceeds threshold, get the object name and time and log it
If cLng( obj_DOMNode.selectSingleNode("responseTime").Text ) > lng_COMRunTimeThreshold Then
str_Result = str_Result & obj_DOMNode.selectSingleNode("processID").Text & "[" & obj_DOMNode.selectSingleNode("responseTime").Text & "ms],"
Call f_WriteLog( str_ScriptLog, "," & str_ComputerName & "," & obj_DOMNode.selectSingleNode("progID").Text & "," & "[" & obj_DOMNode.selectSingleNode("responseTime").Text & " ms]", 1, 4 )
End If
Hi ,
ReplyDeleteI am not able to download the dll egilh Com+ Tracker (egilh.ComTracker)
Could You please Help me and give the correct location from where i can download
Thanks
This is a broken link.
ReplyDeletehttp://www.egilh.com/download/blog/egilhComTracker.zip