概要
<手前ウィンドウのタイトル>と、<手前ウィンドウを表示するプロセスのモジュールのフルパス>をコンソール出力するツールを作成した。![]() |
動作の様子。500ミリ秒おきにコンソール出力する。 |
製造
百聞は一見に如かず。ソースコードの主要な個所を以下に示す。
メイン関数
- #include <fcntl.h>
- #include <io.h>
- #include <stdlib.h>
- #include <wchar.h>
- #include <windows.h>
- #include "process_tools.h"
- int main(void) {
- HWND hWnd = NULL;
- DWORD processId = 0;
- HANDLE hProcess = NULL;
- std::vector<std::wstring> moduleNames;
- wchar_t title[MAX_PATH] = {0};
- // Use Unicode.
- _setmode(_fileno(stdout), _O_U8TEXT);
- while (true) {
- // Open process handle.
- // Window handle -> Process ID -> Process handle
- hWnd = GetForegroundWindow();
- GetWindowThreadProcessId(hWnd, &processId);
- hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
- processId);
- if (hProcess == NULL) {
- return false;
- }
- // Get name of each module.
- tools::EnumModuleNames(hProcess, moduleNames);
- // Get title of window.
- GetWindowText(hWnd, title, MAX_PATH);
- // Show window title and module names.
- wprintf(L"%ls\n", title);
- wprintf(L"%u modules\n", moduleNames.size());
- for (auto& module : moduleNames) {
- wprintf(L" %ls\n", module.c_str());
- }
- wprintf(L"--------------------------------------------------\n");
- wprintf(L"\n");
- // Close process handle.
- CloseHandle(hProcess);
- // Interval in read.
- Sleep(500);
- }
- return EXIT_SUCCESS;
- }
ポイント
- EXE と DLL
アプリケーションの EXE だけでなく、アプリケーションがロードした DLL を
合わせてコンソールに表示する。EnumProcessModulesEx 関数がフルパスの一覧を
取得する。 - Unicode 出力について
ウィンドウテキストが日本語の場合もコンソール出力で困らないように
以下のステップで標準出力を UTF-8 にする。- setmode(_fileno(stdout), _O_U8TEXT);
※このステップを入れずにコードページを 65001 とし、
フォントを日本語対応フォント(MS Gothic)とした際は、
最初の1文字以降が尻切れになる問題が発生した。
原因を調べていたところ、参考文献4にこの方法を見つけ、
問題は解決した。
関数ヘッダファイル
- #pragma once
- #include <wchar.h>
- #include <windows.h>
- #include <string>
- #include <vector>
- namespace tools {
- constexpr int ARGUMENT_ERROR = -1;
- // (中略)
- int EnumModuleNames(HANDLE hProcess, std::vector<std::wstring>& moduleNames);
- } // namespace tools
関数ソースファイル
- #include "./process_tools.h"
- #include <psapi.h>
- #include <windows.h>
- #include <algorithm>
- // (中略)
- namespace tools {
- // (中略)
- //
- // @brief <br> Get the module file path of given window.
- // @param [in] hProcess process handle.
- // @param [out] moduleNames Full path for modules.
- // @return Actually read module number.
- //
- int EnumModuleNames(HANDLE hProcess, std::vector<std::wstring>& moduleNames) {
- if (hProcess == NULL) {
- return ARGUMENT_ERROR;
- }
- // Enumerate modules of process.
- DWORD dwSize = 0;
- EnumProcessModulesEx(hProcess, NULL, 0, &dwSize, LIST_MODULES_ALL);
- std::vector
hModules(dwSize / sizeof(HMODULE)); if (EnumProcessModulesEx( hProcess, hModules.data(), static_cast (sizeof(HMODULE) * hModules.size()), &dwSize, LIST_MODULES_ALL) == 0) { return 0; } // Get file path of modules. moduleNames.clear(); wchar_t strBuf[MAX_PATH] = {0}; for (auto& hModule : hModules) { GetModuleFileNameEx(hProcess, hModule, strBuf, MAX_PATH); moduleNames.push_back(strBuf); } return static_cast<int>(moduleNames.size()); } } // namespace tools
ポイント
- EnumProcessModules を 2回呼び出す
1回目の呼び出しではプロセスのモジュールの数を取得し、
2回目の呼び出しではプロセスのモジュールのハンドルを取得する。
この方法のメリットは、先に数が分かり、動的に配列を準備できることだ。
この方法のデメリットは、2回の呼び出しに冗長であることと、
「1回目と2回目の呼び出しの間に、モジュールの数が変わらないの?」
という懸念があることだ。
※尚、MSDN では決め打ちで十分に大きな固定長配列を準備することが
推奨されている。 - 処理の流れ
処理の流れを以下に示す(参考文献1、参考文献2)。
1. 手前ウィンドウのウィンドウハンドルを取得する(main関数)
2. ウィンドウハンドルからプロセスIDを取得する(main関数)
3. プロセスIDのプロセスを開く(main関数)
4. プロセスのモジュールハンドルを列挙する(本関数)
5. モジュールのフルパスを取得する(本関数)
※EnumProcessModulesEx について、詳しくは MSDN の
リファレンスを見てほしい(参考文献3)
テスト
googletest を用いて EnumModuleNames 関数の単体テスト(C0)を実施した。NuGet を利用し gmock v1.10.0 パッケージマネージャをインストールした。
(main関数の方は適当やで)
環境
開発環境および動作確認を実施したPCの情報を以下に示す。
開発環境
Visual Studio Community 2015
PC情報開発環境
Visual Studio Community 2015
OS 名: Microsoft Windows 8.1
OS バージョン: 6.3.9600 N/A ビルド 9600
システム モデル: dynabook KIRA V73/PS
システムの種類: x64-based PC
物理メモリの合計: 8,103 MB
プロセッサ: Intel64 Family 6 Model 61 Stepping 4 GenuineIntel ~2200 Mhz
なぜ作った
必要を感じたから。- 見切れているウィンドウタイトルの全体を知りたい
- 長いウィンドウタイトルをコピーしたい
稀によくあるシチュエーションである。
感想
単体テストしないと落ち着かない性質になってしまった。バグを出さないことは重要であり、そのためには多大な労力が必要なんだな
と感じる今日この頃である。
0 件のコメント:
コメントを投稿
コメント表示は承認制に設定しています