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

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

(パフォーマンスデータ収集クラス実装)

このクラスの実装内容も本題とは違うので、同期オブジェクト に関することを含めて詳しい説明はプロセス間通信をご覧下さい。 ここでは手順だけ書きます。

・同期クラス作成

MFCではプロセス間通信の際に使う同期オブジェクトはCSyncObjectの派生クラスとして ライブラリ化されていたので便利でしたが、このプロジェクトはWin32プロジェクトなので これは使えないので自分で作らないといけません。ということで以下のように同期クラスを 実装してください。(内容はMFCのソースをかなり参考にしています)

ソリューションエクスプローラの PerfDnpプロジェクトフォルダを右クリックして追加(D)→クラス(C)でクラスの 追加ダイアログが起動しますので以下の設定をしてください。

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

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

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

同期クラスのSimpleSync.h、SimpleSync.cppができているので 以下のように定義・実装してください。

//同期オブジェクト:ヘッダファイル - SimpleSync.h
#pragma once

//CSimpleSync 同期オブジェクト基底クラス////////////////////////////////
class CSimpleSync
{
    //メンバ変数//////////////////////////////////////
protected:
    HANDLE        m_hObject;                  //同期オブジェクトハンドル

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

protected:
    CSimpleSync(LPCWSTR lpszName);            //引数付コンストラクタ

public:
    ~CSimpleSync();                           //デストラクタ
    BOOL Lock(DWORD dwTimeout = INFINITE);    //所有権獲得
};



//CSimpleSemaphore セマフォ同期オブジェクト/////////////////////////////
class CSimpleSemaphore : public CSimpleSync
{
    //メンバ関数///////////////////////////////////////
public:
    CSimpleSemaphore(LONG lInitialCount,       //引数付コンストラクタ
                     LONG lMaxCount,
                     LPCWSTR lpName = NULL);

    ~CSimpleSemaphore();                       //デストラクタ

    BOOL Release(LONG lCount = 1);             //セマフォ返却
};
//同期オブジェクト:実装ファイル - SimpleSync.cpp
#include "StdAfx.h"
#include "SimpleSync.h"

//CSimpleSync 同期オブジェクト基底クラス////////////////////////////////
//標準コンストラクタ(private)
CSimpleSync::CSimpleSync() : m_hObject(NULL)
{
}

//コピーコンストラクタ(private)
CSimpleSync::CSimpleSync(const CSimpleSync &obj) : m_hObject(NULL)
{
}

//引数付コンストラクタ(protected)
//引数 lpszName セマフォ名
CSimpleSync::CSimpleSync(LPCWSTR lpszName) : m_hObject(NULL)
{
}

//所有権獲得
//引数 dwTimeout セマフォ獲得までのタイムアウト
//戻値 セマフォ獲得TRUE
BOOL CSimpleSync::Lock(DWORD dwTimeout /*=INFINITE*/)
{
    if (! m_hObject)
        return FALSE;

    DWORD dwRet = ::WaitForSingleObject(m_hObject, dwTimeout);

    //オブジェクトのシグナル化or 所有権を保持したスレッドが解放なしに終了
    if (dwRet == WAIT_ABANDONED || dwRet == WAIT_OBJECT_0)
        return TRUE;
    else
        return FALSE;
}

//デストラクタ
CSimpleSync::~CSimpleSync()
{
    if (m_hObject)
    {
        ::CloseHandle(m_hObject);            m_hObject = NULL;
    }
}



//CSimpleSemaphore セマフォ同期オブジェクト////////////////////////////////////////
//引数付コンストラクタ
//引数 lInitialCount セマフォ初期カウント(初期アクセス許可スレッド数)
//     lMaxCount セマフォ最大カウント(最大アクセス許可スレッド数)
//     lpName セマフォ名
CSimpleSemaphore::CSimpleSemaphore(LONG lInitialCount,
                                   LONG lMaxCount,
                                   LPCWSTR lpName /*=NULL*/)
: CSimpleSync(lpName)
{
    if (lMaxCount <= 0 || lMaxCount < lInitialCount)
        return;

    m_hObject = ::CreateSemaphore(NULL, lInitialCount, lMaxCount, lpName);
}

