◆イベントログ − VC++ 2005 その3 〜 イベントログへの書込み

[ ホーム ] [ 上へ ] [ イベントログ - VC++ 2005 その4 〜 イベントログの読込み ]

・イベントログを使っての作業

<イベントログ書込み対象アプリケーション作成>

早速イベントログの書込みを行いたいところですが、その前にイベントログの書込み対象の 簡単なアプリケーションを作成したいと思います。アプリケーションの内容はダイアログ内の テキストボックスにファイル名を入力して、そのファイルが存在するかどうかというごく単純なアプリです。 このアプリの起動・終了と、入力したファイル名が存在するか・しないかをイベントログに 書き込もうと思います。

前頁のTestEventLogのソリューションにFileExistプロジェクトとして加えるので、 TestEventLogプロジェクトを開いた後、以下のように行ってください。
ツールバーのファイル(F)→新規作成(N)→プロジェクト(P)で「新しいプロジェクト」ダイアログボックスを 表示させて次のように設定してください。

プロジェクトの種類(P)   :Visual C++
テンプレート(T)     :MFCアプリケーション
プロジェクト名(N)    :FileExist
ソリューション(S)    :ソリューションに追加
→OK

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

→次へ
アプリケーションの種類
    アプリケーションの種類:ダイアログベース(D)
    (他はそのまま)         
→次へ

ユーザーインターフェイス機能
    メインフレームのスタイル:システムメニュー(S)のみチェック
→次へ

高度な機能
    (そのまま)         
→次へ
 
生成されたクラス
    (そのまま)         
→完了

以上でスケルトンが出来上がっているので、CFileExistDlgクラスとリソースを 加筆・修正していきます。

まずはダイアログリソースの作成から。 ツールバーからリソースビューを表示させ、IDD_FILEEXIST_DIALOGのダイアログリソースを 表示して以下のようにエディットコントロール1つ、コマンドボタン1つ、 スタティックテキスト2つを作成してください。

  プロパティ 設定値
ID IDC_ED_FILEPATH
ID IDC_BTN_EXEC
ID IDC_STC_RESULT
Client Edge True
ID IDC_STC_NOTIFYEVT
Client Edge True

次のメインダイアログのCFileExistDlgクラスの ヘッダ・実装ファイルに以下のように記述してください。

// FileExistDlg.h : ヘッダーファイル  
//
#pragma once

// CFileExistDlg ダイアログ
class CFileExistDlg : public CDialog  
{
// ・
// ・
// ・
protected:
    virtual void OnOK();  
    virtual void OnCancel();  

    void PreEndDialog();                //ダイアログ終了前処理
    BOOL GetSID(PSID *ppSid);           //現在のユーザのセキュリティ識別子取得
    BOOL WriteEvent(WORD wType,         //イベントログへの書込み
                    WORD wCategory,  
                    DWORD dwEventID,  
                    WORD wNumStrings,  
                    DWORD dwDataSize,  
                    LPCTSTR *lpStrings,  
                    LPVOID lpRawData);  

public:
    afx_msg void OnBnClickedBtnExec();  //検索ボタンクリック時

protected:
    DECLARE_MESSAGE_MAP()
};
// FileExistDlg.cpp : 実装ファイル  
//
#include "stdafx.h"
#include "FileExist.h"
#include "FileExistDlg.h"
#include "..\\MyApp\\MyAppMsg.h"

#define     EVENT_SRC_NAME     "MyApp" //イベントソース名


// CFileExistDlg ダイアログ  
// ・
// ・
// ・

BEGIN_MESSAGE_MAP(CFileExistDlg, CDialog)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_BN_CLICKED(IDC_BTN_EXEC, &CFileExistDlg::OnBnClickedBtnExec)  
END_MESSAGE_MAP()

// CFileExistDlg 初期化
BOOL CFileExistDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
// ・
// ・

    if (! WriteEvent(EVENTLOG_INFORMATION_TYPE,  
                     APP_ACTION,
                     APP_OPEN,
                     0,
                     0,
                     NULL,
                     NULL)
    )
        TRACE(_T("Can't Write it in the Eventlog\n"));

    return TRUE;  
}

// ・
// ・
// ・

