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

[ ホーム ] [ 上へ ] [ パフォーマンスモニタ returns 〜 カスタムパフォーマンスモニタの作成 その5 ]

<パフォーマンスDLLの作成>

ここまでの道のりが結構長かったのでうんざりしているかもしれませんが、 いよいよ本体のパフォーマンスDLLの作成です。モニタ対象アプリはMFCで作成しましたが、 このDLLは呼び出しにコストがかかるので、できるだけ余分なライブラリを使わないで 軽くしたいということと、このDLLにMFCを使う理由がないのでWin32で実装します。 前頁と勘違いしないように注意してください。

(PerfDnpプロジェクトの作成)

現在、PerfCustomソリューションにDnpプロジェクトが 1つある状態だと思いますが、このソリューションにPerfDnpというパフォーマンスDLLの プロジェクトを作成します。ツールバーのファイル(F)→新規作成(N)→プロジェクト(P)で「 新しいプロジェクト」ダイアログを表示させて、以下のように設定してください。

プロジェクトの種類(P) :Win32
テンプレート(T)        :Win32プロジェクト
プロジェクト名(N)      :PerfDnp
ソリューション(S)      :ソリューションに追加
→OK

アプリケーションウィザードが起動するので次のように設定してください。

→次へ
アプリケーションの設定
    アプリケーションの種類         :DLL(D)
    追加のオプション               :なし
    共通ヘッダーファイルを追加     :なし
→完了

MFCの場合、文字列処理はTCHAR型でマルチバイトと ユニコードとを切り替えていましたが、今回のパフォーマンスDLLではパフォーマンスインスタンス名 などすべてユニコードで、あとでユニコードの文字列関数を使用したいので、 PerfDnpプロジェクトのstdafx.hに次のプリプロセッサを記述してください。

// PerfDnpプロジェクト - stdafx.h
#pragma once

// ・
// ・

// TODO: プログラムに必要な追加ヘッダーをここで参照してください。
#include <wchar.h>
#define     _L(x)          __L(x)            //ユニコード用マクロ
#define     __L(x)         L ## x

また、PerfDnp.dllの実装ファイルPerfDnp.cppができていると思いますが、 このままにしておいて先にイベントログの実装を行います。

(イベントログの実装)

ここではイベントログの内容等詳しいことはまた別の機会があれば書きたいと思います。 ここでは、今回のDLL作成に必要な手順だけを記述します。

・メッセージファイルの作成

[A]

いったんVisual Studioでの作業を中断して、メモ帳などのテキストエディタを 起動してください。
エクスプローラなどのファイラーで確認して、 PerfDnp.cppなどのファイルがあるPerfDnpのフォルダ(デフォルトでは xxxxxxx\Projects\PerfCustom\PerfDnp)にDnpEventLog.MCという名前で テキストファイルを作成してください。このテキストファイルの内容は次のとおりです。

DnpEventLog.MCの内容

;/*PerfDnp.dll内で使用されるメッセージテキストファイル(イベントメッセージ定義ファイル)
;
; 以下のコマンドラインでこのファイル(DnpEventlog.h)を含めて3つのファイルが生成される
; mc.exe DnpEventLog.MC
;
; 生成されるファイルDnpEventLog.h (このファイル- シンボル名)
; DnpEventLog.rc (リソースファイル)
; MSG00411.bin (バイナリリソースファイル)
;*/
;//
;#ifndef _DNPEVENTLOG_H_
;#define _DNPEVENTLOG_H_
;//
;//
MessageIdTypedef=DWORD
SeverityNames=(
    Success=0x0
    Informational=0x1
    Warning=0x2
    Error=0x3
 )
FacilityNames=(
    Application=0x0
    System=0x1
    Security=0x2
 )
