◆パフォーマンスモニタ returns 〜 カスタムパフォーマンスモニタの作成 その7

[ ホーム ] [ 上へ ]

(PerfDnp.dllの実装)

PerfDnp.dll内のOpen関数、Collect関数、Close関数はコールバック関数です。 で、何から呼ばれるかというとパフォーマンスモニタシステムです。DLL外から呼ばれるので エクスポートしなければなりません。__declspec(dllexport)を使う方法と、定義ファイル (.defファイル)を使う方法がありますが、今回は後者でやるので早速ファイルを作ります。

ソリューションエクスプローラの PerfDnpプロジェクトフォルダを右クリックして追加(D)→新しい項目(W)で新しい項目の 追加ダイアログが起動しますので以下の設定をしてください。

カテゴリ(C)    :コード
テンプレート(T):モジュール定義ファイル(.def)
ファイル名(N)  :PerfDnp.def
→追加(A)

作成されたPerfDnp.defファイルに次のように記述したのち保存してください。

LIBRARY "PerfDnp"

EXPORTS
    OpenDnpPerformanceData        @1   
    CollectDnpPerformanceData     @2   
    CloseDnpPerformanceData       @3

次に実装ファイルに宣言してもいいですが、まあ、流儀なので3つの関数の プロトタイプを宣言するためにヘッダファイルを作ります。
ソリューションエクスプローラのPerfDnpプロジェクトフォルダを右クリックして 追加(D)→新しい項目(W)で新しい項目の追加ダイアログが起動しますので 以下の設定をしてください。

カテゴリ(C)    :コード
テンプレート(T):ヘッダファイル(.h)
ファイル名(N)  :PerfDnp.h
→追加(A)

作成されてPerfDnp.hファイルに次のように記述した後保存してください。

//PerfDnp.h

//RegQueryValueExでHKEY_PERFORMANCE_DATAを開いてパフォーマンスデータを取得する際に
//システムから呼ばれる、Open,Collect,Close各関数:ヘッダファイル
#pragma once

//--エクスポート関数-------------------------------------------------------------------

//コントロールパネル→管理ツール→パフォーマンスなどパフォーマンスアプリケーションで
//初めにシステムより呼ばれる関数
DWORD WINAPI OpenDnpPerformanceData(LPWSTR lpDeviceNames);

//パフォーマンスアプリケーションがRegQueryValueExで
//パフォーマンスデータを要求する毎に呼ばれる関数
DWORD WINAPI CollectDnpPerformanceData(IN     LPWSTR lpwszValue,
                                       IN OUT LPVOID *lppData,
                                       IN OUT LPDWORD lpcbTotalBytes,
                                       IN OUT LPDWORD lpNumObjectTypes);

//パフォーマンスアプリケーションがRegCloseKeyで
//HKEY_PAFORMANCE_KEYを閉じたときに呼ばれる関数
DWORD WINAPI CloseDnpPerformanceData();
//-------------------------------------------------------------------------------------

//RegQueryValueExの第二引数に指定された文字列がこのパフォーマンスデータを
//取得するよう要求しているか
//(Global, 又はPerfDnpパフォーマンスオブジェクトIDを指定しているか)
BOOL IsRetrieveType(LPCWSTR lpwszValue, DWORD dwObjectId);

Open関数で初期化をして、Collect関数で実際にデータを収集して パフォーマンスデータブロック をパフォーマンスシステムに渡しますが、その方法は次のようです。

パフォーマンスシステムは必要な パフォーマンスデータブロック の容量を推定して確保後、Collect関数の第二引数にその領域の先頭アドレスへのポインタを渡します。 プログラマはPERF_OBJECT_TYPE、PERF_COUNTER_DEFINITION X カウンタの数、 (PERF_INSTANCE_DEFINITION + カウンタデータ ) X インスタンス数 という形式でシステムから渡された領域にコピーをして、作成します。
このパフォーマンスデータブロック の形式をよくみると、PERF_OBJECT_TYPEとPERF_COUNTER_DEFINITIONはデータ内容はともかく、 メンバの各位置は固定なので、初期化のOpen関数で初期化しておくのが便利です。実際これらの 構造体のデータ内容で、Collect関数の呼出のたびに変更されるメンバは、 PERF_OBJECT_TYPEのTotalByteLengthメンバ、NumInstancesメンバだけです。(PerfTime, PerfFreqメンバは システムによって扱われます。)、そこで、Open関数内で初期化しやすいように2種類の構造体を まとめたデータ構造(構造体)を用意します。また、PERF_COUNTER_BLOCK以下のカウンタデータも 扱いやすいようにまとまった構造体を用意します。