//検索ボタンクリック
void CFileExistDlg::OnBnClickedBtnExec()
{
    CString     strPath;  
    CString     strResult;  
    DWORD       dwAttri   = 0;  
    int         nExistFlg = 0; //1:あり   0:エラー    -1:なし

    //パス取得
    GetDlgItem(IDC_ED_FILEPATH)->GetWindowTextW(strPath);

    if (strPath.IsEmpty())  
        return;

    //ファイル検索
    dwAttri = ::GetFileAttributes(strPath);  
    if (dwAttri == (DWORD)-1)  
    {
        nExistFlg = -1;  
    } else  
    {
        dwAttri & FILE_ATTRIBUTE_DIRECTORY ? nExistFlg = -1 : nExistFlg = 1;  
    }

    //結果表示
    strResult = strPath;  
    nExistFlg == 1 ? strResult += _T("が見つかりました")  
                   : strResult += _T("がみつかりません");
    GetDlgItem(IDC_STC_RESULT)->SetWindowTextW(strResult);

    //イベントログへの書込み
    LPCTSTR     pInstStr = (PCWSTR)strPath;  

    if (! WriteEvent(nExistFlg == 1 ? EVENTLOG_AUDIT_SUCCESS : EVENTLOG_AUDIT_FAILURE,
                     USR_ACTION,
                     nExistFlg == 1 ? FILE_FIND : FILE_NOT_FOUND,  
                     1,
                     sizeof(DWORD),
                     &pInstStr,
                     &dwAttri)
    )
        TRACE(_T("Can't Write Eventlog\n"));
}

//OnOKオーバーライド
void CFileExistDlg::OnOK()
{
    PreEndDialog();
    CDialog::OnOK();
}

//OnCancelオーバーライド
void CFileExistDlg::OnCancel()
{
    PreEndDialog();
    CDialog::OnCancel();
}

//ダイアログ終了前処理 (ダイアログ終了をイベントログへ書込む)
void CFileExistDlg::PreEndDialog()
{
    if (! WriteEvent(EVENTLOG_INFORMATION_TYPE,  
                     APP_ACTION,
                     APP_CLOSE,
                     0,
                     0,
                     NULL,
                     NULL)
    )
        TRACE( _T("Can't Write it in the Eventlog\n") );
}

//セキュリティ識別子(SID)取得
BOOL CFileExistDlg::GetSID(PSID *ppSid)  
{
    CString          strUser;  
    DWORD            dwUserSize   = 0;  
    BOOL             bRet         = FALSE;  
    DWORD            dwSidSize    = 0;  
    DWORD            dwDomainSize = 0;  
    CString          strDomain;  
    PSID             pSid         = NULL;  
    SID_NAME_USE     snu;  

    //ユーザ名取得
    ::GetUserName(NULL, &dwUserSize);  
    if ( ::GetLastError() != ERROR_INSUFFICIENT_BUFFER )  
        return FALSE;  

    bRet = ::GetUserName(strUser.GetBuffer(dwUserSize), &dwUserSize);  
    strUser.ReleaseBuffer();

    if (! bRet)  
        return FALSE;  

    //SID取得
    ::LookupAccountName(NULL, strUser, NULL, &dwSidSize, NULL, &dwDomainSize, &snu);  
    if ( ::GetLastError() != ERROR_INSUFFICIENT_BUFFER )  
        return FALSE;  

    pSid = (PSID) new BYTE[dwSidSize];  

    bRet = ::LookupAccountName(NULL, strUser, pSid, &dwSidSize,  
                               strDomain.GetBuffer(dwDomainSize), &dwDomainSize, &snu);
    strDomain.ReleaseBuffer();

    if (! bRet)  
        delete [] pSid;  
    else
        *ppSid = pSid;  

    return bRet;  
}

