リストビューの表示を定期的にファイルに出したい(3)

続いては、

  • モジュール名が指定された文字列を含んでいれば
  • 子ウインドウを列挙する
  • さらに、ListViewかどうかも判定する

という処理を加えます。イベントハンドラー(コールバック関数)に渡したいパラメーターが「指定された文字列」と「モジュール名を取得する関数」の2つになってしまったので、適当に構造体を作ることにします。
あと、Cygwinにはstrcasestr()がないので、一度全部小文字に変換しています。まあこの辺は適当に・・・。
パラメーターを構造体にしたおかげで、関数ポインターをキャストするところがLispみたいに括弧だらけになってしまった。まあその辺も気にしない。

#include <stdio.h>
#include <ctype.h>
#include <windows.h>
#include <commctrl.h>

#define BUFSIZE 128
#define DEFAULT_MODULE "Explorer.EXE"

typedef struct _callback_args{
  void *fp;
  char module_name[BUFSIZE];
} callback_args;

BOOL CALLBACK EnumWindowsProc(HWND, LPARAM);
BOOL CALLBACK EnumChildProc(HWND, LPARAM);
void tolower_str(char *);

int main(int argc, char **argv){
  HINSTANCE hInstpsapi;
  callback_args cba;  

  if(argc > 1){
    strncpy(cba.module_name, argv[1], BUFSIZE);
  }
  else{
    strncpy(cba.module_name, DEFAULT_MODULE, BUFSIZE);
  }
  tolower_str(cba.module_name);
  
  if((hInstpsapi = LoadLibrary("psapi.dll")) == NULL){
    return -1;
  }
  if((cba.fp = GetProcAddress(hInstpsapi, "GetModuleBaseNameA")) == NULL){
    return -1;
  }
  EnumWindows(EnumWindowsProc, (LPARAM)&cba);
  FreeLibrary(hInstpsapi);

  return 0;
}

BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
  char title[BUFSIZE], modulename[BUFSIZE];
  DWORD pid;
  HANDLE hProc;
  DWORD (WINAPI *fpGetModuleBaseName)(HANDLE, HMODULE, LPTSTR, DWORD) =
    (DWORD (WINAPI *)(HANDLE, HMODULE, LPTSTR, DWORD))(((callback_args *)lParam)->fp);

  GetWindowThreadProcessId(hWnd, &pid);
  if(!(hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid))){
    return 2;
  }
  fpGetModuleBaseName(hProc, NULL, modulename, BUFSIZE);
  tolower_str(modulename);
  GetWindowText(hWnd, title, BUFSIZE);
  printf("%08X, %s, %s\n", (unsigned)hWnd, modulename, title);
  
  if(strstr(modulename, ((callback_args *)lParam)->module_name) != NULL){
    EnumChildWindows(hWnd, EnumChildProc, lParam);
  }
  return 1;
}

BOOL CALLBACK EnumChildProc(HWND hWnd, LPARAM lParam)
{
  char classname[BUFSIZE];
  GetClassName(hWnd, classname, BUFSIZE);
  printf("    %08X, %s\n", (unsigned)hWnd, classname);
  if(strstr(classname, WC_LISTVIEW) != NULL){
    printf("      => %s is ListView (%d items)\n", classname, ListView_GetItemCount(hWnd));
  }
  return 1;
}

/* lower string */
void tolower_str(char *s)
{
  char *p;
  for (p=s; *p; p++){
    *p = tolower(*p);
  }
}

適当に書いたわりには動いてしまってびっくり。Windows APIは面白いなあ。思ったんだけど、これって簡易psとして使えるよなあ。PIDがウインドウハンドルで。killしたいときはウインドウハンドルにWM_CLOSE(だっけ?)とか送りつければいいし。Unix系(POSIX?)のシグナルよりもいろんなことができるので、面白い。
いよいよ次回は http://www.yoshibaworks.com/ayacy/inasoft/lv2csv/lvcsvcom/chapter3.html に書かれている、他のプロセスのメモリーを読み書きする処理にいくよ!これを見ると、さすがにWindowsすげーって思わざるを得ないね。そんなことできるのかよ!って。