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
}
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
- 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
}
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("
'csStatistics.Append("
csStatistics.Append("
csStatistics.Append("
'csStatistics.Append("
'csStatistics.Append("
'csStatistics.Append("
'csStatistics.Append("
'csStatistics.Append("
'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("
csStatistics.Append("
'csStatistics.Append("
'csStatistics.Append("
'csStatistics.Append("
'csStatistics.Append("
csStatistics.Append("
'csStatistics.Append("
'csStatistics.Append("
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

1 comment:
Looks like you forgot to link back to the original article
Post a Comment