//イベントログへの書込み
BOOL CFileExistDlg::WriteEvent(WORD wType,  
                               WORD wCategory,  
                               DWORD dwEventID,  
                               WORD wNumStrings,  
                               DWORD dwDataSize,  
                               LPCTSTR *lpStrings,  
                               LPVOID lpRawData)  
{
    PSID       pSid   = NULL;  
    HANDLE     hEvent = 0;  
    BOOL       bRet   = FALSE;  

    do
    {
        //セキュリティ識別子取得
        if (! GetSID(&pSid) )  
            break;

        //イベントソースのハンドル取得
        hEvent = ::RegisterEventSource( NULL, _T(EVENT_SRC_NAME) );  
        if (! hEvent)  
            break;

        //イベントログへの書込み
        if (! ::ReportEvent(hEvent, wType, wCategory, dwEventID, pSid,  
                            wNumStrings, dwDataSize, lpStrings, lpRawData)  
        )
            break;

        bRet = TRUE;  
    } while(0);  

    if (pSid)   { delete [] pSid;                      pSid = NULL;}  
    if (hEvent) { ::DeregisterEventSource(hEvent);     hEvent = 0;}  

    return bRet;  
}

<1.イベントログへの書込み>

アプリケーションの起動時・終了時とファイルの検索時(OnBnClickedBtnExec実行時) に上記のWriteEventによってイベントログに書き込んでいます。 WriteEvent内で、RegisterEventSourceReportEventによって イベントの各項目をイベントログに 書き込んでいます。実際にイベントログに書き込むAPI、RegisterEventSource/ReportEventの定義は 次のようになっています。

HANDLE RegisterEventSource(
  LPCTSTR lpUNCServerName,  // 操作を実行するサーバーの名前
  LPCTSTR lpSourceName      // 登録済みハンドルを取得するイベントソースの名前
);
BOOL ReportEvent(
  HANDLE  hEventLog,   // イベントログのハンドル(RegisterEventSourceで得たハンドル)
  WORD    wType,       // ログに書き込むイベントの種類
  WORD    wCategory,   // イベントの分類
  DWORD   dwEventID,   // イベント識別子
  PSID    lpUserSid,   // ユーザーセキュリティ識別子(省略可能)
  WORD    wNumStrings, // メッセージにマージする文字列の数
  DWORD   dwDataSize,  // バイナリデータのサイズ(バイト数)
  LPCTSTR *lpStrings,  // メッセージにマージする文字列の配列
  LPVOID  lpRawData    // バイナリデータのアドレス
);

実際にログに書き込むReportEventの各引数の内容は次のようになっています。

引数 指定内容
hEventLog 前頁の前準備で レジストリに登録したイベントソース名(MyApp)を設定して得たハンドルを、 RegisterEventSourceの第二引数に指定して得たハンドル
wType エラー、警告などイベントの種類を指定する(winnt.h内の値)
wCateogory メッセージテキスト( MyAppMsg.MC)をmc.exeでコンパイルして できたMyAppMsg.hヘッダ内のカテゴリのIDを指定する
dwEventID メッセージテキスト( MyAppMsg.MC)をmc.exeでコンパイルして できたMyAppMsg.hヘッダ内のメッセージのIDを指定する
lpUserSid 現在のユーザ名を指定しますが、文字列として渡すのではなく セキュリティ識別子(SID)で渡さなければならないので、LookupAccountName APIを使って SIDを取得することになります。(上記GetSID関数を参照)
wNumStrings 前頁の メッセージファイルの例で%1のプレースホルダで示されているパラメータに埋め込む文字列を 指定する引数です。1つのメッセージあたり、%2、%3・・・とプレースホルダがあれば、 その数をwNumStringsに指定して、文字列の配列へのポインタを指定します。
lpStrings
dwDataSize dwDataSizeとlpRawDataは任意のデータ (今回はファイル検索時にファイルの属性を指定)を設定します。dwDataSizeはlpRawDataのサイズを、 lpRawDataはログに記録するデータの領域へのポインタを指定します。
lpRawData

記述例は、上記のWriteEventの定義、このメンバ関数が 使われているCFileExist::OnInitDialog、CFileExist::PreEndDialog、 CFileExist::OnBnClickedBtnExecをご覧ください。
また、カスタムパフォーマンスモニタの 作成時にもイベントログへの書込みを行っているのでご覧になってください。

以上でイベントログへの書込みのすべての作業が終了したので、FileExistプロジェクトを ビルドして実行後、任意のファイル検索をしてください。そのあと、イベントビューアでイベントログが 記録されていることを確認してください。