ソリューションエクスプローラの PerfDnpプロジェクトフォルダを右クリックして追加(D)→クラス(C)で クラスの追加ダイアログが起動しますので以下の設定をしてください。

カテゴリ(C)    :C++
テンプレート(T):C++クラス
→追加(A)

汎用C++クラスウィザードダイアログで以下の設定をしてください。

クラス名(L):CDataDnp
→完了

DataDnp.hとDataDnp.cppの2つのファイルにCDataDnpの 宣言と実装のスケルトンができていますが、作ろうとしているのは構造体なので すべてコメントアウトして以下のように構造体を定義してください。

/* DataDnp.h
Dnpダイアログカウンタデータ(経過時間・コントロール背景色)定義:ヘッダファイル
(RegQueryValuEx(Win32API)でカウンタデータを収集する際に使われるデータフォーマット)
*/
#pragma once

/*パフォーマンスデータのタイプがPERF_SIZE_LARGE(winperf.h参照)を含む場合、データサイズが
8バイト(64ビット)になるので構造体のアラインメントを固定*/
#pragma pack (8)

//カウンタオブジェクト/カウンタ定義構造体
typedef struct _DNP_DATA_DEFINITION
{
    PERF_OBJECT_TYPE             DnpObjType;
    PERF_COUNTER_DEFINITION      ElapseDef;
    PERF_COUNTER_DEFINITION      ColorDef;

    _DNP_DATA_DEFINITION();                       //コンストラクタ

} DNP_DATA_DEFINITION, *LPDNP_DATA_DEFINITION;


//カウンタデータ構造体
typedef struct _DNP_COUNTER
{
    PERF_COUNTER_BLOCK           CntrBlock;
    DWORD                        dwReserved;      //8バイトアラインメント用
    DWORD                        dwElapsed;
    COLORREF                     clBkColor;
} DNP_COUNTER, *LPDNP_COUNTER;
#pragma pack ()
//DataDnp.cpp
//カウンタオブジェクト/カウンタ定義構造体初期化:実装ファイル
#include "StdAfx.h"
#include <winperf.h>
#include "DnpCtrOffset.h"
#include "DataDnp.h"