//セマフォ返却
//引数 lCount セマフォカウント増分値
//戻値 セマフォカウンタ増分成功時TRUE
BOOL CSimpleSemaphore::Release(LONG lCount /*=1*/)
{
    return m_hObject ? ::ReleaseSemaphore(m_hObject, lCount, NULL) : FALSE;
}

//デストラクタ
CSimpleSemaphore::~CSimpleSemaphore()
{
    Release();
}

・パフォーマンスデータ収集クラス実装

Dnpアプリケーションをモニタリングする時点で、このウィンドウ(インスタンス)が いくつ起動しているかは不明です。Dnpウィンドウを配列の形でインスタンス数、個々のプロセスID、 共有メモリを管理して、DLL内で Collect関数での要求に応じて パフォーマンスデータを供給する機能をCDnpArrayというクラスで実装します。 クラスの機能は下図のようになります。(プロセスIDの管理は省略しています。)

ソリューションエクスプローラの PerfDnpプロジェクトフォルダを右クリックして追加(D)→クラス(C)でクラスの 追加ダイアログが起動しますので以下の設定をしてください。

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

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

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

同期クラスのDnpArray.h、DnpArray.cppができているので 以下のように定義・実装してください。

//Dnpアプリケーション共有データ配列クラスヘッダファイル - DnpArray.h
#pragma once

//前方宣言---------------
class CSimpleSemaphore;
//-----------------------

class CDnpArray
{
    //メンバ変数//////////////////////////////////////////////////
protected:

    typedef struct _DNP_BYPROCESS //各プロセスの共有メモリなどデータの配列
    {
        int                nNum;           //起動しているDnpのプロセス数
        DWORD              *pdwProcess;    //DnpプロセスIDの配列
        HANDLE             *phMapFile;     //ファイルマッピングオブジェクトハンドルの配列
        SHARE_MEM          **ppShareMem;   //共有メモリへのポインタの配列
        CSimpleSemaphore   **ppSema;       //共有メモリへの排他アクセス用
                                           //セマフォへのポインタの配列
    } DNP_BYPROCESS, *LPDNP_BYPROCESS;

    DNP_BYPROCESS m_DnpData;               //共有メモリデータ

//メンバ関数/////////////////////////////////////////////////
protected:
    CDnpArray(const CDnpArray &obj);                   //コピーコンストラクタ
    static BOOL CALLBACK EnumWndProc(HWND hWnd,
                                     LPARAM lParam);   //プロセスID取得用EnumWindows用
                                                       //コールバック関数
    BOOL CreateMapObject();                            //File Mapping Object作成
    BOOL CreateMapView();                              //File Mapping Objectのビュー作成
    BOOL CreateSema();                                 //共有メモリアクセス用セマフォ作成

public:
    CDnpArray();                             //コンストラクタ
    ~CDnpArray();                            //デストラクタ
    int Initialize();                        //初期化(稼動中プロセス/マッピングオブジェクト
                                             ///セマフォ構築)
    void Terminate();                        //終了処理
    DWORD GetCount() const;                  //起動中Dnpプロセス数
    DWORD GetProcessId(int nIdx) const;      //各プロセスID取得
    const void *operator[] (int nIdx) const; //各プロセスの共有メモリへのポインタ取得
    BOOL Lock(int   nIdx,
              DWORD dwTimeout) const;        //セマフォ獲得
    BOOL UnLock(int nIdx = -1) const;        //セマフォ解放
};
//Dnpアプリケーション共有データ配列クラス実装ファイル - DnpArray.cpp
#include "StdAfx.h"

#include "..\\Dnp\\DnpShare.h"
#include "SimpleSync.h"

#include "DnpArray.h"

#define    MAX_CAPTION    32        //Window最大表題文字数・各オブジェクト名最大文字数


typedef CSimpleSemaphore *LPCSIMPLESEMAPHORE;

//コンストラクタ
CDnpArray::CDnpArray()
{
    ::memset( &m_DnpData, 0, sizeof(DNP_BYPROCESS) );
}

