EternalWindows
PDH / カウンタとインスタンス

パフォーマンスモニタリングをするオブジェクトを決定したら、 次はそのオブジェクトのカウンタを決定することになります。 カウンタというのは、そのオブジェクト固有の一種のデータあり、 たとえば、プロセスならプロセスIDやスレッド数などが含まれています。 オブジェクトが公開しているカウンタは、PdhEnumObjectItemsで列挙することができます。

PDH_STATUS PdhEnumObjectItems(
  LPCTSTR szDataSource,
  LPCTSTR szMachineName,
  LPCTSTR szObjectName,
  LPTSTR mszCounterList,
  LPDWORD pcchCounterListLength,
  LPTSTR mszInstanceList,
  LPDWORD pcchInstanceListLength,
  DWORD dwDetailLevel,
  DWORD dwFlags
);

szDataSourceは、データソースを表す文字列を指定します。 szMachineNameは、オブジェクトを列挙するコンピュータ名を指定します。 szObjectNameは、カウンタを列挙したいオブジェクト名を指定します。 mszCounterListは、カウンタのリストを受け取るバッファを指定します。 格納形式は、PdhEnumObjectと同様になります。 pcchCounterListLengthは、カウンタのリストのサイズを指定します。 これも、PdhEnumObjectのときと同じ要領になります。 mszInstanceListは、オブジェクトのインスタンスとなる一連の文字列を 受け取るバッファを指定します。 インスタンスについては、後述します。 pcchInstanceListLengthは、インスタンスのリストを格納するバッファのサイズを指定します。 関数によって初期化された値が0の場合、指定したオブジェクトはインスタンスをサポートせず、 2の場合はインスタンスをサポートするものの、現在存在しないことを意味します。 dwDetailLevelは、列挙するカウンタの詳細レベルを指定します。 PERF_DETAIL_NOVICEが最も理解しやすいカウンタのみを列挙するのに対し、 PERF_DETAIL_WIZARDはオブジェクトが公開する全てのカウンタを列挙します。 dwFlagsは、0を指定するべきとされています。

インスタンスという概念は、オブジェクトによって存在するかどうか別れます。 プロセスというオブジェクトの場合、それは一種の実行中のアプリケーションですから その数は複数存在し、その1つ1つがインスタンスとなります。 プロセスIDを取得するとなったとき、どのプロセスを対象にするかを決定しますが、 この場合のどのプロセスというのがインスタンスになるわけです。 一方、メモリのようなオブジェクトはインスタンスを持ちませんから、 カウンタを決定するだけで済むことになります。

今回のプログラムは、PdhEnumObjectItemsでカウンタを列挙します。 対象とするオブジェクトはプロセスとし、PERF_DETAIL_WIZARDを指定することで、 オブジェクトの全てのカウンタを列挙します。

#include <windows.h>
#include <pdh.h>

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

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndListBox = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		TCHAR  szCounterList[1024];
		DWORD  dwCounterSize;
		TCHAR  szInstanceList[1024];
		DWORD  dwInstanceSize;
		LPTSTR lp;

		hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);	
		
		dwCounterSize  = sizeof(szCounterList);
		dwInstanceSize = sizeof(szInstanceList);
		PdhEnumObjectItems(NULL, NULL, TEXT("Process"), szCounterList, &dwCounterSize, szInstanceList, &dwInstanceSize, PERF_DETAIL_WIZARD, 0);

		lp = szCounterList;
		while (*lp != '\0') {
			SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)lp);
			lp += lstrlen(lp) + 1;
		}
	
		return 0;
	}
	
	case WM_SIZE:
		MoveWindow(hwndListBox, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

プログラムを実行すると、プロセスに指定可能なカウンタがリストボックスに列挙されます。 この数は、プロセスというオブジェクトから得られるデータの種類となりますから、 PDHがいかに豊富で貴重なデータを取得できるかが分かると思います。 たとえば、% Processer TimeというカウンタはCPU使用率を表していますが、 このようなデータはPDHを介して取得する以外に方法はありません。 PdhEnumObjectItemsの呼び出しは、WM_CREATEの次の部分です。

dwCounterSize  = sizeof(szCounterList);
dwInstanceSize = sizeof(szInstanceList);
PdhEnumObjectItems(NULL, NULL, TEXT("Process"), szCounterList, &dwCounterSize, szInstanceList, &dwInstanceSize, PERF_DETAIL_WIZARD, 0);

PdhEnumObjectItemsは、カウンタとインスタンスという2つのリストを取得しますから、 事前に2つのリストを受け取るバッファのサイズを初期化しておくことになります。 第3引数に指定できるオブジェクト名は、前節のPdhEnumObjectsで列挙できる文字列で、 Processという文字列はその1つに入っていたはずです。

関数より取得したリストの走査は、PdhEnumObjectsの際と全く同じ要領になります。 プログラムでは、カウンタを列挙するためにszCounterListをlpに代入していますが、 szInstanceListに代入すれば、インスタンスを列挙することになります。 この点に注目すると、PdhEnumObjectItemsをプロセス列挙のAPIに見立てることができますが、 関数が低速なうえに得られる情報が実行ファイル名だけの点を踏まえると、 PSAPIのEnumProcessesやTool HelpのProcess32Firstの方が優れているといえるでしょう。 やはり、PDHの魅力はオブジェクトから得られるカウンタの豊富性にありますから、 この機会に主要となれるカウンタを探しておくとよいと思われます。

パフォーマンスデータとアクセス権

パフォーマンスDLLの設計者の視点から考えた場合、 一部のオブジェクト、または一部のオブジェクトのカウンタには アクセスできるユーザーを制限したい場合があるかもしれませんが、 実際には種類は問わず、パフォーマンスデータへのアクセスが成功するかどうかは、 次に示すレジストリキーのセキュリティ記述子に基づくことになっています。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib

デフォルトではこのキーのセキュリティ記述子は、 SYSTEM、Administratorsに全てのアクセスが許可され、 NETWORK SERVICE、INTERACTIVEには読み取りアクセスが許可されています。 この読み取りアクセスが、パフォーマンスデータへのアクセスに必要となります。 INTERACTIVEは、システムに対話的にログオンするユーザーは必ず属することになりますから、 Administratorsのような管理者グループに属さないユーザーでも パフォーマンスデータにはアクセスできることになります。



戻る