//コンストラクタ
_DNP_DATA_DEFINITION::_DNP_DATA_DEFINITION()
{
    //Dnp Application Counter Object Initialize
    DnpObjType.TotalByteLength = sizeof(DNP_DATA_DEFINITION) 
                                          + sizeof(DNP_COUNTER); //Collect関数実行時変更
    DnpObjType.DefinitionLength = sizeof(DNP_DATA_DEFINITION);  
    DnpObjType.HeaderLength = sizeof(PERF_OBJECT_TYPE);  
    DnpObjType.ObjectNameTitleIndex = DNP_OBJ;   //Open関数実行時にFirst Counter値を加算
    DnpObjType.ObjectNameTitle = NULL;  
    DnpObjType.ObjectHelpTitleIndex = DNP_OBJ;   //Open関数実行時にFirst Counter値を加算
    DnpObjType.ObjectHelpTitle = NULL;  
    DnpObjType.DetailLevel = PERF_DETAIL_NOVICE; //PdhEnumObjectsなどで使用される
    DnpObjType.NumCounters = ( sizeof(DNP_DATA_DEFINITION) - sizeof(PERF_OBJECT_TYPE) ) /  
                                    sizeof(PERF_COUNTER_DEFINITION);
    DnpObjType.DefaultCounter = 0;  
    DnpObjType.NumInstances = PERF_NO_INSTANCES; //Collect関数実行時変更
    DnpObjType.CodePage = 0;  

    //Elapsed Time Counter Definition Initialize
    ElapseDef.ByteLength = sizeof(PERF_COUNTER_DEFINITION);  
    ElapseDef.CounterNameTitleIndex = DNP_ELAPSED; //Open関数実行時にFirst Counter値を加算
    ElapseDef.CounterNameTitle = NULL;  
    ElapseDef.CounterHelpTitleIndex = DNP_ELAPSED; //Open関数実行時にFirst Counter値を加算
    ElapseDef.CounterHelpTitle = NULL;  
    ElapseDef.DefaultScale = 0;  
    ElapseDef.DetailLevel = PERF_DETAIL_NOVICE;  
    ElapseDef.CounterType = PERF_COUNTER_RAWCOUNT;  
    ElapseDef.CounterSize = sizeof(time_t);  
    ElapseDef.CounterOffset = (DWORD_PTR)&( ((LPDNP_COUNTER)0)->dwElapsed );  

    //Background Color Counter Definition Initialize
    ColorDef.ByteLength = sizeof(PERF_COUNTER_DEFINITION);  
    ColorDef.CounterNameTitleIndex = DNP_COLOR;  //Open関数実行時にFirst Counter値を加算
    ColorDef.CounterNameTitle = NULL;  
    ColorDef.CounterHelpTitleIndex = DNP_COLOR;  //Open関数実行時にFirst Counter値を加算
    ColorDef.CounterHelpTitle = NULL;  
    ColorDef.DefaultScale = 0;  
    ColorDef.DetailLevel = PERF_DETAIL_NOVICE;  
    ColorDef.CounterType = PERF_COUNTER_RAWCOUNT_HEX;  
    ColorDef.CounterSize = sizeof(COLORREF);  
    ColorDef.CounterOffset = (DWORD_PTR)&( ((LPDNP_COUNTER)0)->clBkColor );  
}

それでは、最後にPerfDnpプロジェクトを作成したときにできていたPerfDnp.cppの実装、 つまりOpen関数、Collect関数、Close関数の実装を行います。

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

#include "DnpEventLog.h" //イベントログメッセージテキストファイル
#include "EventLog.h"    //イベントログ管理クラス
#include "..\\Dnp\\DnpShare.h"
#include "DnpArray.h"    //Dnpアプリケーション管理クラス
#include <winperf.h>
#include "DataDnp.h"     //Dnpパフォーマンスオブジェクト/カウンタ定義ヘッダ
#include <time.h>

#include "PerfDnp.h"

#ifdef _MANAGED
#pragma managed(push, off)
#endif

#define        MAX_INSTNAME_SIZE       16      //パフォーマンスインスタンス名最大文字数

//スレッドオープン数
DWORD          gdwOpenCount = 0;
//パフォーマンスモニタ初期化(最初のOpen関数へのアクセス成功フラグ)
BOOL           gbInit = FALSE;


//イベントログ
CEventLog              gEventlog;

//Systemにオブジェクト/カウンタ定義
//(PERF_OBJECT_TYPE, PERF_COUNTER_DEFINITION)のプロトタイプ
DNP_DATA_DEFINITION    gDnpDataDef;

//起動中Dnpアプリケーションの配列
CDnpArray              gDnpArray;

/*
パフォーマンスデータ要求時システムから呼ばれる最初の関数
引数 lpDeviceNames HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfDnp\Linkage
                   レジストリキーの中のExport値に保存されるUnicode 文字列
戻値 成功時 ERROR_SUCCESS それ以外が返された時は, Collect関数が呼ばれずデータ収集されない
仕様 ・呼出元のアプリケーションに返すデータ構造体の初期化
       (PERF_OBJECT_TYPE, PERF_COUNTER_DEFINITION, などの初期化)
     ・稼動中Dnpアプリケーションのデータ共有メモリ管理クラス初期化)
     ・イベントログ登録済みイベントソースのハンドル取得*/