//コピーコンストラクタ(protected)
CDnpArray::CDnpArray(const CDnpArray &obj)
{
}

//デストラクタ
CDnpArray::~CDnpArray()
{
    Terminate();
}

//初期化(稼動中プロセスID取得/マッピングオブジェクト・ビュー構築/セマフォ構築)
//戻値 成功 : 0
//     プロセスID取得失敗:1   ファイルマッピングオブジェクト構築失敗:2
//     ビュー構築失敗:3      セマフォ構築失敗:4
int CDnpArray::Initialize()
{
    int nRet = 0;

    //各プロセスデータクリア
    Terminate();

    do
    {
        //稼動プロセスID配列作成
        if (! ::EnumWindows(EnumWndProc, (LPARAM)&m_DnpData) )  {nRet = 1;    break;}

        //稼動プロセスない時0を返却
        if (! m_DnpData.nNum)
            break;

        //各プロセスごとにファイルマッピングオブジェクト構築
        if (! CreateMapObject() )                               {nRet = 2;    break;}

        //各プロセスごとにマッピングオブジェクトのビュー構築
        if (! CreateMapView() )                                 {nRet = 3;    break;}

        //セマフォ構築
        if (! CreateSema() )                                    {nRet = 4;    break;}

    } while(0);

    if (nRet)
        Terminate();

    return nRet;
}

//終了処理(プロセスID配列解放/マッピングオブジェクト・ビューClose/セマフォ破棄)
void CDnpArray::Terminate()
{
    if (! m_DnpData.nNum)     //稼動プロセスなし
        return;

    if (m_DnpData.ppSema)     //セマフォ配列解放
    {
        for (int i=0; i<m_DnpData.nNum; i++)
        {
            delete m_DnpData.ppSema[i];                  m_DnpData.ppSema[i] = NULL;
        }
        delete [] m_DnpData.ppSema;                      m_DnpData.ppSema = NULL;
    }

    if (m_DnpData.ppShareMem) //共有メモリ配列アンマップ
    {
        for (int i=0; i<m_DnpData.nNum; i++)
        {
            ::UnmapViewOfFile(m_DnpData.ppShareMem[i]);  m_DnpData.ppShareMem[i] = NULL;
        }
        delete [] m_DnpData.ppShareMem;                  m_DnpData.ppShareMem = NULL;
    }

    if (m_DnpData.phMapFile)  //ファイルマッピングオブジェクトClose
    {
        for (int i=0; i<m_DnpData.nNum; i++)
        {
            ::CloseHandle(m_DnpData.phMapFile[i]);       m_DnpData.phMapFile[i] = NULL;
        }
        delete [] m_DnpData.phMapFile;                   m_DnpData.phMapFile = NULL;
    }

    if (m_DnpData.pdwProcess) //プロセスID配列解放
    {
        delete [] m_DnpData.pdwProcess;                  m_DnpData.pdwProcess = NULL;
    }

    m_DnpData.nNum = 0;       //稼動プロセス数にリセット
}

