◆パフォーマンスモニタ 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を実装します。