DWORD WINAPI OpenDnpPerformanceData(LPWSTR lpDeviceNames)
{
    LONG      lRet        = ERROR_SUCCESS;
    HKEY      hKey        = NULL;
    DWORD     dwType      = 0;
    DWORD     dwSize      = sizeof(DWORD);
    DWORD     dwFirstCntr = 0;
    DWORD     dwFirstHelp = 0;

    do
    {
        //最初のスレッドがこの関数を呼んだ時
        if (! gdwOpenCount)
        {
            //イベントログ- 登録済みイベントソースハンドル取得
            if (! gEventlog.Register())
            {
                lRet = ERROR_EVENTLOG_CANT_START;          break;
            }

            //PerfDnpのCouter Indexが収められているレジストリキーOpen
            lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                               L"SYSTEM\\CurrentControlSet\\Services\\PerfDnp\\Performance",
                               0,
                               KEY_READ,
                               &hKey);
            if (lRet != ERROR_SUCCESS)
            {
                gEventlog.Report(EVENTLOG_ERROR_TYPE, PERFDNP_CANT_OPEN_PERF_KEY,
                                 &lRet, sizeof(LONG));
                break;
            }

            //PerfDnpの最初のCounter Index値取得
            lRet = ::RegQueryValueEx(hKey,
                                     L"First Counter",
                                     NULL,
                                     &dwType,
                                     (LPBYTE)&dwFirstCntr,
                                     &dwSize);
            if (lRet != ERROR_SUCCESS)
            {
                gEventlog.Report(EVENTLOG_ERROR_TYPE, PERFDNP_CANT_READ_FIRST_COUNTER,
                                 &lRet, sizeof(LONG));
                break;
            }

            //PerfDnpの最初のHelp Index値取得
            lRet = ::RegQueryValueEx(hKey,
                                     L"First Help",
                                     NULL,
                                     &dwType,
                                     (LPBYTE)&dwFirstHelp,
                                     &dwSize);
            if (lRet != ERROR_SUCCESS)
            {
                gEventlog.Report(EVENTLOG_ERROR_TYPE, PERFDNP_CANT_READ_FIRST_COUNTER,
                                 &lRet, sizeof(LONG));
                break;
            }

            //Systemに渡すPERF_OBJECT_TYPE, PERF_COUNTER_DEFINITIONの各インデックス値・
            //Default Counter値設定
            gDnpDataDef.DnpObjType.ObjectNameTitleIndex += dwFirstCntr;  
            gDnpDataDef.DnpObjType.ObjectHelpTitleIndex += dwFirstHelp;  
            gDnpDataDef.DnpObjType.DefaultCounter        = 0;  
            gDnpDataDef.ElapseDef.CounterNameTitleIndex += dwFirstCntr;  
            gDnpDataDef.ElapseDef.CounterHelpTitleIndex += dwFirstHelp;  
            gDnpDataDef.ColorDef.CounterNameTitleIndex  += dwFirstCntr;  
            gDnpDataDef.ColorDef.CounterHelpTitleIndex  += dwFirstHelp;  

            //稼動中Dnp管理オブジェクト初期化(共有メモリへのポインタ取得)
            lRet = gDnpArray.Initialize();
            if (lRet)
            {
                gEventlog.Report(EVENTLOG_ERROR_TYPE, PERFDNP_CANT_INIT_DNPAPPDATA,
                                 &lRet, sizeof(LONG));
                break;
            }

            gbInit = TRUE;
        } //gdwOpenCount == 0 処理ここまで

        gdwOpenCount++;

    } while(0);

    //終了処理 
    if (hKey)     ::RegCloseKey(hKey);

    return lRet;
}