//DnpプロセスID配列取得用EnumWindows用コールバック関数(Initialize内でCall)
//引数 hWnd トップレベルの全ウィンドウのハンドル
//     lParam 各プロセスのデータを保持するm_DnpDataへのポインタが渡される
//戻値 常にTRUEを返却
//仕様 hWndがDnpダイアログの時プロセスIDをpdwProcessメンバに加え、プロセス数のnNumメンバ
//     を1インクリメント
//     エラー時はそのプロセスIDはm_DnpData内に加えられない
BOOL CALLBACK CDnpArray::EnumWndProc(HWND hWnd, LPARAM lParam)
{
    DNP_BYPROCESS     *pDnpArray = reinterpret_cast<DNP_BYPROCESS *>(lParam);
    WCHAR             wszCaption[MAX_CAPTION];       //Window表題
    WCHAR             wszPreCaption[MAX_CAPTION];    //一時Windows表題プレフィックス
    const WCHAR       wszPreDnp[] = _L(DNP_CAPTION); //Dnp表題プレフィックス
    WCHAR             wszProcessId[MAX_CAPTION];     //プロセスID文字列
    DWORD             dwProcessId;                   //プロセスID
    WCHAR             *pwszEnd = NULL;
    DWORD             *pdwTemp = NULL;               //プロセスID配列結果用

    //現在のウィンドウのCaptionをwszCaptionに取得
    if (! ::GetWindowText(hWnd, wszCaption, MAX_CAPTION) )
        return TRUE;

    //wszPreCaptionにPreDnp(DnpApp-)の文字列数分だけ取得した表題をコピー
    if ( ::wcsncpy_s(wszPreCaption, MAX_CAPTION, wszCaption, wcslen(wszPreDnp)) )
        return TRUE;

    //wszPreCaptionとwszPreDnpの比較(表題プレフィックス比較)--------------------
    if ( ::wcsncmp(wszPreCaption, wszPreDnp, wcslen(wszPreDnp)) )
        return TRUE;
    //--------------------------------------------------------------------------

    //ProcessID分の文字列コピー
    if ( wcscpy_s(wszProcessId, MAX_CAPTION, wszCaption + wcslen(wszPreDnp)) )
        return TRUE;

    //文字列ProcessIDを数値化
    dwProcessId = ::wcstoul(wszProcessId, &pwszEnd, 10);
    if (dwProcessId == 0xffffffffUL || ! dwProcessId )
        return TRUE;

    //新しいProcessID領域確保
    pdwTemp = new DWORD[ ++(pDnpArray->nNum) ];
    pdwTemp[pDnpArray->nNum - 1] = dwProcessId;       //新規の値を最後の要素にセット

    //以前のProcessIDの配列をpdwTempにコピー
    if (pDnpArray->nNum > 1)
    {
        if (::memcpy_s(pdwTemp,
                       sizeof(DWORD) * pDnpArray->nNum,
                       pDnpArray->pdwProcess,
                       sizeof(DWORD) * (pDnpArray->nNum - 1)) )
        {
            delete [] pdwTemp;       --(pDnpArray->nNum);
            return TRUE;
        }

        //前回までのProcessIDの配列解放
        delete [] pDnpArray->pdwProcess;
    }

    //新しいProcessIDの配列設定
    pDnpArray->pdwProcess = pdwTemp;

    return TRUE;
}

//ファイルマッピングオブジェクト作成
//戻値 File Mapping Objectが作成されたor 既に作成されている時TRUE
//     Dnp稼動プロセスがない or エラー時FALSE
//仕様 エラー時は全てのプロセスのFile Mapping ObjectをCloseして
//     phMapFileメンバをNULLにセット
BOOL CDnpArray::CreateMapObject()
{
    BOOL      bRet = FALSE;
    WCHAR     wszMapFile[ MAX_CAPTION ];     //ファイルマッピングオブジェクト名
    WCHAR     wszProcessId[ MAX_CAPTION ];   //プロセスID文字列

    if (! m_DnpData.nNum)           return FALSE;  //Dnp稼動プロセスがない時FALSE

    if (m_DnpData.phMapFile)        return TRUE;   //既にFile Mapping Object配列が
                                                   //ある時TRUE

    //ファイルマッピングオブジェクトハンドル領域構築
    m_DnpData.phMapFile = new HANDLE[ m_DnpData.nNum ];

    do
    {
        BOOL bErr = FALSE;

        for (int i=0; i<m_DnpData.nNum; i++)
        {
            //ファイルマッピングオブジェクト名プレフィックスコピー
            if ( ::wcscpy_s(wszMapFile, MAX_CAPTION, _L(DNP_FILEMAP)) )
            {
                bErr = TRUE;        break;
            }
            //プロセスIDの文字列化
            if (::swprintf_s(wszProcessId,
                             MAX_CAPTION, _L("%d"), m_DnpData.pdwProcess[i]) == -1)
            {
                bErr = TRUE;        break;
            }
            //ファイルマッピングオブジェクト名構築
            if ( ::wcscat_s(wszMapFile, MAX_CAPTION, wszProcessId) )
            {
                bErr = TRUE;        break;
            }
            //ファイルマッピングオブジェクト作成
            m_DnpData.phMapFile[i] = ::CreateFileMapping(INVALID_HANDLE_VALUE,
                                                         NULL,
                                                         PAGE_READWRITE,
                                                         0,
                                                         sizeof(SHARE_MEM),
                                                         wszMapFile);
            if (! m_DnpData.phMapFile[i])
            {
                bErr = TRUE;        break;
            }
        }
        if (bErr)      break;

        bRet = TRUE;
    } while(0);

    //失敗時
    if (! bRet)
    {
        for (int i=0; i<m_DnpData.nNum; i++)
        {
            if (m_DnpData.phMapFile[i])
            {
                ::CloseHandle(m_DnpData.phMapFile[i]);   m_DnpData.phMapFile[i] = NULL;
            }
        }
        delete [] m_DnpData.phMapFile;                   m_DnpData.phMapFile = NULL;
    }

    return bRet;
}

