<レジストリデータでの一覧表示>
レジストリからパフォーマンスオブジェクト・カウンタ・インスタンスやその説明の情報を取得するには
ちょっと前提知識が必要になります。今回は名前と説明を取得するだけですが、オブジェクトに含まれる
カウンタの数、それぞれのカウンタのスケール、インスタンスのIDなどいろいろな情報があり、どのように
レジストリに格納されているか理解しないとなかなか実装までいきません。
ということで、先にパフォーマンス関連の情報がどのようにレジストリに蓄えられているかをプログラムの
流れとともに確認してみます。
(データ構造1)
オブジェクト名カウンタ名とカウンタの説明はそのインデックスとともに
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\011
(英語の場合は最後が009になります)キーにあります。このキーのCounterエントリにオブジェクト名と
カウンタ名、Helpエントリにカウンタの説明が納められています。レジストリエディタ(regedit.exe)で確認
してみてください。
どのように入っているかというとMULTI_SZ形式で以下のようになっています。
1●1847●2●System●4●Memory●6●%Processor Time
●8 ・・・・ 12346●Voltage●●
(●はNULL終端文字をあわします。)
偶数のインデックスと名前とが対になった文字列です。Helpエントリの場合は奇数の
インデックスになってます。ここから取り出せばいいのですが、どのカウンタがどのオブジェクトに
あるのかというような情報がまったく盛り込まれていないので一覧を作成できません。
そこででてくるのがもうひとつのキーである
HKEY_PERFORMANCE_DATAキーです。
(データ構造2)
実はHKEY_PERFORMANCE_DATAキーは
レジストリエディタで確認しようと思ってもないはずです。レジストリ関連のAPI
RegQueryValuExでエントリ名をGlobalに設定すると値を取得できるのですが、
実際はレジストリには存在してないということです。まあ、今回はそれよりこのキーで
返ってくるデータの方が重要です。レジストリエディタで確認できないので以下に書いておきます。
レジストリキーをHKEY_PERFORMANCE_DATA、
エントリをGlobalに指定してRegQueryValueExを
実行すると、以下のようなメモリブロックが作成されその先頭のアドレスがRegQueryValueEx
の第五引数に受け取ることができます。(図のAの先頭アドレス)
図の一マスは構造体のインスタンスを表し、その構造体の種類を色分けしています。
A(緑)・・・PERF_DATA_BLOCK構造体 パフォーマンスデータ全体の情報があります。(このメモリブロックのバイト数など) B(赤)・・・PERF_OBJECT_TYPE構造体 各パフォーマンスオブジェクトに関連する情報を中心に次のオブジェクトまでの バイト数(8)の部分なども格納されています。 C(黄)・・・PERF_COUNTER_DEFINITION構造体 BのPERF_OBJECT_TYPEのパフォーマンスオブジェクトに関連した カウンタの情報を中心に格納されています。 E(青)・・・PERF_INSTANCE_DEFINITION構造体 BのPERF_OBJECT_TYPEのパフォーマンスオブジェクトに関連した インスタンスの情報を中心に格納されています。 F(水色)・・・PERF_COUNTER_BLOCK構造体 (5)のバイト数が格納されています。G(白)、H(白)・・・これは構造体ではないので色分けしませんでした。 GはEのインスタンスのUnicodeでの名前(今回の取得対象)が、 HはFのインスタンスに対応するカウンタの数が格納されています。G(白)・・・構造体ではないので色をつけませんでした。EのインスタンスのUnicodeでの 名前(今回の取得対象)が格納されています。 H(白)・・・Eの1つのインスタンスに対応するすべてのカウンダデータ (Cのカウンタの数だけあります)が格納されています。
Hに関してですが、winperf.hのPERF_COUNTER_BLOCKのコメントには
// The PERF_COUNTER_BLOCK is followed by PERF_OBJECT_TYPE.NumCounters
// number of counters.
とあったため勘違いしていたようです。実際には直前のインスタンスに対応するカウンタデータが
収められています。
まず、全体の先頭にPERF_DATA_BLOCK(A)があり、それに続いて最初のオブジェクト(B)が 置かれ、(B)にあるカウンタ群(C)が列挙されます。すべてのカウンタが列挙されたあとは インスタンスの情報(E)が現れます。インスタンスとその関連情報(E,G,F,Hが1ユニット)が列挙されると、 次のオブジェクトが現れてオブジェクトの数だけ繰り返されています。
(1)〜(8)は構造体、各メモリブロックまでのバイト数をあらわし、 その数値はその先頭の構造体に情報が収められています。(例えば(7),(8)の場合はB, (4),(6)の場合はE)
(1)・・PERF_DATA_BLOCK.HeaderLengthメンバ
(最初のオブジェクトまでの距離)
(2)・・PERF_OBJECT_TYPE.HeaderLengthメンバ
(このオブジェクトに対応する最初のカウンタまでの距離)
(3)・・PERF_COUNTER_DEFINITION.ByteLengthメンバ
(構造体のサイズ/次のカウンタまでの距離)
(4)・・PERF_INSTANCE_DEFINITION.ByteLengthメンバ
(インスタンス名を含む構造体のサイズ)
(5)・・PERF_COUNTER_BLOCK.ByteLengthメンバ
(この構造体から次のインスタンスまでの距離)
(6)・・PERF_INSTANCE_DEFINITION.NameOffsetメンバ
(インスタンス名が保持されている文字列までの距離)
(7)・・PERF_OBJECT_TYPE.DefinitionLengthメンバ
(このオブジェクトに対応する最初のインスタンスまでの距離)
(8)・・PERF_OBJECT_TYPE.TotalByteLengthメンバ
(このオブジェクトから次のオブジェクトまでの距離)
肝心のオブジェクト名・インスタンス名・カウンタ名・カウンタの説明はどこ? ということになりますが、インスタンス名以外は、このメモリブロックには直接収められていません。 その代わりここ で取得した各名称に対応するインデックスという形で保持されています。
オブジェクト名インデックス・・・PERF_OBJECT_TYPE.ObjectNameTitleIndexメンバ
オブジェクトの説明インデックス・・・PERF_OBJECT_YPE.ObjectHelpTitleIndexメンバ
カウンタ名インデックス・・・PERF_COUNTER_DEFINITION.CounterNameTitleIndexメンバ
カウンタの説明インデックス・・・PERF_COUNTER_DEFINITION.CounterHelpTitleIndexメンバ
このインデックスを使ってデータ構造1
からオブジェクト名・カウンタ名、それぞれの説明を得ることができます。
インスタンス名はここのGに格納されているので
PERF_INSTANCE_DEFINITION(E)へのポインタを取得できたら(6)の距離
(PERF_INSTANCE_DEFINITION.NameOffsetメンバ)分だけポインタ演算をすれば
直接インスタンス名を得ることができます。
また、インスタンスについてはThreadオブジェクトなど
親インスタンスが必要なオブジェクトがあります。この場合は、
PERF_INSTANCE_DEFINITIONのParentObjectTitleIndexですべてのPERF_OBJECT_TYPEの
ObjectNameTitleIndexを検索し、マッチしたオブジェクトからParentObjectInstanceで
目的のPERF_INSTANCE_DEFINITIONを探し出す作業をします。
ということで、オブジェクト名・カウンタ名・インスタンス名はそれぞれ、 PERF_OBJECT_TYPE、PERF_COUNTER_DEFINITION、PERF_INSTANCE_DEFINITIONの 各インスタンスを取得すればいいことになります。
オブジェクト名については、RegQueryValueExで得たPERF_DATA_BLOCKへの ポインタに(1)を加えれば最初のPERF_OBJECT_TYPEインスタンスへのポインタが得られ、 このポインタに(8)の距離を加えれば、次のオブジェクトインスタンスへのポインタが取得でき、 これを繰り返せば目的は達成できます。
カウンタ名についてはPERF_OBJECT_TYPEから(2)の距離を加えて最初の PERF_COUNTER_DEFINITIONへのポインタを取得したら、(3)の距離を加えるポインタ演算を 順番にしていけばいいです。
インスタンス名についてはPERF_OBJECT_TYPEのポインタに(7)を加える ポインタ演算をして最初のインスタンスを得たら、(4)と(5)バイト数を加えていけば最後まで PERF_INSTANCE_DEFINITIONを得ることができます。
以上でレジストリデータの構造とオブジェクト名・ カウンタ名・インスタンス名とその説明の取得法を書いたので最後にコードを掲載しておきます。
// xxxxxx.cpp : コンソールアプリケーションのエントリポイントを定義します。 // #include "stdafx.h" #include "pdh.h" #pragma comment(lib, "pdh.lib") //////////////////プロトタイプ宣言//////////////////////// //<Registry使用> Performance Object/Instance/Counter名・各説明の取得 void GetPerfDataFromRegistry(); //<Registry使用> RegistryよりObject/Counter又は説明データの取得 BOOL GetRawDataFromRegistry(LPTSTR **pppszStringArray, DWORD *pdwSize, LPTSTR *ppszRawString, BOOL bHelp = FALSE); //<Registry使用> Performance Instanceの親インスタンス名の取得 LPTSTR GetParentInstanceName(DWORD dwParentObjIdx, DWORD dwParentInstIdx, const PPERF_DATA_BLOCK pPerfData); //////////////////////////////////////////////////////// //メイン関数 int _tmain(int argc, _TCHAR* argv[]) { GetPerfDataFromRegistry(); return 0; } //<Registry使用> Performance Object/Instance/Counter名・各説明の取得 void GetPerfDataFromRegistry() { //取得するPERF_DATA_BLOCKのサイズ DWORD dwPerfDataSize = 102400; DWORD dwTempSize = 102400; //パフォーマンス情報全体を保持する構造体 PPERF_DATA_BLOCK pPerfData = NULL; //各Performance Object情報を保持する構造体 PPERF_OBJECT_TYPE pPerfObject = NULL; //Objectに対応するInstance情報を保持する構造体 PPERF_INSTANCE_DEFINITION pPerfInstance = NULL; //Objectに対応するCounter情報を保持する構造体 PPERF_COUNTER_DEFINITION pPerfCounter = NULL; //各PERF_INSTANCE_DEFINITIONの後に続く構造体 PPERF_COUNTER_BLOCK pCounterBlk = NULL; //Instanceの親インスタンス名 LPTSTR pParentInstName = NULL; //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\011 //キーの各エントリの文字列格納用 LPTSTR *ppStringArray = NULL; //Object/Counter/Instance名文字列の配列 DWORD dwStringArraySize = 0; //ppStringArrayの配列のサイズ LPTSTR pRawString = NULL; //上記キーのCounterエントリの文字列 LPTSTR *ppExplainArray = NULL; //各Object/Counterの説明の文字列の配列 DWORD dwExplainArraySize = 0; //ppExplainArryaの配列のサイズ LPTSTR pRawExplain = NULL; //上記キーのHelpエントリの文字列 //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\011 //キーのCounterエントリの文字列取得&インデックスに対応するの要素番号に //Object/Counter/Instance名格納 if (!GetRawDataFromRegistry(&ppStringArray, &dwStringArraySize, &pRawString)) return; //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\011 //キーのHelpエントリの文字列取得&インデックスに対応する要素番号に //Object/Counterの説明格納 if (!GetRawDataFromRegistry(&ppExplainArray, &dwExplainArraySize, &pRawExplain, TRUE) ) return; //pPerfDataにPERF_DATA_BLOCKのデータを取得 while (::RegQueryValueEx(HKEY_PERFORMANCE_DATA, _T("Global"), NULL, NULL, (LPBYTE)pPerfData, &dwTempSize) == ERROR_MORE_DATA ) { if (pPerfData) delete [] pPerfData; dwPerfDataSize += 1024; dwTempSize = dwPerfDataSize; pPerfData = (PPERF_DATA_BLOCK) new BYTE[dwPerfDataSize]; } //最初のPerformance Object取得 pPerfObject = (PPERF_OBJECT_TYPE)((PBYTE)pPerfData + pPerfData->HeaderLength); //Performance Object列挙 for (DWORD i=0; i<pPerfData->NumObjectTypes; i++) { _tprintf(_T("Object%d : %s -> %s\n"), i, ppStringArray[pPerfObject->ObjectNameTitleIndex], ppExplainArray[pPerfObject->ObjectHelpTitleIndex]); //ObjectにInstanceが存在する場合 if (pPerfObject->NumInstances > 0) { //最初のPerformance Instance 取得 pPerfInstance = (PPERF_INSTANCE_DEFINITION)((PBYTE)pPerfObject + pPerfObject->DefinitionLength); //インスタンス列挙 for (LONG j=0; j<pPerfObject->NumInstances; j++) { //現在のインスタンス(pPerfInstance)の親Instance名取得(存在する場合) if (pPerfInstance->ParentObjectTitleIndex > 0) { pParentInstName = GetParentInstanceName(pPerfInstance->ParentObjectTitleIndex, pPerfInstance->ParentObjectInstance, pPerfData); _tprintf(_T("\tInstance% 3d : %S/%S\n"), j, pParentInstName, (TCHAR *)((PBYTE)pPerfInstance + pPerfInstance->NameOffset)); } else //親インスタンスがない時 { _tprintf(_T("\tInstance% 3d : %S\n"), j, (TCHAR *)((PBYTE)pPerfInstance + pPerfInstance->NameOffset)); } //次のインスタンス pCounterBlk = (PPERF_COUNTER_BLOCK)((PBYTE)pPerfInstance + pPerfInstance->ByteLength); pPerfInstance = (PPERF_INSTANCE_DEFINITION)((PBYTE)pCounterBlk + pCounterBlk->ByteLength); } } //最初のCounter取得 pPerfCounter = (PPERF_COUNTER_DEFINITION)((PBYTE)pPerfObject + pPerfObject->HeaderLength); //Performance Counter列挙// for (DWORD k=0; k<pPerfObject->NumCounters; k++) { _tprintf(_T("\tCounter% 4d : %s -> %s\n"), k, ppStringArray[pPerfCounter->CounterNameTitleIndex], ppExplainArray[pPerfCounter->CounterHelpTitleIndex]); //次のCounte取得 pPerfCounter = (PPERF_COUNTER_DEFINITION)((PBYTE)pPerfCounter + pPerfCounter->ByteLength); } //次のPerfomance Object pPerfObject = (PPERF_OBJECT_TYPE)((PBYTE)pPerfObject + pPerfObject->TotalByteLength); _tprintf(_T("\n")); } //各データの後始末 delete [] ppStringArray; delete [] pRawString; delete [] ppExplainArray; delete [] pRawExplain; delete [] pPerfData; //キーのクローズ(WINPEF.Hを参照) ::RegCloseKey(HKEY_PERFORMANCE_DATA); } //<Registry使用> RegistryよりObject/Counter又は説明データの取得 //第一引数: 第三引数で取得したMULTI_SZ形式のObject/Counter/Instanceのインデックスと // 名前文字列へのポインタを配列に格納して受け取る // (*pppszStringArrayに結果を受け取る) // (例) // 2 ObjectA 4 CounterB 6 CounterD・・・ (第三引数で受け取るMULTI_SZ形式) // ↓ // (*pppszStringArray)[2]=ObjectA, (*pppszStringArray)[4]=CounterB, // としてこの引数に出力 //第二引数: 第一引数の文字列の配列の要素数(*pppszStringArrayの要素数)を出力 //第三引数: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\011 // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009 // キーのCounterまたはHelpエントリにある文字列を出力 // (Object/Counter/Instance名又はその説明と対応するIndexのMULTI_SZ形式の文字列) //第四引数: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\011 // のCounterエントリ(Object/Counter/Instance名)を取得する時はFALSE, // Helpエントリ(Object/Counter/Instanceに対応した説明)を取得する時はTRUE //戻値 : 成功TRUE //仕様 : 第一、第三引数は呼出し側で必要メモリを確保する必要はないが、使い終わったら // delete [] *pppszStringArray, delete [] *ppszRawStringで解放する事 BOOL GetRawDataFromRegistry(LPTSTR **pppszStringArray, DWORD *pdwSize, LPTSTR *ppszRawString, BOOL bHelp /*=FALSE*/) { HKEY hPerflibKey; //レジストリキーハンドル取得用 DWORD dwLastIdx = 0; //Counter名に対応したインデックスの最後の値 DWORD dwIdxSize = sizeof DWORD; //dwLastIdxのサイズ LPTSTR *ppStringArray = NULL; //第一引数に対応する各Counter名などを収める文字列の配列 LPTSTR pRawString = NULL; //第三引数に対応するRegistryから取得したMULTI_SZ文字列 LPTSTR pCurrentString = NULL; //pRawStringから切り出したObject/Counter名の保持用 DWORD dwMaxLen = 0; //レジストリキーのすべてのエントリの中で最大の文字数取得 //第一〜第三引数のうちつでもNULLの場合は終了 if (!pppszStringArray || !ppszRawString || !pdwSize) return FALSE; //SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflibキーのハンドル取得 if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib"), 0, KEY_READ, &hPerflibKey) != ERROR_SUCCESS) { return FALSE; } //Counter名またはその説明の最後のインデックス番号取得 if (::RegQueryValueEx(hPerflibKey, bHelp ? _T("Last Help") : _T("Last Counter"), NULL, NULL, (LPBYTE)&dwLastIdx, &dwIdxSize) != ERROR_SUCCESS) { ::RegCloseKey(hPerflibKey); return FALSE; } ::RegCloseKey(hPerflibKey); //第一引数に出力する(最後のインデックス番号+1)要素数の文字列へポインタの配列作成 ppStringArray = new LPTSTR[dwLastIdx+1]; for (UINT i=0; i<dwLastIdx+1; i++) ppStringArray[i] = NULL; //第三引数に出力する文字列が格納されているレジストリキーハンドル取得 if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\011"), 0, KEY_READ, &hPerflibKey) != ERROR_SUCCESS) { delete [] ppStringArray; return FALSE; } //開いたレジストリキーの各エントリの中で最大の文字数を取得 if (::RegQueryInfoKey(hPerflibKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwMaxLen, NULL, NULL) != ERROR_SUCCESS) { delete [] ppStringArray; ::RegCloseKey(hPerflibKey); return FALSE; } //第三引数に出力するMULTI_SZ文字列用のメモリ確保 pRawString = new TCHAR[++dwMaxLen]; //Counter又は説明の文字列を取得 if (::RegQueryValueEx(hPerflibKey, bHelp ? _T("Help") : _T("Counter"), NULL, NULL, (LPBYTE)pRawString, &dwMaxLen) != ERROR_SUCCESS) { delete [] pRawString; delete [] ppStringArray; ::RegCloseKey(hPerflibKey); return FALSE; } ::RegCloseKey(hPerflibKey); //取得したpRawStringを切り出してインデックスに対応したppStringArray配列の要素番号に //Counter/Instance名などを格納 for (pCurrentString = pRawString; *pCurrentString != 0; pCurrentString += (lstrlen(pCurrentString) + 1) ) { DWORD dwIdx = 0; dwIdx = _tstol(pCurrentString); pCurrentString += (lstrlen(pCurrentString) + 1); ppStringArray[dwIdx] = pCurrentString; } //引数に結果を渡す *pppszStringArray = ppStringArray; *ppszRawString = pRawString; *pdwSize = dwLastIdx + 1; return TRUE; } //<Registry使用> Performance Instanceの親インスタンス名の取得 //第一引数: 親Performance Objectのインデックス番号 // (PERF_OBJECT_TYPE構造体のObjectNameTitleIndexメンバ) //第二引数: PERF_OBJECT_TYPEに続くPERF_INTANCE_DEFINITION構造体のインデックス(順番) //第三引数: HKEY_PERFORMANCE_DATAキーで取得したPERF_DATA_BLOCK構造体へのポインタ //戻値 : RegQueryValueで取得したPERF_DATA_BLOCKに続く該当PERF_INSTANCE_DEFINITION // のNameOffsetメンバにより取得するUnicodeのInstance名 // (WINPERF.HのPERF_INSTANCE_DEFINITION参照) LPTSTR GetParentInstanceName(DWORD dwParentObjIdx, DWORD dwParentInstIdx, const PPERF_DATA_BLOCK pPerfData) { //現在のPerformance Objectへのポインタ PPERF_OBJECT_TYPE pPerfObject = NULL; //現在のPerformance Instanceへのポインタ PPERF_INSTANCE_DEFINITION pPerfInstance = NULL; //PPER_COUNTER_BLOCKへのポインタ PPERF_COUNTER_BLOCK pPerfCounterBlk = NULL; DWORD i = 0; //最初のPerformance Object pPerfObject = (PPERF_OBJECT_TYPE)((PBYTE)pPerfData + pPerfData->HeaderLength); //第一引数で指定した親Objectのインデックスと一致するPERF_OBJECT_TYPEを検索 for (i = 0; (i < pPerfData->NumObjectTypes) && (pPerfObject->ObjectNameTitleIndex != dwParentObjIdx); i++) { //次のPerformance Object pPerfObject = (PPERF_OBJECT_TYPE)((PBYTE)pPerfObject + pPerfObject->TotalByteLength); } //該当Performance Objectがない時 if (i >= pPerfData->NumObjectTypes) return NULL; //該当Performance Objectの最初のInstance 取得 pPerfInstance = (PPERF_INSTANCE_DEFINITION)((PBYTE)pPerfObject + pPerfObject->DefinitionLength); //第二引数で指定した親InstanceのIndexと一致するPERF_INSTANCE_DEFINITIONを検索 for (i = 0; (static_cast<LONG>(i) < pPerfObject->NumInstances) && (i != dwParentInstIdx); i++) { //次のInstance pPerfCounterBlk = (PPERF_COUNTER_BLOCK)((PBYTE)pPerfInstance + pPerfInstance->ByteLength); pPerfInstance = (PPERF_INSTANCE_DEFINITION)((PBYTE)pPerfCounterBlk + pPerfCounterBlk->ByteLength); } //該当Performantce Instanceがない時 if (static_cast<LONG>(i) >= pPerfObject->NumInstances) return NULL; //親Instance名を返却 return (LPTSTR)((PBYTE)pPerfInstance + pPerfInstance->NameOffset); } |
これでどんなリソースのどんな活動状況があるかだいだい分かったので 最後に監視(モニタ)のデモをやってみます。