LanguageNames=(Japanese=0x411:MSG00411)
;//------------メッセージ定義-----------------------------------
;//
MessageId=0x1
Severity=Error
Facility=Application
SymbolicName=PERFDNP_CANT_OPEN_PERF_KEY
Language=Japanese
PerfDnpのパフォーマンスキーを開くことができません.
.
;//
MessageId=0x2
Severity=Error
Facility=Application
SymbolicName=PERFDNP_CANT_READ_FIRST_COUNTER
Language=Japanese
PerfDnpの最初のカウンタインデックスを取得できません.
.
;//
MessageId=0x3
Severity=Error
Facility=Application
SymbolicName=PERFDNP_CANT_READ_FIRST_HELP
Language=Japanese
PerfDnpの最初の説明インデックスを取得できません
.
;//
MessageId=0x4
Severity=Error
Facility=Application
SymbolicName=PERFDNP_CANT_INIT_DNPAPPDATA
Language=Japanese
DnpAppウィンドウデータ取得のための初期化ができません。
.
;//
;#endif _DNPEVENTLOG_H_

[B]

DnpEventLog.MCファイルを保存できたら、 次にmc.exeという実行ファイルがデフォルトで
C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\Bin
にあるのでパスを通しておいてください。

スタート→ファイル名を指定して実行(R)で
cmd  [リターン」
でコマンドプロンプトを起動します。カレントパスをDnpEventLog.MCのあるフォルダ (xxxxx\Projects\PerfCustom\PerfDnp)に移り、
mc.exe DnpEventLog.MC
を実行してください。
「MC: Compiling DnpEventLog.MC」と表示されればOKです。このとき、DnpEventLog.MCのある フォルダに次の3つのファイルが新しく作られたことをdirなどで確認してください。

  1. DnpEventLog.h
  2. DnpEventLog.rc
  3. MSG0411.bin

[C]

次に同じくテキストエディタでDnpEventLog.reg と名前のテキストファイルを 先ほどと同じPerfDnpのフォルダに作成して次の内容の テキストファイルを作成してください。

DnpEventLog.regの内容

REGEDIT4
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\PerfDnp]
"EventMessageFile"=hex(2):25,53,79,73,74,65,6d,52,6f,\
6f,74,25,5c,53,79,73,74,65,6d,33,32,\
5c,50,65,72,66,44,6e,70,2e,64,6c,6c,\
00
"TypesSupported"=dword:00000007

[D]

DnpEventLog.regを保存できたら、再びコマンドプロンプトで 先ほどと同じPerfDnpフォルダに移して以下のコマンドを実行してください。
regedit DnpEventLog.reg

「DnpEventLog.reg内の情報をレジストリに追加しますか?」 と聞いてくるので、「はい」をクリックしてください。「DnpEventLog.regの情報が、 正しくレジストリに入力されました」と返ってくるのでレジストリエディタで次のような キーとエントリーが作成されているか確認してください。

レジストリのパスは
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\PerfDnp
です。

EventMessageFileエントリの値は作成した.regファイルではバイトシーケンスで 保存されていて直感的にわからないので
%SystemRoot%\System32\PerfDnp.dll
と、きちんと登録されているか確認してください。

[E]

以上でVisual Studio外のメッセージファイルに関する 作業が終了したので、再びVisual Studioでイベントログの実装の続きを行いますが、 IDEの管理下に置くためにVisual Studio外で作成したファイルをソリューションに加えておきます。

Visual Studioに戻り、ソリューションエクスプローラの PerfDnpプロジェクトフォルダを右クリックして追加(D)→既存の項目(W)で、 追加できるファイルの一覧が表示されるので
DnpEventLog.MC
DnpEventLog.reg
の2つのファイルを加えます。その際、「対応するカスタムビルド規則が見つかりません」 と表示されますが、「いいえ」をクリックしてください。

今度はソリューションエクスプローラのPerfDnpフォルダ配下の 「リソースファイル」を右クリックして追加(D)→既存の項目(W)で、 追加できるファイルの一覧が表示されるので
DnpEventLog.rc
MSG0411.bin
の2つのファイルを加えます。同様にカスタムビルド規則のダイアログが表示されますが、 いいえを選択してください。

