EternalWindows
ETW / ログファイルモード

アプリケーション内で扱われる情報を外部に出力するような場合、 それはWindowsにおけるイベントという形で出力することが一般的には望まれます。 理由は、そうした既存の仕組みに準拠することで、 外部アプリケーションが既知の方法で情報を参照できるようになるからです。 Windowsにおけるイベントの出力には、 主にイベントログかETW(Event Tracing for Windows)を使用することになります。 イベントログは、あらゆるアプリケーションとシステムにとってのイベントの格納場所であり、 様々な種類のイベントを一元管理する役割を果たします。 基本的に、イベントをイベントログに書き込んだ場合は、 イベントログを通じて誰でもイベントを確認できるようになります。 一方、ETWは特定のプロバイダが書き込むイベントを取得するためのもので、 トレースの準備をしたアプリケーションでしかイベントは取得できません。 ETWの機能を使用してイベントを取得することは、一般にイベントトレースと呼ばれます。

トレースの準備をするというのは、ETWの用語で言えばセッションを開始するということになります。 また、イベントを書き込むアプリケーション(DLLでも可)はイベントトレースプロバイダと呼ばれ、 プロバイダからイベントをトレースしたいアプリケーションをコンシューマと呼ばれます。 セッションを開始したら何が起こるのかを理解するには、 管理ツールから「信頼性とパフォーマンス モニタ」を開くのが一番分かりやすいでしょう。

左側のペインで「イベント トレース セッション」という項目を選択すると、 現在実行中のセッションが右側のペインに列挙されます。 アプリケーションがセッションを作成するStartTraceという関数を呼び出せば、 "My Event Trace Session LogFileMode"というような独自のセッションもこのペインに表示されるようになります。 右クリックメニューからセッションのプロパティを開くと、 次のようなダイアログが表示されます。

右側の「追加」ボタンを押せばプロバイダを選択するためのダイアログが表示され、 そこで選択したプロバイダは、実際にリストボックスに追加されます。 このリストボックスに追加されたプロバイダが、イベントのトレース対象となるプロバイダになります。 上図のプロバイダはUAC関連のイベントを書き込めるプロバイダですから、 このプロバイダがセッションに追加されているということは、 UACに関するイベントをアプリケーションはトレースしたがっていることになります。 「トレース セッション」タブは、次のようになっています。

セッションは1つ以上のバッファを持つことができ、 プロバイダが書き込んだイベントは、まずこのバッファに格納されます。 そして、上記の「ストリームモード」の部分が「ファイル」である場合は、 バッファの内容がログファイルとして書き込まれることになり、 「リアルタイム」の場合は、アプリケーションにイベントが通知されます。 保存するログファイルの拡張子は、トレースファイルを示す.etlが一般的です。

実際に、セッションを開始するアプリケーションを開発して実行したとして、 それから具体的には何を行えばよいのでしょうか。 先の例の場合、プロバイダはUAC関連の処理を対象にしていますから、 UACに関わる何らかの動作を行うと、プロバイダがイベントを書き込みそうに思えます。 たとえば、あるアプリケーションが次のコードを実行したらどうでしょうか。