//File Mapping Objectのビュー作成
//戻値 ビューが作成されたor 既に作成されている時 TRUE
//     Dnp稼動プロセスがない or エラー時 FALSE
//仕様 エラー時は全てのプロセスのビューをアンマップしてppShareMemメンバをNULLにセット
BOOL CDnpArray::CreateMapView()
{
    BOOL        bRet = FALSE;
    BOOL        bErr;

    if (! m_DnpData.nNum)         return FALSE;     //Dnp稼動プロセスがない時FALSE

    if (m_DnpData.ppShareMem)     return TRUE;      //既にViewのハンドル配列がある時TRUE

    //各File Mapping Objectハンドル確認
    bErr = FALSE;
    for (int i=0; i<m_DnpData.nNum; i++)
    {
        if (! m_DnpData.phMapFile[i]) {bErr = TRUE;     break;}
    }
    if (bErr)
        return FALSE;

    //共有メモリへのポインタ領域作成
    m_DnpData.ppShareMem = new LPSHARE_MEM[ m_DnpData.nNum ];

    do
    {
        //共有メモリのビューのマッピング
        bErr = FALSE;
        for (int i=0; i<m_DnpData.nNum; i++)
        {
            m_DnpData.ppShareMem[i] = (LPSHARE_MEM)::MapViewOfFile(m_DnpData.phMapFile[i],
                                                                   FILE_MAP_READ,
                                                                   0, 0, 0);
            if (! m_DnpData.ppShareMem[i])
            {
                bErr = TRUE;       break;
            }
        }
        if (bErr)     break;

        bRet = TRUE;
    } while(0);

    if (! bRet)
    {
        for (int i=0; i<m_DnpData.nNum; i++)
        {
            if (m_DnpData.ppShareMem[i])
            {
                ::UnmapViewOfFile(m_DnpData.ppShareMem[i]);
                m_DnpData.ppShareMem[i] = NULL;
            }
        }
        delete [] m_DnpData.ppShareMem;         m_DnpData.ppShareMem = NULL;
    }

    return bRet;
}

