リストビュー コントロール(以下、リストビュー)とは、エクスプローラの右側や、タスク マネージャのタスク一覧などのように、表形式で情報を表示することのできる、Windows標準のコモンコントロールです。実際には表形式になっていないものもあり、デスクトップのアイコンやスタートボタンのアイコンなども、リストビューで表現されることがあります。
今回は、環境上のリストビューを列挙する方法について記述します。
どういうことかというと、例えば、一つの画面上に、エクスプローラ・いじくるつくーる・タスク マネージャが立ち上がっていたとしたら、これらのソフトウェアが所持しているリストビューを指すようなID番号を見つけて、列挙するということです。
Windowsでは、個々のコントロールについて、通常我々が目にする「ウィンドウ」と同格に存在として管理しています。つまり、リストビューもツリービューも、編集ボックスもリストボックスも、全部Windows的には「ウィンドウ」の一種となります。ただ、それぞれに非常に個性的な特徴があるだけに過ぎません。
Windowsでは、ウィンドウは「ウィンドウハンドル」(以下、ハンドル)で管理しており、リストビューについても、この「ハンドル」で管理されています。
個々のリストビューにアクセスするときのID番号についても、この「ハンドル」を用いることになります。
今回は、環境上のリストビューから情報を取得するためのソフトウェアを作ろうとしていますので、私たちは環境上のリストビューにアクセスするためのID番号を知らなければなりません。そこでまずは、この「ハンドル」の一覧を取得することにしましょう。
ハンドルの取得方法については、これといって難しいことはありません。しかし、「コールバック関数」という、少し特殊な概念が登場しますので、そこで進まなくなってしまう方もいるかもしれません。が、とりあえずはそういう単なる記号だと思って進めていってしまっても良いと思います。
先ほど項でも少し触れたとおり、コールバック関数が登場する方法となります。具体的に何を用いるのかというと、Windows API の EnumWindows と EnumChildWindows です。
これらにコールバック関数を渡してやると、1つのウィンドウまたは1つのコントロールが見つかるごとに、コールバック関数が1回ずつ呼ばれます。
コールバック関数が呼ばれるとき、ウィンドウまたはコントロールのハンドルが1つ、渡されますので、そのハンドルを検査して、リストビューのものかどうかを判断することになります。
ハンドルがリストビューのものかどうかを判定する基準は、ハンドルのクラス名を取得して、WC_LISTVIEW(文字列"SysListView32"を表す)と合致しているかどうかです。ハンドルのクラス名の取得には、GetClassName API を用います。
というわけで、これまで述べてきたことを利用して、実際にソースコードを書いてみましょう。
ちなみにこのサンプルソースコードは、作者のC++の練習も兼ねているので、たまにC++文法が登場します。
// ListView to CSV for CommandLine (lvcsvcom.cpp)
// Copyright(C) 2005 T.Yabuki
#include <windows.h>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
#include <iostream>
#include <iomanip>
using namespace std;
#define MAX_GETWINDOWTEXT 79
#define MAX_GETWINDOWCLASS 79
// --------------------------------------------------------------
// EnumChildWindowsにより呼び出されるコールバック関数
// --------------------------------------------------------------
// 引数 :hWnd = コントロールのハンドル
// p_lpszTitleBar = タイトルバー文字列へのポインタ
// 戻り値:TRUE(必ずEnumChildWindowsを継続させる)
// --------------------------------------------------------------
BOOL CALLBACK callback_EnumChildWindowsProc(HWND hCtrl, LPARAM p_lpszTitleBar)
{
char szClassName[MAX_GETWINDOWCLASS+1];
char *lpszTitleBar = (char *)p_lpszTitleBar;
// クラス名を取得する
if (GetClassName(hCtrl, szClassName, MAX_GETWINDOWCLASS)) {
if ( strcmp(szClassName, WC_LISTVIEW) == 0 ) {
// クラス名がWC_LISTVIEW(文字列"SysListView32"を表す)であれば
// リストビューであると見なす→ウィンドウハンドルと要素数を表示。
// 各ウィンドウにつき1回ずつ、タイトルバー文字列を表示する。
// タイトルバー文字列を1回表示したら、lpszTitleBarの最初の
// 1バイトをNULL文字にしておく。これにより、タイトルバー文字列が
// 表示済みであることを表すことにする。
if ( lpszTitleBar && lpszTitleBar[0] ) {
cout << "--------------------" << endl << lpszTitleBar << endl;
lpszTitleBar[0] = '\0';
}
// ウィンドウハンドルを8桁の16進数で表示
cout << " ハンドル番号:" << setw(8) << setfill('0') << hex << (UINT)hCtrl;
// 要素数を表示
cout << " アイテム数:" << dec << ListView_GetItemCount(hCtrl) << endl;
}
}
return TRUE;
}
// --------------------------------------------------------------
// EnumWindowsにより呼び出されるコールバック関数
// --------------------------------------------------------------
// 引数 :hWnd=ウィンドウハンドル
// 戻り値:TRUE(必ずEnumWindowsを継続させる)
// --------------------------------------------------------------
BOOL CALLBACK callback_EnumWindowsProc(HWND hWnd, LPARAM)
{
char szTitleBar[MAX_GETWINDOWTEXT+1] = "????????????????";
// 調査中のウィンドウのタイトルバーを取得しておく
// (コマンドプロンプトの窓は横幅80カラムであると仮定するので、
// タイトルバーの文字列は最大で79バイトまで取得する)
GetWindowText(hWnd, szTitleBar, MAX_GETWINDOWTEXT);
// もし、タイトルバーが空っぽであれば、「タイトルなし」の文字列を代入する。
if (szTitleBar[0] == '\0') {
strcpy(szTitleBar, "タイトルなし");
}
// コントロールの一覧を列挙する
// (コントロール1つにつき1回ずつ、callback_EnumChildWindowsProc() が呼ばれる)
EnumChildWindows(hWnd, (WNDENUMPROC)callback_EnumChildWindowsProc, (LPARAM)szTitleBar);
// 返却値TRUE:EnumWindowsによるウィンドウハンドルの列挙を継続する
return TRUE;
}
// --------------------------------------------------------------
// リストビューコントロールの一覧を作成
// --------------------------------------------------------------
// 引数 :なし
// 戻り値:なし
// --------------------------------------------------------------
void showListViewControls()
{
// ウィンドウの一覧を列挙する
// (ウィンドウ1つにつき1回ずつ、callback_EnumWindowsProc() が呼ばれる)
EnumWindows((WNDENUMPROC)callback_EnumWindowsProc, 0);
}
// --------------------------------------------------------------
// メイン関数
// --------------------------------------------------------------
// 引数:今のところはなし
// --------------------------------------------------------------
int main(int argc, char* argv[])
{
// コモンコントロールの初期化
InitCommonControls();
// コマンドラインの解析
if ( argc < 2 ) {
// コマンドライン引数が何も指定されていない場合は、
// リストビューコントロールのハンドル番号一覧を表示。
showListViewControls();
return 0;
}
else {
cout << "まだ実装されていません。" << endl;
}
return 0;
}
あまり解説することはありませんが、軽く流れを解説しておきます。
まず、main関数が呼ばれます。main関数からshowListViewControls関数が呼ばれます。
showListViewControls関数で、先に登場したEnumWindows API を呼び出します。EnumWindowsは、環境上のすべてのウィンドウを列挙し、列挙のたびにcallback_EnumWindowsProc コールバック関数を呼び出します。
callback_EnumWindowsProc では、ウィンドウ内のコントロールを列挙します。列挙のために、先に登場したEnumChildWindows API を呼び出します。EnumChildWIndowsは、ウィンドウ内のすべてのコントロールを列挙し、列挙のたびにcallback_EnumChildWindowsProc コールバック関数を呼び出します。
callback_EnumChildWindowsProc では、渡されたハンドルのクラス名の取得を行い、クラス名がリストビューのクラス名と同じであるかどうかを調べています。リストビューのクラスメイト同じであった場合は、そのハンドルを出力します。なお、ハンドルだけが出力されても、なんのことだかわかりませんので、ヒントとして、アイテムの個数と、リストビューを所有するウィンドウのウィンドウタイトルを出力します。
ウィンドウタイトルは1回だけ出力します。仕組みについての解説は割愛します。
出力結果:
次回は、リストビューから文字列を取得してみます。しかし、なかなかうまくいかない部分であるため、次回は失敗例を挙げてみようと思います。