◆パフォーマンスモニタ - VC++ 2005編 その1

[ ホーム ] [ 上へ ] [ パフォーマンスモニタ VC++編 その2 ]

・パフォーマンスオブジェクト・カウンタ・インスタンスおよび説明の一覧

パフォーマンスモニタに関して何かする手段としては主に
1.PDH系のAPIで処理する。
2.レジストリのデータを使って処理する。
3.ATL で処理する。


パフォーマンスモニタは実は既存のオブジェクト・カウンタだけでなく目的のアプリケーションを 監視するために自分で追加(拡張)できます。その場合、監視されるアプリに公開する オブジェクト・カウンタを実装しなければなりませんが、このとき上記項目の3が必要になってきます (ATLを使わなくても可能です)。今回はパフォーマンスオブジェクト・カウンタの拡張については 書かないので、こういうこともできる程度に思ってください。

項目1と2でオブジェクト・カウンタ・インスタンスの一覧を実行してみます。

<PDH系APIでの一覧表示>

PDH系のAPIでオブジェクト・カウンタ・インスタンスを表示するには以下の2つを使います。

・PdhEnumObjects・・・・指定したコンピュータ上で利用可能なオブジェクトのリストを取得。
・PdhEnumObjectItems・・指定したコンピュータ上で、指定したオブジェクトによって提供される
                        カウンタとインスタンスを取得。

カウンタの説明を取得するにはPdhGetCounterInfo APIを使って引数に指定した PDH_COUNTER_INFO構造体に情報を受け取りますが、PdhGetCounterInfoを調べればわかりますが 受け取るにはカウンタへのハンドルが必要になります。カウンタへのハンドル を取得するにはPdhOpenQueryで問合せへのハンドルを得て、 オブジェクト名・カウンタ名・インスタンス名などからPdhAddCounterを実行します。

プログラムの大きな流れとしてはPdhEnumObjectsでオブジェクト名を列挙して、 取得した個々のオブジェクト名に対してPdhEnumObjectItemsを発行してカウンタ名・ インスタンス名(ある場合)を列挙します。ここで、オブジェクト名・カウンタ名・ インスタンス名が揃ったのでカウンタパス(カウンタを一意に特定する文字列)を作成して PdhOpenQuery、PdhAddCounterで得たカウンタへのハンドルを使ってPdhGetCounterInfoを実行して カウンタの情報を受け取ります。これを繰り返してすべてのカウンタ情報をコンソールに出力します。

それでは実装します。
Visual C++のWin32コンソールアプリケーションを選択します。 アプリケーションウィザードで以下のようにしてください。

アプリケーションの種類      :コンソールアプリケーション
追加のオプション            :プリコンパイル済みヘッダー
共通ヘッダーファイルを追加  :なし

プロジェクトが作成されたらツールバーのプロジェクト(P)→xxxxxのプロパティ(P)でプロジェクトのプロパティを表示し、 左ペインの 構成プロパティ - 全般を選び、右ペインの文字セット「マルチバイト文字セットを使用する」にして ください。

// xxxxxx.cpp : コンソールアプリケーションのエントリポイントを定義します。
//
#include "stdafx.h"
#include "pdh.h"

#pragma comment(lib, "pdh.lib")

//////////////////プロトタイプ宣言////////////////////////
//<PDH系API> Performance Object(Counter/Instance)名リスト及び説明取得
void GetPdhObjectList();
//<PDH系API> Performance Objectに対応するCounter/Instance名リスト及び説明の取得
void GetPdhObjectItemList(LPCTSTR pszObjectName,
                          HQUERY  hQuery);
//<PDH系API> Object/Counterに対応するモニタの説明取得
BOOL GetPdhCounterExplain(LPCTSTR pszObjectName,   LPCTSTR pszCounterName,
                          LPCTSTR pszInstanceName, HQUERY hQuery,
                          LPTSTR  *ppszExplain);
/////////////////////////////////////////////////////////
//メイン関数
int _tmain(int argc, _TCHAR* argv[])
{
    GetPdhObjectList();
    return 0;
}
//<PDH系API> Performance Object(Counter/Instance)名リスト及び説明取得
void GetPdhObjectList()
{
    DWORD   dwSize        = 0;    //pszObjectListに受け取る文字列のサイズ
    LPTSTR  pszObjectList = NULL; //Objectのリスト名のリスト
    LPTSTR  pszTemp       = NULL; //pszObjectListから取り出した各Object名格納用
    int     i;
    HQUERY  hQuery        = 0;    //Perfomance Dataの管理に使用する問合せへのハンドル

    //問合せOpen
    if (::PdhOpenQuery(NULL, 0, &hQuery) != ERROR_SUCCESS)
        return;

    //第三引数に受取るObject名のリストに必要なメモリサイズを取得
    ::PdhEnumObjects(NULL, NULL, NULL, &dwSize, PERF_DETAIL_EXPERT, TRUE);

    //取得したdwSizeのメモリを確保
    pszObjectList = new TCHAR[dwSize];

    while (TRUE)
    {
        //pszObjectListにObject名のリスト(MULTI_SZ形式)取得
        if (::PdhEnumObjects(NULL, NULL, pszObjectList, &dwSize,
                                    PERF_DETAIL_EXPERT, FALSE) != ERROR_SUCCESS)  //(A)
            break;

        for (pszTemp = pszObjectList, i = 0;
             *pszTemp != 0;
             pszTemp += (lstrlen(pszTemp) + 1), i++
            )
        {
            _tprintf(_T("\nObject%d : %s\n"), i, pszTemp);  //(B)

            //現在のObjectに対応するInstance/Counter名取得
            GetPdhObjectItemList(pszTemp, hQuery);          //(C)
        }
        break;
    }

    delete [] pszObjectList;
    ::PdhCloseQuery(hQuery);
}