/*
パフォーマンスデータ要求毎にシステムより呼ばれる関数
引数
lpwszValue   パフォーマンスデータ要求時に発行されるRegQueryValueExの第二引数に指定する
             文字列へのポインタが渡されている
             (ex. Global, Foreign, カウンタオブジェクトのIndex)
lppData      PERF_OBJECT_TYPEのデータを先頭としたカウンタ定義・データを返却する先頭位置
             へのポインタがシステムより渡されている
             この関数内でPERF_OBJECT_TYPE, PERF_COUNTER_DEFINITION, 
             PERF_INSTANCE_DEFINITION,カウンタデータを*lppDataからセットし、
             次のカウンタオブジェクトのデータが収めるべき位置をセットして返却する
lpcbTotalBytes PERF_OBJECT_TYPE〜カウンタデータまでのすべてのデータ格納に必要なバイト数を
               システムより概算で渡されている
               *lppDataに収められたすべてのデータのバイト数を返却する、最初に渡された値より
               収められるデータ量が大きければ0にセットして、ERROR_MORE_DATAを戻値にする
lpNumObjectTypes この関数で渡されるカウンタオブジェクトの数を渡す
                 lpcbTotakBytesで必要バイト数が不足したり、Open関数が実行されていなかったり
                 エラーの場合は、0にセットする
戻値
ERROR_SUCCESS又はERROR_MORE_DATAのいずれか。渡されたlpcbTotalBytesより必要バイト数が
多い時はERROR_MORE_DATAを返却。それ以外はERROR_SUCCESS

仕様
エラー時などデータを*lppDataに返却できない時はlpcbTotalBytes, lpNumObjectTypesを
0にセットしてERROR_SUCCESSを返す
成功時はlpcbTotalBytesに必要バイト数、lpNumObjectTypesにカウンタオブジェクト数、*lppDataに
次のカウンタオブジェクトのデータが収められるべき位置を返却する*/
DWORD WINAPI CollectDnpPerformanceData(IN     LPWSTR  lpwszValue,
                                       IN OUT LPVOID  *lppData,
                                       IN OUT LPDWORD lpcbTotalBytes,
                                       IN OUT LPDWORD lpNumObjectTypes)
{
    ULONG                         ulNeedSpace  = 0;
    LPDNP_DATA_DEFINITION         pDnpDataDef  = NULL;
    PERF_INSTANCE_DEFINITION      *pPerfInst   = NULL;
    DNP_COUNTER                   *pDnpCounter = NULL;

    if (! gbInit) //初期化完了確認
    {
        *lpcbTotalBytes   = 0;
        *lpNumObjectTypes = 0;                return ERROR_SUCCESS;
    }

    //RegQueryValuExの第二引数でGlobal又はこのカウンタオブジェクトのインデックスが
    //選択されているか
    if (! IsRetrieveType(lpwszValue,
                         gDnpDataDef.DnpObjType.ObjectNameTitleIndex))
    {
        *lpcbTotalBytes   = 0;
        *lpNumObjectTypes = 0;                return ERROR_SUCCESS;
    }

    //パフォーマンスデータ格納必要領域算出
    ulNeedSpace = sizeof(DNP_DATA_DEFINITION)
                    + ( sizeof(PERF_INSTANCE_DEFINITION)
                            + MAX_INSTNAME_SIZE * sizeof(WCHAR) + sizeof(DNP_COUNTER) )
                         * (gDnpArray.GetCount() > 0 ? gDnpArray.GetCount() : 1);

    if (ulNeedSpace > *lpcbTotalBytes)
    {
        *lpcbTotalBytes   = 0;
        *lpNumObjectTypes = 0;                return ERROR_MORE_DATA;
    }

    //パフォーマンスオブジェクト(PERF_OBJECT_TYPE)格納開始位置取得
    pDnpDataDef = (LPDNP_DATA_DEFINITION) *lppData;

    //PERF_OBJECT_TYPE, PERF_COUNTER_DEFINITIONを返却領域にコピー
    ::memmove_s( pDnpDataDef, sizeof(DNP_DATA_DEFINITION),
                                          &gDnpDataDef, sizeof(DNP_DATA_DEFINITION) );

    //最初のインスタンス
    pPerfInst = (PERF_INSTANCE_DEFINITION *) & pDnpDataDef[1];

    //各PERF_INSTANCE_DEFINITIONのデータセット& 各カウンタデータセット
    for (DWORD i=0; i<gDnpArray.GetCount(); i++)
    {
        DWORD     dwProcessId = gDnpArray.GetProcessId(i); //プロセスID
        WCHAR     wszProcessId[MAX_INSTNAME_SIZE];    //プロセスID文字列
        DWORD     dwNameSize  = 0;                    //wszProcessIdのバイト数(NULL文字含む)
        DWORD     dwOctSize   = 0;                    //dwNameSizeより大きいの倍数値
        time_t    tmCurrent   = 0;                    //現在時刻
        time_t    tmStart     = 0;                    //各プロセスダイアログ開始時刻
        double    dblElapsed  = 0;                    //ダイアログ表示時間

        //プロセスIDの文字列化
        ::_snwprintf_s(wszProcessId, MAX_INSTNAME_SIZE, _TRUNCATE, L"%d", dwProcessId);

        dwNameSize = ((DWORD)wcslen(wszProcessId) + 1) * sizeof(WCHAR); //文字列のバイト数
        dwOctSize = (dwNameSize + sizeof(LONGLONG) - 1)
                                / sizeof(LONGLONG) * sizeof(LONGLONG);  //8の倍数に調整

        //PERF_INSTANCE_DEFINITIONデータセット
        pPerfInst->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) + dwOctSize;  
        pPerfInst->ParentObjectTitleIndex = 0;  
        pPerfInst->ParentObjectInstance = 0;  
        pPerfInst->UniqueID = PERF_NO_UNIQUE_ID;  
        pPerfInst->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);  
        pPerfInst->NameLength = dwNameSize;  

        //インスタンス名コピー
        ::wcscpy_s( (WCHAR *) & pPerfInst[1], dwOctSize / sizeof(WCHAR), wszProcessId );

        //カウンタデータ位置セット
        pDnpCounter = (LPDNP_COUNTER)( (PBYTE)pPerfInst + pPerfInst->ByteLength );

        pDnpCounter->CntrBlock.ByteLength = sizeof(DNP_COUNTER);
        pDnpCounter->dwReserved = 0;

        //セマフォ獲得時
        if ( gDnpArray.Lock(i, 500) )
        {
            //共有メモリよりデータ取得-------------------------------------
            tmStart = ((LPSHARE_MEM)gDnpArray[i])->tmStart;
            pDnpCounter->clBkColor = ((LPSHARE_MEM)gDnpArray[i])->rgbColor; //カウンタデータ
            //-------------------------------------------------------------

            //セマフォ解放
            gDnpArray.UnLock(i);

            //経過時間演算
            ::time(&tmCurrent);
            dblElapsed = ::difftime(tmCurrent, tmStart);

            pDnpCounter->dwElapsed = tmStart ? (DWORD)dblElapsed : 0; //カウンタデータ

        } else //セマフォ獲得不可
        {
            pDnpCounter->clBkColor = 0;         pDnpCounter->dwElapsed = 0;
        }

        //次のインスタンス
        pPerfInst = (PERF_INSTANCE_DEFINITION *) & pDnpCounter[1];
    } //for インスタンス数分ループ

    //Dnpプロセスがない時- (1つPERF_INSTANCE_DEFINITION, カウンタデータ領域を作り0で満たす)
    if (! gDnpArray.GetCount())
    {
        DWORD     dwNonInstSize = sizeof(PERF_INSTANCE_DEFINITION)
                                + MAX_INSTNAME_SIZE * sizeof(WCHAR) + sizeof(DNP_COUNTER);

        ::memset(pPerfInst, 0, dwNonInstSize);

        //次のインスタンス(次のカウンタオブジェクトの開始位置)
        pPerfInst = (PERF_INSTANCE_DEFINITION *)( (PBYTE)pPerfInst + dwNonInstSize );
    }

    //PERF_OBJECT_TYPEの残りの設定
    pDnpDataDef->DnpObjType.TotalByteLength = (DWORD)
                                                   ((PBYTE)pPerfInst - (PBYTE)pDnpDataDef);
    pDnpDataDef->DnpObjType.NumInstances = gDnpArray.GetCount();

    //返却値設定
    *lppData          = (LPVOID) pPerfInst;
    *lpcbTotalBytes   = pDnpDataDef->DnpObjType.TotalByteLength;
    *lpNumObjectTypes = 1;

    return ERROR_SUCCESS;
}