最後にPerfDnpフォルダ配下の「ヘッダーファイル」を右クリックして 追加(D)→既存の項目(W)で、追加できるファイルの一覧が表示されるので
DnpEventLog.h
を加えます。それではVisual Studioでコーディングの続きを行います。

・イベントログ管理クラスの作成

エラー時等に先ほど作成したイベントログの機能を作動させるクラスを作成します。 ソリューションエクスプローラのPerfDnpプロジェクトフォルダを右クリックして 追加(D)→クラス(C)でクラスの追加ダイアログが起動しますので以下の設定をしてください。

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

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

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

EventLog.hとEventLog.cppのヘッダ、 実装ファイルができているので以下の実装をしてください。

//イベントログ管理クラスヘッダファイル - EventLog.h
#pragma once

class CEventLog
{
    //メンバ変数//////////////////////////////////////////////////
protected:
    HANDLE              m_hEventLog;        //登録済みイベントソースハンドル
    static DWORD        m_dwLogUser;        //イベントログ利用者数
    BOOL                m_bFlgRegister;     //イベントソース登録フラグ

    //メンバ関数//////////////////////////////////////////////////
private:
    CEventLog(const CEventLog &obj);        //コピーコンストラクタ

public:
    CEventLog();                            //コンストラクタ
    ~CEventLog();                           //デストラクタ

    BOOL Register();                        //イベントソースを登録
    BOOL Report(WORD  wType,                //イベントログへの書込み
                DWORD dwEventID,
                void  *lpData,
                DWORD dwSize);
    VOID Close();                           //イベントログハンドルを閉じる
};
//イベントログ管理クラス実装ファイル - EventLog.cpp
#include "StdAfx.h"
#include "EventLog.h"

#define EVENT_SOURCE "PerfDnp"       //イベントソース名

DWORD CEventLog::m_dwLogUser = 0;    //イベントログの利用者数

//コンストラクタ
CEventLog::CEventLog() : m_hEventLog(NULL)
                       , m_bFlgRegister(FALSE)
{
}

//コピーコンストラクタ(private)
CEventLog::CEventLog(const CEventLog &obj) : m_hEventLog(NULL)
                                           , m_bFlgRegister(FALSE)
{
}

//デストラクタ
CEventLog::~CEventLog()
{
    if (m_bFlgRegister)        Close();
}

//イベントソース登録
//引数 登録するイベントソース名
//     HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\PerfDnp
//                                                                               ^^^^^^^
//                                                                              ここの名前
//戻値 登録成功TRUE
//仕様 レジストリにイベントソース名が見つからなくてもログの記録は
//     アプリケーションログファイルが使われる
BOOL CEventLog::Register()
{
    if (! m_hEventLog)
        m_hEventLog = ::RegisterEventSource(NULL, _L(EVENT_SOURCE));

    if (m_hEventLog)
    {
        CEventLog::m_dwLogUser++;
        m_bFlgRegister = TRUE;
    }

    return m_hEventLog ? TRUE : FALSE;
}

//イベントログへの書込み
//引数 wType     書込むイベントの種類(winnt.h内)
//     dwEventID mc.exeでDnpEventlog.MCより生成されたDnpEventlog.hのイベントID
//     lpData    イベントログに書き込むデータ(エラーコードなど)
//     dwSize    lpDataのサイズ
//戻値 書き込み成功 TRUE
BOOL CEventLog::Report(WORD wType, DWORD dwEventID, void *lpData, DWORD dwSize)
{
    if (! m_hEventLog)
        return FALSE;

    return ::ReportEvent(m_hEventLog, wType, 0, dwEventID, NULL, 0, dwSize, NULL, lpData);
}

//イベントログハンドルを閉じる
VOID CEventLog::Close()
{
    if (m_hEventLog)
    {
        CEventLog::m_dwLogUser--;
        m_bFlgRegister = FALSE;

        if (CEventLog::m_dwLogUser <= 0)
        {
            ::DeregisterEventSource(m_hEventLog);         m_hEventLog = NULL;
        }
    }
}

続いて、前頁で作成した Dnpからパフォーマンスデータを収集するクラスを作成します。