//共有メモリアクセス用セマフォ作成
//戻値 セマフォが作成された or 既に作成されている時 TRUE
//     Dnp稼動プロセスがない or エラー時 FALSE
//仕様 エラー時は全てのプロセスに対するセマフォを削除してppSemaメンバをNULLにセット
BOOL CDnpArray::CreateSema()
{
    BOOL            bRet = FALSE;
    WCHAR           wszSema[MAX_CAPTION];         //セマフォ名
    const WCHAR     wszPreSema[] = _L(DNP_SEMA);  //セマフォ名のプレフィックス
    WCHAR           wszProcessId[MAX_CAPTION];    //プロセスID文字列

    if (! m_DnpData.nNum) return FALSE;           //Dnp稼動プロセスがない時FALSE

    if (m_DnpData.ppSema) return TRUE;            //既にセマフォ配列がある時TRUE

    //各プロセスのセマフォへのポインタの格納領域の作成
    m_DnpData.ppSema = new LPCSIMPLESEMAPHORE[ m_DnpData.nNum ];

    do {
        BOOL      bErr = FALSE;

        for (int i=0; i<m_DnpData.nNum; i++)
        {

            //セマフォプレフィックス(DnpShare.hのセマフォ名のプレフィックス)コピー
            if ( ::wcscpy_s(wszSema, MAX_CAPTION, wszPreSema) )
            {
                bErr = TRUE;         break;
            }
            //プロセスIDの文字列化
            if ( ::swprintf_s(wszProcessId, 
                              MAX_CAPTION, 
                              _L("%d"), 
                              m_DnpData.pdwProcess[i]) == -1
               )
            {
                bErr = TRUE;         break;
            }
            //セマフォ名構築
            if ( ::wcscat_s(wszSema, MAX_CAPTION, wszProcessId) )
            {
                bErr = TRUE;         break;
            }

            //セマフォ構築
            m_DnpData.ppSema[i] = new CSimpleSemaphore(1, 1, wszSema);
        }
        if (bErr)     break;

        bRet = TRUE;
    } while(0);

    if (! bRet)
    {
        for (int i=0; i<m_DnpData.nNum; i++)
        {
            if (m_DnpData.ppSema[i])
            {
                delete m_DnpData.ppSema[i];         m_DnpData.ppSema[i] = NULL;
            }
        }
        delete [] m_DnpData.ppSema;         m_DnpData.ppSema = NULL;
    }

    return bRet;
}

//起動中Dnpプロセス数
DWORD CDnpArray::GetCount() const
{
    return m_DnpData.nNum;
}

//各プロセスID取得
//引数 nIdx Dnpの稼動プロセスのIndex(稼動プロセス数 - 1で指定)
//戻値 成功時:指定IndexのプロセスID  失敗時又は稼動プロセスがなし:0
//仕様 nIdxが範囲外の時はを返却(0以上で起動プロセス数より小さい)
DWORD CDnpArray::GetProcessId(int nIdx) const
{
    if(nIdx >= m_DnpData.nNum || nIdx < 0)         return 0;

    return     m_DnpData.pdwProcess[nIdx];
}

//各プロセスの共有メモリへのポインタ取得
//引数 nIdx Dnpの稼動プロセスのIndex(稼動プロセス数 - 1で指定)
//戻値 成功時:共有メモリへのポインタ 失敗時又は稼動プロセスなし:NULL
//仕様 nIdxが範囲外の時はを返却(0以上で起動プロセス数以下)
const void *CDnpArray::operator [](int nIdx) const
{
    if(nIdx >= m_DnpData.nNum || nIdx < 0)         return NULL;

    return     m_DnpData.ppShareMem[nIdx];
}

//セマフォ獲得
//引数 nIdx Dnpの稼動プロセスのIndex(稼動プロセス数 - 1で指定)
//     dwTimeout セマフォ獲得までのタイムアウト
//戻値 成功:セマフォ獲得 失敗又はDnpプロセスがないときFALSE
BOOL CDnpArray::Lock(int nIdx, DWORD dwTimeout) const
{
    if(nIdx >= m_DnpData.nNum || nIdx < 0)         return FALSE;  

    return     m_DnpData.ppSema[nIdx]->Lock(dwTimeout);
}

//セマフォ解放
//引数 nIdx Dnpの稼動プロセスのIndex(稼動プロセス数 - 1で指定)
//     nIdx == -1の時 すべてのセマフォを解放
//戻値 成功:セマフォ解放 失敗又はDnpプロセスがないときFALSE
BOOL CDnpArray::UnLock(int nIdx /*=-1*/) const
{
    if(nIdx >= m_DnpData.nNum || nIdx < -1) return FALSE;  

    if (nIdx == -1) //すべてのセマフォ解放要求
    {
        BOOL      bErr = FALSE;

        for (int i=0; i<m_DnpData.nNum; i++)
        {
            if (! m_DnpData.ppSema[i]->Release() )  { bErr = TRUE;       break;}
        }

        return m_DnpData.nNum ? !bErr : FALSE;
    }

    return m_DnpData.ppSema[nIdx]->Release();
}

以上でパフォーマンスデータ収集クラスの実装は終了です。このクラスと イベントログ管理クラス を使って、本題のパフォーマンスDLLを実装します。