/*
パフォーマンスデータ取得後RegCloseKeyでHKEY_PERFOMRMANCE_KEYが閉じられる時に呼ばれる関数
戻値 常にERROR_SUCCESS
仕様 Dnp各プロセスの供給メモリへの接続を解除
     イベントログ終了処理*/
DWORD WINAPI CloseDnpPerformanceData()
{
    //最後のスレッドの時
    if ( !(--gdwOpenCount) )
    {
        gDnpArray.Terminate();      //Dnp各プロセスとの接続終了処理
        gEventlog.Close();          //イベントログ終了処理
    }
    return ERROR_SUCCESS;
}
//---------------------------------------------------------------------------------------

/*
RegQueryValueExの第二引数に指定された文字列がこのパフォーマンスデータを
取得するよう要求しているか
(Global, 又はDnp ApplicationパフォーマンスオブジェクトIDを指定しているか)  
引数 lpwszValue RegQueryValuExの第二引数に指定された文字列(ex. Globalなど)
     dwObjectId パフォーマンスデータ要求に対応するカウンタオブジェクトID
                (このカウンタオブジェクト(Dnp Application)のID)  
戻値 RegQueryValueExの第二引数(lpwszValue)が"Global"又はこのオブジェクトIDを
     要求している時TRUE
     (外部からの要求(Foreign)は未サポートのためFALSE)*/
