Tuesday, April 12, 2005

655.aspx

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


 

43 comments:

  1. 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.

    ReplyDelete
  2. 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.

    ReplyDelete
  3. 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!

    ReplyDelete
  4. How can i get the application name from the guid??

    ReplyDelete
  5. 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

    ReplyDelete
  6. 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.

    ReplyDelete
  7. 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

    ReplyDelete
  8. 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.

    ReplyDelete
  9. 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

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

    ReplyDelete
  11. 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.

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

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

    ReplyDelete
  14. Can you post the source code for the .dll?

    ReplyDelete
  15. 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)



    ReplyDelete
  16. 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.

    ReplyDelete
  17. 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.

    ReplyDelete
  18. 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.

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

    ReplyDelete
  20. 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.

    ReplyDelete
  21. 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

    ReplyDelete
  22. 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

    ReplyDelete
  23. 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

    ReplyDelete
  24. 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.

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

    ReplyDelete
  26. 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.

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

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



    Thanks,

    Egil

    ReplyDelete
  29. 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?

    ReplyDelete
  30. 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.

    ReplyDelete

  31. 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.

    ReplyDelete

  32. The error is:

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

    ReplyDelete
  33. 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.

    ReplyDelete

  34. 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.

    ReplyDelete
  35. 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?







    ReplyDelete

  36. -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}

    ReplyDelete
  37. 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".

    ReplyDelete
  38. 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.





    ReplyDelete
  39. 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

    ReplyDelete
  40. 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.

    ReplyDelete
  41. Hi ,

    I 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

    ReplyDelete
  42. 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.

    ReplyDelete