HANDLE hFile;
hFile = CreateFile(TEXT("C:\\Program Files\\file.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
CloseHandle(hFile);

このコードはProgram Filesにファイルを作成しようとしていますが、 UACが有効な場合は希望通りにはいきません。 その代わり、仮想化という仕組みによって、ファイルが作成可能な場所へリダイレクトされるわけですが、 このときにプロバイダはイベントを書き込むようになっています。 つまり、先に示した「ストリームモード」の部分が「ファイル」であった場合は、 イベントが書き込まれたログファイルが作成されます。 ちなみに、一度仮想化されたファイルの場合は、イベントは書き込まれない模様です。

書き込まれたイベントの内容を理解しなければ、イベントをトレースした意味がありませんから、 イベントを解析する作業というのが別途必要になります。 アプリケーションはTDH APIを呼び出すことでこれを行うことができますが、 用意されている既存のツールでも十分な情報を得ることができます。 たとえば、コマンドプロント上で次のように入力すれば、 ログファイルの中身を解析した結果が出力されます。

tracerpt C:\sample.etl

tracerptの後に、ログファイルのパスを指定するようにします。 そうすれば、tracerpt.exeがファイルの中身を解析し、 dumpfile.xmlとsummary.txtを出力します。 tracerptはsystem32フォルダに存在するため出力先もこのフォルダとなり、 コマンドプロントを管理者として実行しなければ作成に失敗することになります。 dumpfile.xmlの中身を見れば、ファイルに書き込まれたイベントを確認できます。

<System>
	<Provider Name="Microsoft-Windows-UAC-FileVirtualization" Guid="{c02afc2b-e24e-4449-ad76-bcc2c2575ead}" />
	<EventID>4000</EventID>
	<Version>0</Version>
	<Level>4</Level>
	<Task>0</Task>
	<Opcode>0</Opcode>
	<Keywords>0x8000000000000000</Keywords>
	<TimeCreated SystemTime="2011-06-29T17:32:46.318014900Z" />
	<Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
	<Execution ProcessID="5504" ThreadID="5496" ProcessorID="1" KernelTime="90" UserTime="45" />
	<Channel>Microsoft-Windows-UAC-FileVirtualization/Operational</Channel>
	<Computer />
</System>
<EventData>
	<Data Name="Flags">0x8</Data>
	<Data Name="SidLength">28</Data>
	<Data Name="Sid">S-1-5-21-...</Data>
	<Data Name="FileNameLength">49</Data>
	<Data Name="FileNameBuffer">\Device\HarddiskVolume3\Program Files\file.txt</Data>
	<Data Name="ProcessImageNameLength">34</Data>
	<Data Name="ProcessImageNameBuffer">\Device\HarddiskVolume3\sample.exe</Data>
	<Data Name="CreateOptions">83886176</Data>
	<Data Name="DesiredAccess">0x120196</Data>
	<Data Name="IrpMajorFunction">0</Data>
</EventData>

Systemというセクション内に書き込まれている情報は、どのイベントにおいても存在することになります。 一方、EventDataというセクション内に書き込まれている情報は、イベントによって変化するようになっており、 今回のプロバイダであればUAC関連の情報が書き込まれています。 たとえば、Sidの箇所にはユーザーのSIDが格納され、FileNameBufferには仮想化の対象となったファイルパス、 ProcessImageNameBufferには仮想化が発生したプロセスのファイルパスが格納されます。

これまでの話から分かるように、イベントをトレースしたいアプリケーションはまずセッションを開始する必要があります。 セッションを開始するには、StartTraceを呼び出します。 この関数を成功させるには、アプリケーションが管理者として実行している必要があります。

ULONG StartTrace(
  PTRACEHANDLE SessionHandle,
  LPCTSTR SessionName,
  PEVENT_TRACE_PROPERTIES Properties
);

SessionHandleは、セッションハンドルを受け取る変数のアドレスを指定します。 ハンドルの型がTRACEHANDLEになっていますが、返されるハンドルはあくまでセッションハンドルです。 このハンドルは、StartTraceで開始されたセッションを識別します。 SessionNameは、開始するセッションの名前を指定します。 Propertiesは、セッションの情報を格納したバッファを指定します。

EVENT_TRACE_PROPERTIES構造体は、次のように定義されています。

typedef struct _EVENT_TRACE_PROPERTIES {
  WNODE_HEADER Wnode;
  ULONG        BufferSize;
  ULONG        MinimumBuffers;
  ULONG        MaximumBuffers;
  ULONG        MaximumFileSize;
  ULONG        LogFileMode;
  ULONG        FlushTimer;
  ULONG        EnableFlags;
  LONG         AgeLimit;
  ULONG        NumberOfBuffers;
  ULONG        FreeBuffers;
  ULONG        EventsLost;
  ULONG        BuffersWritten;
  ULONG        LogBuffersLost;
  ULONG        RealTimeBuffersLost;
  HANDLE       LoggerThreadId;
  ULONG        LogFileNameOffset;
  ULONG        LoggerNameOffset;
} EVENT_TRACE_PROPERTIES, *PEVENT_TRACE_PROPERTIES;

BufferSizeは、バッファ(セッションのバッファではなく、PEVENT_TRACE_PROPERTIESで識別できるバッファ)のサイズを指定します。 バッファには文字列などの可変長データを含めなければならないため、 sizeof(EVENT_TRACE_PROPERTIES)にならないことに注意してください。 MinimumBuffersは、セッションのバッファプールに確保するバッファの最小値を指定します。 既定の値(4)でよい場合は0を指定します。 MaximumBuffersは、セッションのバッファプールに確保するバッファの最大値を指定します。 既定の値(25)でよい場合は0を指定します。 MaximumFileSizeは、作成されるログファイルの最大サイズをMB単位で指定します。 LogFileModeは、モード値を指定します。 この値によってイベントをファイルに保存するログファイルモードか、 通知によって受け取るリアルタイムモードかが決定します。 FlushTimerは、よく分かりませんが0で問題ないと思われます。 EnableFlagsは、NT Kernel Loggerセッションのイベントをトレースする際に定数を指定します。 AgeLimitは、使用されません。 NumberOfBuffersからLoggerThreadIdは、StartTraceが内部で初期化します。 LogFileNameOffsetは、ログファイルの名前を格納したアドレスへのオフセットを指定します。 このオフセットには、アプリケーションがログファイルの名前を格納する必要があります。 LoggerNameOffsetは、セッション名を格納するアドレスへのオフセットを指定します。 このオフセットには、アプリケーションがセッションの名前を格納する必要はありません。 それぞれのオフセットは、バッファの先頭をベースにしている必要があります。

セッションが開始されると、「信頼性とパフォーマンス モニタ」からプロバイダを追加できます。 アプリケーション内からプロバイダを追加(有効)を行いたい場合は、EnableTraceExを呼び出します。 この関数はWindows Vistaから追加されたため、それ以前の環境ではEnableTraceを呼び出してください。

ULONG EnableTraceEx(
  LPCGUID ProviderId,
  LPCGUID SourceId,
  TRACEHANDLE TraceHandle,
  ULONG IsEnabled,
  UCHAR Level,
  ULONGLONG MatchAnyKeyword,
  ULONGLONG MatchAllKeyword,
  ULONG EnableProperty,
  PEVENT_FILTER_DESCRIPTOR EnableFilterDesc
);

ProviderIdは、有効にするプロバイダのGUIDを指定します。 SourceIdは、NULLで問題ありません。 TraceHandleは、StartTraceで取得したハンドルを指定します。 IsEnabledは、プロバイダを有効にしたい場合に1を指定します。 Levelは、イベントをどれくらい詳細に取得するかを示すレベルを指定します。 たとえば、致命的なエラーに関するイベントを取得したい場合はTRACE_LEVEL_CRITICAL(1)を指定しますが、 通常のエラーに関するイベントを取得する場合はTRACE_LEVEL_ERROR(2)を指定します。 上位のレベルは下位のレベルを含むため、RACE_LEVEL_ERRORを指定した場合は、 TRACE_LEVEL_CRITICALのイベントも取得することに注意してください。 MatchAnyKeywordとMatchAllKeywordは、有効にしたいイベントのキーワードを指定します。 MatchAnyKeywordのいずれかを含むイベントが有効にされ、 MatchAllKeywordのすべてを含むイベントが有効にされます。 MatchAnyKeywordが0の場合は全てのイベントが有効になるため、MatchAllKeywordも0を指定します。 EnablePropertyは、イベントが書き込まれる際に追加されてほしいデータを識別する定数を指定します。 不要な場合は0で問題ありません。 EnableFilterDescは、よく分かりませんがNULLで問題ないようです。

プロバイダを追加すれば、そのプロバイダがイベントを書き込んだ際にセッションのバッファにデータが格納され、 ログファイルモードの場合はログファイルに書き込みが行われます。 一方、リアルタイムモードの場合はアプリケーションに通知が送られます。 書き込みや通知が不要になった場合は、ControlTraceでセッションを停止します。

ULONG ControlTrace(
  TRACEHANDLE SessionHandle,
  LPCTSTR SessionName,
  PEVENT_TRACE_PROPERTIES Properties,
  ULONG ControlCode
);

SessionHandleは、セッションを識別するハンドルを指定します。 SessionNameは、セッションの名前を指定します。 Propertiesは、セッションの情報を格納したバッファを指定します。 ControlCodeは、セッションをどのように制御するかを示す定数を指定します。 セッションを停止する場合はEVENT_TRACE_CONTROL_STOPを指定します。

今回のプログラムは、ログファイルモードとしてセッションを開始します。 プロバイダがイベントの書き込みを行った場合は、ログファイルにデータが保存されることになります。

#include <windows.h>
#include <evntcons.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR                   szSessionName[] = TEXT("My Event Trace Session LogFileMode");
	TCHAR                   szLogFilePath[] = TEXT("sample.etl");
	TRACEHANDLE             hSession;
	PEVENT_TRACE_PROPERTIES pProperties;
	DWORD                   dwBufferSize;
	ULONG                   uResult;
	const GUID              guidSession = {0xefa6b42b, 0xe1f0, 0x410d, {0x8a, 0xe9, 0xb5, 0x43, 0x29, 0xaa, 0x33, 0x1c}};
	GUID                    guidProvider;

	dwBufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(szSessionName) + sizeof(szLogFilePath);
	pProperties = (PEVENT_TRACE_PROPERTIES)HeapAlloc(GetProcessHeap(), 0, dwBufferSize);

	ZeroMemory(pProperties, dwBufferSize);
	pProperties->Wnode.BufferSize    = dwBufferSize;
	pProperties->Wnode.Guid          = guidSession;
	pProperties->Wnode.ClientContext = 1;
	pProperties->Wnode.Flags         = WNODE_FLAG_TRACED_GUID;
	pProperties->MaximumFileSize     = 1;
	pProperties->LogFileMode         = EVENT_TRACE_FILE_MODE_SEQUENTIAL;
	pProperties->LoggerNameOffset    = sizeof(EVENT_TRACE_PROPERTIES);
	pProperties->LogFileNameOffset   = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(szSessionName);
	lstrcpy((LPTSTR)((LPBYTE)pProperties + pProperties->LogFileNameOffset), szLogFilePath);

	uResult = StartTrace(&hSession, szSessionName, pProperties);
	if (uResult != ERROR_SUCCESS) {
		if (uResult == ERROR_ACCESS_DENIED)
			MessageBox(NULL, TEXT("アクセスが拒否されました。"), NULL, MB_ICONWARNING);
		else if (uResult == ERROR_ALREADY_EXISTS)
			MessageBox(NULL, TEXT("セッションは既に存在します。"), NULL, MB_ICONWARNING);
		else
			MessageBox(NULL, TEXT("StartTraceの呼び出しに失敗しました。"), NULL, MB_ICONWARNING);
		HeapFree(GetProcessHeap(), 0, pProperties);
		return 0;
	}

	CLSIDFromString(L"{c02afc2b-e24e-4449-ad76-bcc2c2575ead}", &guidProvider);
	EnableTraceEx((LPCGUID)&guidProvider, NULL, hSession, 1, TRACE_LEVEL_INFORMATION, 0, 0, 0, NULL);
	
	MessageBox(NULL, TEXT("ボタンを押すとセッションを終了します。"), TEXT("OK"), MB_OK);

	ControlTrace(hSession, szSessionName, pProperties, EVENT_TRACE_CONTROL_STOP);
	HeapFree(GetProcessHeap(), 0, pProperties);
	
	return 0;
}

StartTraceを呼び出すためには、PEVENT_TRACE_PROPERTIESで識別できるバッファを確保しなければなりません。 このバッファにはセッション名とログファイルの名前を含める必要があるため、 それらのサイズを加算してdwBufferSizeに格納し、HeapAllocでバッファを確保しています。 Wnode.BufferSizeはバッファサイズを指定し、Wnode.Guidは開始するセッションを識別するGUIDを指定します。 Wnode.ClientContextはセッションのクロック タイプを指定でき、 1を指定した場合はQPC(Query performance counter)になります。 Wnode.Flagsは、WNODE_FLAG_TRACED_GUIDを必ず含めるようにします。 MaximumFileSizeが1であることから、ログファイルの最大サイズは1MBになります。 LogFileModeにEVENT_TRACE_FILE_MODE_SEQUENTIALを指定した場合は、 トレースがログファイルモードになります。 LoggerNameOffsetはセッション名を格納するアドレスへのオフセットであり、 sizeof(EVENT_TRACE_PROPERTIES)を指定した場合は、 EVENT_TRACE_PROPERTIES構造体の直後にセッション名が存在することになります。 LogFileNameOffsetはログファイルのパスを格納したアドレスへのオフセットであり、 sizeof(EVENT_TRACE_PROPERTIES) + sizeof(szSessionName)を指定した場合は、 セッション名の直後にログファイルのパスが存在することになります。

StartTraceが失敗する理由は主に2通り存在します。 1つ目はアクセスが拒否される場合で、この場合は管理者としてアプリケーションを実行することになります。 2つ目は既にセッションが存在する場合であり、これはアプリケーションがセッションを停止せずに終了した場合に発生します。 StartTraceで開始したセッションは、アプリケーションの 終了と共に停止するようなことはないため、 アプリケーションが終了する場合は必ずControlTraceでセッションを停止しなければなりません。 もし、セッションが残ってしまった場合は、セッション名とセッションGUIDを変更してアプリケーションを実行してください。

StartTraceが成功したら、EnableTraceExでプロバイダをセッションに追加します。 第1引数はGUID型の変数のアドレスであるため、事前にGUID文字列をCLSIDFromStringで変換します。 何故、「Microsoft-Windows-UAC-FileVirtualization」のGUID文字列が分かったのかについては、 下記のレジストキーを調べたからです。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Publishers

EnableTraceExは、存在しないプロバイダのGUIDを指定しても成功する点に注意してください。 存在するプロバイダのGUIDを指定すれば、 「信頼性とパフォーマンス モニタ」からプロバイダの名前を確認できるはずです。 メッセージボックスが表示されたら、既に述べたように、UACの仮想化が発生するような動作を行えばよいでしょう。 その後アプリケーションを終了し、tracerptを使用してファイルを解析するようにします。

今回の場合は、プロバイダがイベントを書き込むタイミングというものを上手く把握できましたが、 ほとんどのプロバイダにおいて、このタイミングというのは不明確です。 また、書き込むタイミング以外に、そもそもどのようなイベントを書き込むプロバイダなのかが 分からないものも多数存在します。 こうしたことから、ETWは自作のアプリケーション間での情報交換を目的に使用することもよくあります。 たとえば、デバイスドライバがイベントを書き込み、 それをサービスアプリケーションがトレースするなどの仕組みなどが考えられます。 サービスアプリケーションならば、管理者として動作を要求するStartTraceを問題なく呼び出せます。

登録されたプロバイダの列挙

StartTraceで開始したセッションにはプロバイダを追加することになりますが、 セッションに追加できるプロバイダというのは、 ETWに対してGUIDを登録したプロバイダに限られます。 このような登録されたプロバイダを列挙する例を示します。

#include <windows.h>
#include <evntcons.h>

LPGUID g_lpGuids;

void ShowInfo(HWND hwndListBox, LPGUID lpGuid);
void EnumGuids(HWND hwndListBox);
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 hwndListBoxLeft = NULL;
	static HWND hwndListBoxRight = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		hwndListBoxLeft = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		hwndListBoxRight = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 0, 0, 0, 0, hwnd, (HMENU)2, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
	
		EnumGuids(hwndListBoxLeft);
	
		return 0;
	}

	case WM_COMMAND: {
		int nIndex;

		if ((HWND)lParam == hwndListBoxRight || HIWORD(wParam) != LBN_SELCHANGE)
			return 0;

		SendMessage(hwndListBoxRight, LB_RESETCONTENT, 0, 0);

		nIndex = (int)SendMessage(hwndListBoxLeft, LB_GETCURSEL, 0, 0);
		
		ShowInfo(hwndListBoxRight, &g_lpGuids[nIndex]);

		return 0;
	}
	
	case WM_SIZE:
		MoveWindow(hwndListBoxLeft, 0, 0, LOWORD(lParam) / 2, HIWORD(lParam), TRUE);
		MoveWindow(hwndListBoxRight, LOWORD(lParam) / 2, 0, LOWORD(lParam) - LOWORD(lParam) / 2, HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		if (g_lpGuids != NULL)
			HeapFree(GetProcessHeap(), 0, g_lpGuids);
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

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

void EnumGuids(HWND hwndListBox)
{
	ULONG  i, uCount;
	ULONG  uBufferSize, uReturnLength;
	LPGUID lpGuids;

	EnumerateTraceGuidsEx(TraceGuidQueryList, NULL, 0, NULL, 0, &uReturnLength);
	lpGuids = (LPGUID)HeapAlloc(GetProcessHeap(), 0, uReturnLength);
	uBufferSize = uReturnLength;
	EnumerateTraceGuidsEx(TraceGuidQueryList, NULL, 0, lpGuids, uBufferSize, &uReturnLength);

	uCount = uBufferSize / sizeof(GUID);WCHAR szGuid[256];

	for (i = 0; i < uCount; i++) {
		StringFromGUID2(lpGuids[i], szGuid, 256);
		SendMessageW(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szGuid);
	}

	g_lpGuids = lpGuids;
}

void ShowInfo(HWND hwndListBox, LPGUID lpGuid)
{
	ULONG                         uBufferSize, uReturnLength;
	PTRACE_GUID_INFO              pInfo;
	PTRACE_PROVIDER_INSTANCE_INFO pInstance;
	TCHAR                         szBuf[256];
	TCHAR                         szImageName[256] = {0};
	DWORD                         dwSize;
	HANDLE                        hProcess;
	
	EnumerateTraceGuidsEx(TraceGuidQueryInfo, lpGuid, sizeof(GUID), NULL, 0, &uReturnLength);
	pInfo = (PTRACE_GUID_INFO)HeapAlloc(GetProcessHeap(), 0, uReturnLength);
	uBufferSize = uReturnLength;
	EnumerateTraceGuidsEx(TraceGuidQueryInfo, lpGuid, sizeof(GUID), pInfo, uBufferSize, &uReturnLength);

	pInstance = (PTRACE_PROVIDER_INSTANCE_INFO)((PBYTE)pInfo + sizeof(TRACE_GUID_INFO));

	wsprintf(szBuf, TEXT("Enable : %d"), (pInstance->Flags & TRACE_PROVIDER_FLAG_PRE_ENABLE) == TRACE_PROVIDER_FLAG_PRE_ENABLE);
	SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);

	wsprintf(szBuf, TEXT("Legacy : %d"), (pInstance->Flags & TRACE_PROVIDER_FLAG_LEGACY) == TRACE_PROVIDER_FLAG_LEGACY);
	SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);

	hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pInstance->Pid);
	dwSize = sizeof(szImageName) / sizeof(TCHAR);
	QueryFullProcessImageName(hProcess, 0, szImageName, &dwSize);
	wsprintf(szBuf, TEXT("Process : %s"), szImageName);
	SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);
	
	HeapFree(GetProcessHeap(), 0, pInfo);
}

EnumGuidsという自作関数では、EnumerateTraceGuidsExを呼び出して登録されたプロバイダのGUIDを列挙しています。 このようなGUIDの列挙の際には、第1引数にTraceGuidQueryListを指定します。 ShowInfoという自作関数は、指定されたGUIDで識別されるプロバイダの情報を取得します。 この際には、EnumerateTraceGuidsExの第1引数にTraceGuidQueryInfoを指定します。 プロバイダの情報はTRACE_PROVIDER_INSTANCE_INFO構造体に格納されており、 FlagsにTRACE_PROVIDER_FLAG_PRE_ENABLEが含まれていれば、 プロバイダが有効であることを意味します。 また、TRACE_PROVIDER_FLAG_LEGACYが含まれていれば、 プロバイダが従来のRegisterTraceGuidsで登録されたことを意味します(含まれていない場合はEventRegister)。 PidにはプロセスIDが格納されているため、これを基にプロセスのハンドルを取得し、 QueryFullProcessImageNameでフルパスを取得しています。



戻る