BOOL IsRetrieveType(LPCWSTR lpwszValue, DWORD dwObjectId)
{
    //Global確認
    if (! lpwszValue || *lpwszValue == L'\0')
        return TRUE;

    const WCHAR lpwszGlobal[] = L"Global";
    if ( _wcsnicmp(lpwszValue, lpwszGlobal, wcslen(lpwszGlobal)) == 0 )
        return TRUE;

    //Foreign確認
    const WCHAR lpwszForeign[] = L"Foreign";
    if ( _wcsnicmp(lpwszValue, lpwszForeign, wcslen(lpwszForeign)) == 0 )
        return FALSE;

    //Costly確認
    const WCHAR lpwszCostly[] = L"Costly";
    if ( _wcsnicmp(lpwszValue, lpwszCostly, wcslen(lpwszCostly)) == 0 )
        return FALSE;

    //各Item内のカウンタオブジェクトID確認
    size_t    size           = wcslen(lpwszValue) + 1;  
    WCHAR     *pwszValueList = new WCHAR[size];  
    BOOL      bRet           = FALSE;  
    WCHAR     *pwszItem      = NULL, *pwszNext = NULL,    *pwszEnd = NULL;  
    DWORD     dwValue        = 0;  

    do
    {
        if ( wcscpy_s(pwszValueList, size, lpwszValue) )
            break;

        //区切り文字(L" ")で要求されているオブジェクトIDを分解
        pwszItem = wcstok_s(pwszValueList, L" ", &pwszNext);

        while(pwszItem != NULL)
        {
            dwValue = wcstoul(pwszItem, &pwszEnd, 10);

            //分解した各IDが要求されているオブジェクトIDと一致していたらTRUEを返却
            if (dwValue == dwObjectId)
            {
                bRet = TRUE;          break;
            }

            pwszItem = wcstok_s(NULL, L" ", &pwszNext);
        }

    } while(0);

    delete [] pwszValueList;          pwszValueList = NULL;

    return bRet;
}

//エントリポイント
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD ul_reason_for_call,
                       LPVOID lpReserved
                      )
{
    return TRUE;  
}

#ifdef _MANAGED
#pragma managed(pop)
#endif

長い道のりでしたが以上ですべての作業が終わりました。 あとはビルドするだけですが、ビルドしてできる PerfDnp.dllはシステムディレクトリに置かないといけないですが、いちいちコピーするのは 面倒なので「ビルド後のイベント」で処理してしまいます。
ソリューションエクスプローラのPerfDnpプロジェクトフォルダを右クリックして 「プロパティ(R)」でプロパティページを表示して以下の設定をしてください。

構成(C) - すべての構成
    - 構成プロパティ  
        - ビルドイベント
            - ビルド後のイベント

コマンドライン : copy $(TargetPath) c:\windows\system32
→OK

これでソリューションをビルドするとDnp.exeとPerfDnp.dllが作成され、 DLLは自動的にシステムフォルダにコピーされます。

早速モニタリングしてみます。1つ以上のDnp.exeを実行したのち、 スタート→ファイル名を指定して実行(R)で
perfmon
を実行してパフォーマンスモニタユーティリティを起動して以下のように オブジェクト/カウンタが追加できるか確認の上、モニタしてみてください。

最後に2つのプロジェクトに様々な実装をしたので、作成&実装した ソリューションエクスプローラのスナップショットを掲載して終わりにします。

DnpプロジェクトPerfDnpプロジェクト

参考文献:Using Performance Counters
http://msdn.microsoft.com/en-us/library/aa373209(v=VS.85).aspx