//<PDH系API> Performance Objectに対応するCounter/Instance名リスト及びその説明の取得
//第一引数: Counter/Instace名を取得する対象のObject名
//第二引数: Performance Data収集時に使う問合せへのハンドル
//仕様    : 第一引数に対応するCounter/Instance名及びその説明を列挙する。
//          第二引数は説明取得時に必要なPdhGetCounterInfo APIで使用する。
void GetPdhObjectItemList(LPCTSTR pszObjectName, HQUERY hQuery)
{
    //Counter/Instance名のリスト格納必要サイズ
    DWORD   dwCounterSize = 0,    dwInstanceSize = 0;
    //Counter/Instance名リスト
    LPTSTR  pCounterList  = NULL, pInstanceList  = NULL;
    //pCounterList,pInstanceListからの各要素の格納用
    LPTSTR      pTempCounter    = NULL, pTempInstance  = NULL;
    LPTSTR      pExplain        = NULL; //Object/Counterの説明
    LPTSTR      pInstForExplain = NULL; //Object/Counterの説明取得のためのInstance名
    PDH_STATUS ret;
    int        i;     

    //Counter/Instance名のリスト取得に必要なサイズ取得
    ::PdhEnumObjectItems(NULL, NULL, pszObjectName,
                         NULL, &dwCounterSize,
                         NULL, &dwInstanceSize,
                         PERF_DETAIL_EXPERT, 0);

    if (dwCounterSize > 0)
        pCounterList = new TCHAR[dwCounterSize];

    if (dwInstanceSize > 0)
        pInstanceList = new TCHAR[dwInstanceSize];

    //Counter/Instance名のリスト取得
    ret = ::PdhEnumObjectItems(NULL, NULL, pszObjectName,
                               pCounterList, &dwCounterSize,
                               pInstanceList, &dwInstanceSize,
                               PERF_DETAIL_EXPERT, 0);

    if (ret == ERROR_SUCCESS)
    {
        //pInstanceListからInstance名があれば取得
        if (pInstanceList != NULL)
        {
            for (pTempInstance = pInstanceList, i = 0;
                 *pTempInstance != 0;
                 pTempInstance += (lstrlen(pTempInstance) + 1), i++
                )
            {
                //カウンタの説明取得用のInstance名
                if (!i)
                    pInstForExplain = pTempInstance;            //(C)

                _tprintf(_T("\tInstance% 3d : %s\n"), i, pTempInstance);
            }
        }

        //pCounterListから各Counter名/説明の取得
        if (pCounterList != NULL)
        {
            for (pTempCounter = pCounterList, i = 0;
                 *pTempCounter != 0;
                 pTempCounter += (lstrlen(pTempCounter) + 1), i++
                )
            {
                //Objectの説明を取得
                GetPdhCounterExplain(pszObjectName, pTempCounter, pInstForExplain,
                                     hQuery, &pExplain);                //(D)

                _tprintf(_T("\tCounter% 4d : %s -> %s\n"), i, pTempCounter, pExplain);

                delete [] pExplain;     pExplain = NULL;
            }
        }
    }

    if (pCounterList != NULL)
        delete [] pCounterList;

    if (pInstanceList != NULL)
        delete [] pInstanceList;
}

