Nathan Enright
Microsoft Corporation
December 2001
日本語版最終更新日 2002 年 7 月 5 日
概要: COM+ のサポート エンジニアに最も多く寄せられる要望は、COM+ アプリケーションのために使用可能な外部のプロファイリング ツール、特に PerfMon カウンタが欲しいというものです。この記事で説明するテクニックを使用すると、既存の COM+ アプリケーション用の独自の PerfMon カウンタを作成し、COM+ システム イベントが組み込みデータに対して提供している柔軟な監視機能を独自のデータに適用することができます。このイベント システムの利用方法を学ぶなかで、COM+ に対する理解を深め、アプリケーションの運用を開始する前にボトルネックを検出できるようになるでしょう。
目次
はじめに
COM+ の計測
プロジェクトのセットアップ
イベントのサブスクリプション
PerfMon カウンタ
カウンタのインストールと削除
インスタンスの追加と削除
インスタンスの操作
結論
はじめに
この記事では、COM+ システム イベントへのサブスクライブ、カスタム イベントの作成とサブスクライブ、そしてこれらのイベントからのデータを有用な PerfMon カウンタへと変換する方法について説明します。アプリケーションは C# で書かれており、COM interop を使ってイベント クラス インターフェイスにサブスクライブし、COM+ カタログを操作しています。COM+ の一時サブスクリプションに関する一般的な知識があると理解の助けになりますが、必須ではありません。
このオブジェクト モデルとコードの大部分は、Platform SDK に収録されている COMSPY サンプルから直接に移植されたものです。これは http://msdn.microsoft.com/library/en-us/cossdk/htm/pgcominstrumentationconcepts_3691.asp から入手することができます。COMSPY サンプルを知っている人は、COM+ Monitor のコードに多くの類似点があることに気づくでしょうが、これは意図的なものです。
COM+ の計測
COM+ の開発者は、アプリケーション プログラマが COM+ アプリケーションに関する重要な情報を入手できるように、先見の明をもって COM+ にリッチなインフラストラクチャを組み込みました。ここでは、このインフラストラクチャを使用してデータを取得し、アプリケーションの背後で起こっていることをグラフィカルに表現する PerfMon カウンタを作成します。このサンプルでは、COM+ の計測インターフェイスのうちの 1 つ、IComMethodEvents のみをインプリメントしました。しかし、COM+ の監視オブジェクト モデルは、追加のインターフェイスをきわめて簡単にインプリメントできるように設計されています。
IComMethodEvents インターフェイスは、OnMethodCall、OnMethodReturn、および OnMethodException の 3 つのメソッドを提供しています。ここでは、これらのメソッドを使用して、メソッドの実行時間を示す PerfMon カウンタを作成します。これはアプリケーションのボトルネックを判定するのに役立ちます。IComMethodEvents イベントは事前バインドされた呼び出しにのみ有効であることに注意してください。遅延バインドの呼び出しは IDispatch を通して、GetIDsOfNames と Invoke を呼び出すことによって行われるためです。遅延バインドの呼び出しでは、OnMethodCall イベントと OnMethodReturn イベントは、GetIDsOfNames で 1 回、Invoke で 1 回、合わせて 2 回発行します。Invoke が呼び出しているメソッドの名前を知る方法はありません。ただし、コンポーネントに独自の計測コードを追加し、イベントを発行して IComUserEvent インターフェイスにサブスクライブすれば、この問題は回避することができます。
注 個々のコンポーネントの任意の COM+ イベントを受け取れるようにするためには、各コンポーネントのコンポーネント サービス スナップインで [コンポーネントはイベントと統計をサポートする] オプションが選択されている必要があります。これはデフォルトでオンになっています。
上で述べたように、COM+ の計測はきわめてリッチなインフラストラクチャです。計測インターフェイスの一覧については、http://msdn.microsoft.com/library/en-us/cossdk/htm/pgcominstrumentationinterfaces_5lir.asp を参照してください。
プロジェクトのセットアップ
COM+ Monitor プロジェクトは、COMPlusMon WinForm EXE と COMSysLCE DLL の 2 つの部分に分かれています。作業の大部分は COMSysLCE DLL の中で行われるので、この記事ではこちらに焦点を当てます。
現在のリリースの.NET Framework は、ここで使用する COM+ の機能を完全には組み込んでいません。実際、このサンプルでは System.EnterpriseServices 名前空間すらも使用していません。ここでは、COM+ 1.0 Admin Type Library と COM+ Services Type Library を使用しています。
これらのライブラリの機能を利用するためには、COM interop を使用し、これらの DLL のためのアセンブリを作成する必要があります。これは Visual Studio .NET が自動的に行ってくれます。単に、COM+ Admin Type Library と COM+ Services Type Library への新しい COM 参照を作成し、Visual Studio にアセンブリを作成させてください。
参照が追加され、アセンブリが生成されたら、次のようにコード モジュールに COMAdmin および COMSVCSLib 名前空間を追加します。
using COMAdmin;
using COMSVCSLib;
必要となる外部参照はこの 2 つだけです。ただし、このサンプルでは以下の名前空間も使用しています。
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Collections;
using Microsoft.Win32;
この記事ではサンプルのインプリメンテーションに焦点を当てるつもりでしたが、オブジェクト モデルについても少し解説しておく必要があるでしょう。サンプルを独自に拡張するためには、その動作の仕組みを理解しておくことが重要です。COMSysLCE DLL は 6 つのメイン クラスと 1 つのインターフェイスを含んでいます。
| CAdminWrap |
このクラスは、COM+ Catalog の作業の大部分をラップするために使用されます。以下のメソッドを含んでいます。
AddTransientSubscription RemoveTransientSubscription GetCollection RemoveNamedObjectFromCollection GetNamedObjectFromCollection SetStringProperty SetIUnknownProperty |
| CAppInfo |
このクラスは、COMPlusMon.EXE が対話を行うメイン クラスです。ユーザーが監視対象の COM+ アプリケーションを選択すると、CAppInfo
は特定のイベント クラスを選択されたアプリケーションにサブスクライブします。COMPlusMon.EXE は、サブスクライブしているすべてのアプリケーションのコレクションを含んでいます。CAppInfo
は以下のメソッドを含んでいます。
CAppInfo (パラメータ コンストラクタ) Dispose AppID (読み取り専用プロパティ) RemoveAllSubscriptions AddSubscription ToggleMonitoring RemoveSubscription IsSubscribedToAny |
| CCOMSysLCE |
CCOMSysLCE は、ICOMSysLCE インターフェイスをインプリメントしている抽象基本クラスです。以下のメソッドを含んでいます。
CCOMSysLCE EventName GetEventType GetEventClass GetInterface GetUnknown AddPCInstances RemovePCInstances |
| CCOMMethodEvent |
これは CCOMSysLCE と IComMethodEvents をインプリメントしているクラスで、さらに以下のメソッドを含んでいます。
GetMethodName GetTypeLibID |
| ICOMSysLCE |
このインターフェイスは以下のメソッドを定義しています。
GetEventClass GetEventType Install UnInstall Monitor (読み書き可能プロパティ) AddPCInstances RemovePCInstances |
オブジェクト モデルを理解するには、コードを何度かステップしてみるといいでしょう。COMPlusMon プロジェクトの chklbApps_ItemCheck イベント ハンドラの先頭にブレークポイントを設定します。プロジェクトを実行し、監視するアプリケーションを選択すると、このブレークポイントに到達します。
サンプルを実行するためには、COMSysLCE DLL と COMPlusMon EXE の両方をコンパイルする必要があります。次に、カウンタをインストールしなくてはなりません。詳細については、後に出てくる「カウンタのインストールと削除」のセクションを参照してください。セットアップが完了したら、付属の Visual Basic Test DLL と EXE を使用してカウンタをテストすることができます。
イベントのサブスクリプション
イベント クラスにサブスクライブする方法は、サブスクライブ先のイベント クラスの種類にかかわらず同じです。サブスクライブ先のインターフェイス ID であるイベント クラス ID と、サブスクリプションの名前を指定します。イベント サブスクリプションは COM+ のカタログ操作の練習問題の 1 つです。ここでは、流れを理解しやすいように、サンプルとは若干異なるコードを使用します。サンプル内での該当箇所を知りたい場合は、COMSysLCE プロジェクトの CAdminWrap クラスの AddTransientSubscription 関数を確認してください。次のコードは、IComMethodEvents インターフェイスにサブスクライブする方法を示しています。
//COMAdminCatalog オブジェクトを取得する。このオブジェクトは
//COMAdmin 名前空間に定義されている。
ICOMAdminCatalog pICat = new COMAdminCatalog();
//TransientSubscriptions コレクションを取得し、格納する。
ICatalogCollection pISubs =
(ICatalogCollection)pICat.GetCollection("TransientSubscriptions");
pISubs.Populate();
//新しい一時サブスクリプションを追加する。
ICatalogObject pISub = (ICatalogObject)pISubs.Add();
//新しいサブスクリプション CatalogObject が得られたので、
//詳細情報の格納を開始することができる。
//サブスクリプションの名前を設定する。
pISub.set_Value("Name", "Method");
//イベント クラス ID を設定する。これは CLSID_ComServiceEvents の
//GUID であり、comsvcs.h に定義されている。
pISub.set_Value("EventCLSID", "{ECABB0C3-7F19-11D2-978E-0000F8757E2A}");
//IComMethodEvents のインターフェイス ID を設定する。
pISub.set_Value("InterfaceID, "{683130A9-2E50-11D2-98A5-00C04F8EE1C4}");
//SubscriberInterface プロパティをサブスクライバの IUnknown ポインタ
//に設定する。サブスクライバは IComMethodEvents インターフェイスを
//インプリメントしているクラス。例:
//IntPtr punk = Marshal.GetIUnknownForObject(CComMethodEvent Object);
pISub.set_Value("SubscriberInterface", punk);
//TransientSubscriptions カタログ コレクションへの変更を保存する。
long lret = pISubs.SaveChanges();
//サブスクリプションを削除できるように、
//サブスクリプション ID を取得して保存しておく必要がある。
string strSubID = (string)pISub.get_Value("ID");
//TransientPublisherProperties コレクションを取得する。
string strKey = (pISub.Key).ToString();
ICatalogCollection pITPPs =
(ICatalogCollection)pISubs.GetCollection("TransientPublisherProperties",
strKey);
pITPPs.Populate();
//新しい TransientPublisherProperties オブジェクトを追加する。
ICatalogObject pITPP = (ICatalogObject)pIProps.Add();
//ここでは、サブスクライブ先のパブリシャを設定する。
//これは監視対象の COM+ アプリケーションの AppID である。
//このコード例では strAppID を設定していない。ここでハードコードする
//ことも可能だが、動的に取得する方法についてはサンプル コードを参照のこと。
pITPP.set_Value("Name", "AppID");
pITPP.set_Value("Value", strAppID);
//最後に、TransientPublisherProperties コレクションの変更を
//保存する。
lret = pITPPs.SaveChanges();
インターフェイスへのサブスクライブは、アプリケーションの処理の 60% を占めています。いったんサブスクライブを行うと、イベントの発生時にはインターフェイス メソッドを通して通知を受け取ることができます。上記のコードで注意すべき点がいくつかあります。
- Marshal.GetIUnknownForObject を使用して、サブスクライブ先のインターフェイスをインプリメントしている C# オブジェクトの、COM 呼び出しが可能な IUnknown ポインタを取得しています。
- OLEView を使用して、サブスクライブ先のインターフェイスのインターフェイス ID を取得することができます。エキスパート モードで、Interfaces ノードを展開し、IComMethodEvents などの目的のインターフェイスを探してください。
- イベント クラス ID は、サブスクライブ先のインターフェイスに関係なく同じです。
最後に説明しておかなくてはならないのは、サブスクリプションの削除方法です。これを怠ると、サブスクリプションはマシンが再ブートされるまで存在を続けます。そうなっても特に悪影響はありませんが、レジストリはきれいに保っておきたいものです。
//COMAdminCatalog オブジェクトを取得する。このオブジェクトは
//COMAdmin 名前空間に定義されている。
ICOMAdminCatalog pICat = new COMAdminCatalog();
//TransientSubscriptions コレクションを取得し、格納を行う。
ICatalogCollection pISubs = (ICatalogCollection)pICat.GetCollection("TransientSubscriptions");
long lCount = 0;
int i = 0;
ICatalogObject pISub;
pISubs.Populate();
lCount = pISubs.Count;
//カウントが 0 ならば、一時サブスクリプションは存在しない。
if (lCount == 0)
{
return;
}
//削除したいサブスクリプションが見つかるまで、
//コレクションをループ処理する。サブスクリプション ID は
//サブスクリプションの作成時に保存してある。
for (i=0; i<lCount; i++)
{
pISub = (ICatalogObject)pISubs.get_Item(i);
if (strSubID == (string)pISub.get_Value("ID"))
{
pISubs.Remove(i);
pISubs.SaveChanges();
return;
}
}
イベントのサブスクライブの場合と同様に、サブスクリプションの削除は基本的にはカタログ操作の練習問題であり、いったんカタログの動作がわかれば簡単に理解することができます。
COM+ カタログと一時サブスクリプションの詳細については、Platform SDK の以下のトピックを参照してください。
- COM+ Administration Reference。http://msdn2.microsoft.com/en-us/library/ms681189.aspx
- Transient Subscriptions。http://msdn.microsoft.com/library/?url=/library/en-us/cossdk/htm/pgservices_events_6yyb.asp
PerfMon カウンタ
.NET の PerfMon カウンタの操作には、主に 3 つの部分があります。
カウンタのインストールと削除
ここでは、.NET のインストール コンポーネントを利用して、インストール プログラムに大部分の作業を行わせます。実際、コードは 1 行も書く必要がありません!
最初に、COMPlusMon プロジェクトに新しい Installer クラスを追加します。このためには、ソリューション エクスプローラで COMPlusMon プロジェクトを右クリックし、[追加] メニューの [コンポーネントの追加] を選択し、[インストーラ クラス] を選択します。
次に、PerformanceCounterInstaller コンポーネントをドラッグし、新しく追加したインストーラ クラスのデザイン ウィンドウにドロップします。
注 ツールボックスの [コンポーネント] のセクションに PerformanceCounterInstaller コンポーネントが表示されない場合は、ツールボックスを右クリックし、[ツールボックスのカスタマイズ] を選択します。[.NET Framework コンポーネント] タブで PerformanceCounterInstaller コンポーネントを追加することができます。
最後に、PerformanceCounterInstaller オブジェクトのプロパティを変更する必要があります。まずカテゴリ名として "COM+" などを指定します。CategoryName
プロパティはパフォーマンス カウンタのオブジェクト名に対応します。その後、Counters コレクションに CounterName="Method
Duration"、CounterType=NumberOfItems32 などのエントリを追加します。われわれに必要な機能は
NumberOfItems32 カウンタが提供しています。利用可能なカウンタの一覧については、「PerformanceCounter.CounterType
プロパティ」を参照してください。
アプリケーションのインストールを作成するときには、新しく作成した Installer クラスを、インストーラ プロジェクトのカスタム アクション セクションに追加することができます。インストール アプリケーションは、ユーザーがアプリケーションを追加または削除したときに、カウンタの追加と削除を自動的に行ってくれます。
インスタンスの追加と削除
パフォーマンス カウンタ インスタンスは、サブスクライブ先のイベントのタイプに依存しています。ここでは、メソッドの実行時間を 1/100 秒単位で知らせてくれる Method Duration カウンタを追加することにします。Method Duration カウンタは COM+ オブジェクトの下にあり、インスタンスは選択された COM+ アプリケーションのすべてのメソッドです。これにより、監視したいメソッドを選択し、各メソッドの統計情報を個別に入手することができます。
個々のイベント クラス (例: CCOMMethodEvent) に 2 つのメソッドを追加します。これらのメソッドは ICOMSysLCE で定義され、CCOMSysLCE で仮想メソッドとなっているので、子クラスでオーバーライドする必要があります。どちらのメソッドも、監視対象の COM+ アプリケーションの AppID を引数として取ります。
override public void AddPCInstances(string strAppID)
{
CAdminWrap oAdminWrap = new CAdminWrap();
COMAdmin.ICatalogCollection appColl, comColl, intColl, metColl;
COMAdmin.ICatalogObject appObj, comObj, intObj, metObj;
int nIndex;
//管理カタログを取得する。
COMAdminCatalog oCatalog = new COMAdminCatalog();
//Applications コレクションを取得する。
appColl = oAdminWrap.GetCollection(oCatalog, "Applications");
//渡された AppID の Catalog オブジェクトを取得する。
appObj = oAdminWrap.GetNamedObjectFromCollection(appColl, strAppID,
out nIndex, "ID");
//アプリケーションの Components コレクションを取得する。
comColl = oAdminWrap.GetCollection(appColl, appObj, "Components");
//まず Components コレクションのすべてのエントリをループ処理する
//必要がある。
for (int nComIndex = 0; nComIndex < comColl.Count; nComIndex++)
{
string strProgID;
comObj = (ICatalogObject)comColl.get_Item(nComIndex);
//ここではコンポーネントの progID を取得する。
strProgID = (string)comObj.get_Value("ProgID");
intColl = oAdminWrap.GetCollection(comColl, comObj,
"InterfacesForComponent");
//その後、各コンポーネントのすべてのインターフェイスを
//ループ処理する。
for (int nIntIndex = 0; nIntIndex < intColl.Count; nIntIndex++)
{
intObj = (ICatalogObject)intColl.get_Item(nIntIndex);
metColl = oAdminWrap.GetCollection(intColl, intObj,
"MethodsForInterface");
//最後に、MethodsForInterfaces コレクションをループ処理して、
//メソッド名を取得する。
for (int nMetIndex = 0; nMetIndex < metColl.Count; nMetIndex++)
{
metObj = (ICatalogObject)metColl.get_Item(nMetIndex);
string strName = (string)metObj.get_Value("Name");
//PerformanceCounter オブジェクト (クラス モジュール変数) が
//まだ初期化されていなければ、ここで初期化し、最初のインスタンスを
//追加する。インスタンスは PROGID::MethodName として追加する。
//PerformanceCounter インスタンスは、
//インスタンス名とその生の値を設定することで作成される。
//AddInstance というようなメソッドは存在しない。
if (pcMethodDuration == null)
{
pcMethodDuration = new PerformanceCounter("COM+", "Method
Duration", strProgID + "::" + strName, false);
}
else
{
pcMethodDuration.InstanceName = strProgID + "::" + strName;
}
//初期値を 0 に設定する。
pcMethodDuration.RawValue = 0;
}
}
}
}
override public void RemovePCInstances(string strAppID)
{
//Remove 関数は、1 つの違いを除けば、Add 関数と
//まったく同じである。
...
for (int nComIndex = 0; nComIndex < comColl.Count; nComIndex++)
{
...
for (int nIntIndex = 0; nIntIndex < intColl.Count; nIntIndex++)
{
...
for (int nMetIndex = 0; nMetIndex < metColl.Count; nMetIndex++)
{
metObj = (ICatalogObject)metColl.get_Item(nMetIndex);
string strName = (string)metObj.get_Value("Name");
int nMethodIndex = (int)metObj.get_Value("Index");
string strCLSID = (string)metObj.get_Value("CLSID");
//インスタンス名を設定する。
pcMethodDuration.InstanceName = strProgID + "::" + strName;
//RemoveInstance() を呼び出す。
pcMethodDuration.RemoveInstance();
}
}
}
}
インスタンスの操作
インスタンスの追加と削除と同様に、インスタンスの操作は、サブスクライブ先のインターフェイスと、カウンタに行わせる処理の内容に大きく依存します。この例では、メソッドが開始されたときの時刻を記録し、メソッドが終了した時刻から引きます。その後、この数値を PerfMon に書き込みます。メソッドの実行時間をどのように計算するかは主観的な問題です。われわれが求めているのは、ボトルネックを発見し、それを解消することです。カウンタがデフォルトの PerfMon グラフに表示されるように、マイクロ秒単位の実行時間を 10,000 で割って、1/100 秒単位で表示するようにします。
最初に行わなくてはならないのは、メソッドが開始されたときの時刻を記録することです。OnMethodCall イベントと OnMethodReturn イベントは、どちらも COMSVCSEVENTINFO イベント構造体へのアクセスを提供しています。この構造体には、真夜中を起点とした秒単位の経過時間である lTime と、lTime を起点としたマイクロ秒単位の lMicroTime が含まれています。次に OnMethodCall のコードを示します。
void IComMethodEvents.OnMethodCall(ref COMSVCSEVENTINFO ei, ulong
lObjID, ref Guid gClsID, ref Guid gIID, uint nIndex)
{
//監視が有効になっており、パフォーマンス カウンタが
//初期化されていることを確認する。
if (Monitor && pcMethodDuration != null)
{
try
{
//初期値を Sorted List コレクションに格納する。
//このためには、
//この呼び出しを表すキーが必要となる。
string strKey = lObjID.ToString() + gClsID.ToString() +
gIID.ToString() + nIndex.ToString();
//上で作成したキーを使って、Sorted List に開始時刻を追加する。
//lTime をマイクロ秒に変換してから、
//lMicroTime を加える。
sl.Add(strKey, (ei.lTime * 1000000) + ei.lMicroTime);
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
}
この例からわかるように、OnMethodCall イベントには大した内容はありません。一方、OnMethodReturn イベントはかなりの量のコードを含んでおり、2 つのヘルパー関数を使用しています。基本的には、以下の作業を行う必要があります。
- インスタンス名を生成する。
- 開始時刻を取得する。
- 終了時刻から開始時刻を引く。
- インスタンスの生の値を設定する。
ヘルパー関数のコードは、このペーパーの範囲を超えるので省略します。次に示すのは、OnMethodReturn イベントのコードです。
void IComMethodEvents.OnMethodReturn(ref COMSVCSEVENTINFO ei, ulong loID,
ref Guid gclsID, ref Guid giID, uint nmethod, int nhresult)
{
CAdminWrap oAdminWrap = new CAdminWrap();
COMAdminCatalog oCatalog = new COMAdminCatalog();
COMAdmin.ICatalogCollection appColl, comColl, intColl, metColl;
COMAdmin.ICatalogObject appObj, comObj, intObj, metObj;
int nRet;
//監視が行われており、パフォーマンス カウンタが
//初期化されていることを確認する。
if (Monitor && pcMethodDuration != null)
{
try
{
//カタログからインスタンス名を
//可能な限り取得する。
string strAppID = "{" + ei.guidApp.ToString().ToUpper() + "}";
string strCLSID = "{" + gclsID.ToString().ToUpper() + "}";
//開始時刻を得るためのキーを作成する。
string strKey = loID.ToString() + gclsID.ToString() +
giID.ToString() + nmethod.ToString();
//開始時刻を取得する。
int nIndex = sl.IndexOfKey(strKey);
long lStart = (long)sl.GetByIndex(nIndex);
appColl = oAdminWrap.GetCollection(oCatalog, "Applications");
appObj = oAdminWrap.GetNamedObjectFromCollection(appColl,
strAppID, out nRet, "ID");
comColl = oAdminWrap.GetCollection(appColl, appObj,
"Components");
comObj = oAdminWrap.GetNamedObjectFromCollection(comColl,
strCLSID, out nRet, "CLSID");
//インスタンス名の ProgID の部分。
string strProgID = (string)comObj.get_Value("ProgID");
intColl = oAdminWrap.GetCollection(comColl, comObj,
"InterfacesForComponent");
string strMN = "";
for (int nCount = 0; nCount < intColl.Count; nCount++)
{
Guid gIID = new
Guid((string)
((ICatalogObject)
intColl.get_Item(nCount)).get_Value("IID"));
//GetMethodName 関数は、LoadRegTypeLib を使ってメソッド名を
//タイプ ライブラリから直接に取得するヘルパー関数である。
//これを行わないと、GetIDsOfNames と Invoke を
//チェックできない。
strMN = GetMethodName(gIID, (int)nmethod);
//メソッドが GetIDsOfNames または Invoke である場合は
//呼び出しを無視する。
if (strMN != "" && strMN != "GetIDsOfNames" && strMN !=
"Invoke")
{
//正しいインスタンス名が得られたので、生の値を、終了時刻から
//開始時刻を引いた値に設定する。
pcMethodDuration.InstanceName = strProgID + "::" + strMN;
pcMethodDuration.RawValue = (((ei.lTime * 1000000) +
ei.lMicroTime) - lStart)/100;
//最後に、Sorted List からエントリを削除する。
sl.Remove(strKey);
}
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
}
}
}
結論
ここでは、.NET のインストーラ コンポーネントを使ったカウンタの追加と削除、インスタンスの追加と削除、およびこれらのインスタンスの操作の方法を説明しました。追加するカウンタとインスタンスは、受け取りたいイベントの種類によって変わります。
COM+ アプリケーションの動作に関する情報は、開発と運用のどちらにおいても非常に有用です。この情報をもとに、開発時には明らかでなかった問題を短時間で修正することができます。問題の確定と識別に加えて、カスタムの計測コードを追加してカスタム カウンタを提供することができます。こうすれば、オーダーからユーザーまでのあらゆるものについて、PerfMon データを入手することができます。
COM+ Monitor サンプルは、COM+ アプリケーションを制御し、Windows のパフォーマンス監視を活用するためのパフォーマンスおよび計測アプリケーションを構築するためのノウハウを提供しています。