(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