//<PDH系API> Object/Counterに対応するカウンタの説明取得
//第一引数: Performance Object名
//第二引数: Counter名
//第三引数: Instance名
//第四引数: 問合せへのハンドル(PdhGetCounterInfo APIで使用)
//第五引数: この変数に該当Counterの説明を受け取る
//戻値    : 成功TRUE 失敗時は*ppszExplain は何もされない
//仕様    : 第一〜第三引数に対応するCounterの説明を第五引数に受取る
//          第五引数はLPTSTR型のアドレスを指定するが、必要なメモリの確保は関数内で行うので
//          必要なし。但しこの関数実行後、delete [] *ppszExplainで解放すること。
BOOL GetPdhCounterExplain(LPCTSTR pszObjectName,
                          LPCTSTR pszCounterName,
                          LPCTSTR pszInstanceName,
                          HQUERY hQuery,
                          LPTSTR *ppszExplain)
{
    TCHAR             tCounterPath[MAX_COUNTER_PATH]; //カウンタパス用
    LPTSTR            pExplain     = NULL;            //説明文字列
    HCOUNTER          hCounter     = 0;               //カウンタへのハンドル
    DWORD             dwSize       = 0;               //PDH_COUNTER_INFO構造体サイズ
    PPDH_COUNTER_INFO pCounterInfo = NULL;            //PDH_COUNTER_INFO構造体へのPointer
    //カウンタパスの作成
    if (_stprintf_s(tCounterPath, MAX_COUNTER_PATH, _T("\%s(%s)\%s"),
                                  pszObjectName, pszInstanceName, pszCounterName

                   ) == -1
        )
        return FALSE;

    //カウンタの追加
    if (::PdhAddCounter(hQuery, tCounterPath, 0, &hCounter) != ERROR_SUCCESS) //(E)
        return FALSE;

    while (TRUE)
    {
        //PDH_COUNTER_INFO構造体のサイズ取得
        ::PdhGetCounterInfo(hCounter, TRUE, &dwSize, NULL);     //(F)

        //PDH_COUNTER_INFO構造体のメモリ確保
        pCounterInfo = (PPDH_COUNTER_INFO)::HeapAlloc(::GetProcessHeap(), 
                                                        HEAP_ZERO_MEMORY, dwSize);      
        if (!pCounterInfo)
            break;

        //カウンタ情報取得
        if (::PdhGetCounterInfo(hCounter, 
                                TRUE, 
                                &dwSize, 
                                pCounterInfo) != ERROR_SUCCESS
           )                                                    //(G)
            break;

        //結果文字列のためのメモリ確保
        pExplain = new TCHAR[lstrlen(pCounterInfo->szExplainText) + 1];  //(H)

        //pCounter->szExplainTextを結果出力用メモリ領域にコピー
        if (!lstrcpy(pExplain, pCounterInfo->szExplainText))  //(H)
        {
            delete [] pExplain;     break;
        }

        //出力文字列へのポインタコピー
        *ppszExplain = pExplain;    //(H)

        break;
    }

    if (pCounterInfo)
        ::HeapFree(::GetProcessHeap(), 0, pCounterInfo);

    if (hCounter)
        ::PdhRemoveCounter(hCounter);

    return TRUE;
}

コードがちょっと長くなったので簡単に説明しておきます。
まず、pdh.hをインクルードして、pdh.libをリンクします。GetPdhObjectList、 GetPdhObjectItemList、GetPdhCounterExplainの各関数でそれぞれ、オブジェクト名、 カウンタ名、カウンタに対応する説明をそれぞれ取得してコンソールに出力しています。

・GetPdhObjectList
(A)でpszObjectListにMULTI_SZ形式でオブジェクトの一覧を受け取ります。MULTI_SZ形式は文字列が 以下のように並んでいます。

文字列1\0文字列2\0 文字列3\0・・・文字列X\0\0

これを(B)で切り出してコンソール出力します。そのオブジェクト名を引数にして GetPdhObjectItemListを呼び出してカウンタ名・インスタンス名を取得します。 カウンタ名・インスタンス名にも同様のことが言えますが、上記例では すべてのオブジェクト名を表示しません。
PdhGetEnumObjets APIの第五引数にPERF_DETAIL_EXPERT(赤文字で表示)がありますが、 取得するオブジェクトのレベルを表しています。このレベルでは大部分のオブジェクト名は取得できますが、 システム設計者レベルまでのオブジェクトは得ることはできません。すべてのオブジェクト名を 得ようとしたらPERF_DETAIL_WIZARDを指定してください。(MSDNのPdhGetEnumObjectsを参照のこと)

・GetPdhObjectItemList
GetPdhObjectと同様で、PdhGetEnumObjectItems APIでpCounterListとpInstanceListに それぞれMULTI_SZ形式でカウンタ名・インスタンス名を受け取ります。インスタンスが あるオブジェクトは説明を得るためのカウンタパスを作成するために(C)で インスタンス名を確保します。このインスタンス名とカウンタ名、引数で受け取った オブジェクト名でカウンタの説明を得るために(D)でGetPdhCounterExplainを呼び出します。

・GetPdhCounterExplain
引数から(E)で問合せにカウンタを追加します。(F)で一度GetCounterInfoを呼んで PDH_COUNTER_INFO構造体の必要サイズをdwSizeに受け取ります。そのサイズ分のメモリを確保して 再度GetCounterInfoを呼んでPDH_COUNTER_INFOに情報を受け取り(G)、説明がある szExplainTextメンバを第五引数に渡す処理を行います。(H)

※Windows XPでコンソールに出力する場合は問題なかったのですが、 Windows2000の場合はカウンタの説明が各カウンタ、途中までしか出力できなくなりました。 ここでプロジェクトの文字セットをマルチバイトに設定していて、 カウンタの説明を受け取る(F)で呼ばれるGetCounterInfoはANSIバージョンのGetCounterInfoAで、 ここで受け取るサイズがANSIのため正しくサイズを受け取ることができないという理由からくる 不具合のようです。回避方法は、プロジェクトのプロパティで文字セットをUnicodeにすることですが、 この度はCmd.exeコンソールにうまく出力できません。なので、上記コードのうち、コンソール出力部分を WideCharToMultiByte APIで変換して出力すれば回避できます。

それでは次にレジストリデータを使って オブジェクト・カウンタおよびその説明を取得してみます。