Version 14.26
ファイル検索アプリの準備
「前回はファイル検索アプリの難しさを説明しました」
『ワーカースレッドってゆーのを使ってやるにしても、面倒なんだよね』
「そう、たとえば」
・アプリを実行するとダイアログが表示される。
・フォルダを選択して【実行】ボタンを押すと検索が開始される。
・検索開始と同時にキャンセル用ダイアログが表示される。
・キャンセル用ダイアログには検索中のファイルが表示される。
・キャンセルボタンを押すと検索がキャンセルされてダイアログが閉じる。
・検索が終了してもキャンセルダイアログが閉じる。
・検索で見つかったファイルの数を表示する。
『数? ファイルのリストとかじゃ……』
「今回はマルチスレッドを勉強するのが目的だから、そういった細かいとこ
ろはパス。リストボックスに出力する方法とかは、これまで教えてきた内容
でできると思うから」
『確かに Version 5.02 ( No.067 ) で教わってるね』
「今回教えるとこは複雑で難しいところだから、シンプルに行きます。とい
うわけでまず、 MFC を使って基本となるダイアログアプリを作りましょう」
『ほーい。作り方はいつも通り?』
「うん。【ファイル】−【新規作成】で【プロジェクト】ページの
【MFC AppWizard (exe)】を選んで、プロジェクト名は……」
『 SearchDlg とか』
「じゃ、それで。あとは、ステップ1で【ダイアログベース】を選んで、あ
とはそのまま【終了】でOK」
『ほいいつものパターンね』
「次に、キャンセル用ダイアログを作ります」
『え、そっち先に作るの?』
「その方がわかりやすいから。ワークスペースの【ResourceView】の
【Dialog】で右クリックして【Dialog の挿入】を選んで」
『ほい、ダイアログ追加されたよ』
「このダイアログ上で右クリック、【プロパティ】で【キャプション】と
【ID】を好きなのに変更して」
『じゃ、【検索中】【IDD_SEARCHING】 で』
「で、このダイアログには初めから【キャンセル】ボタンが付いているの
で、キャンセルにはこれを使います」
『おお、便利』
「というわけで邪魔な【OK】ボタンは削除しておいて」
『ほい。選択して DEL っと』
「次に、このダイアログを管理するためのクラスを作ります」
『なんかクラスって懐かしい……』
「ちゃんとした MFC アプリ作るのが久しぶりかも……。このダイアログが
ダイアログエディタで表示されている状態で、メニューの【表示】−
【ClassWizard】を選んで」
『【クラスの追加】ってダイアログが出た』
「【新規クラスの作成】を選んで【OK】ボタン押して、【クラス名】を
【CSearchingDlg】にして【OK】ボタン押して」
『ほい』
「【MFC ClassWizard】ってダイアログが出てるだろうからこれも【OK】
ボタン押して閉じて」
『ほい』
「これで、【検索中】ダイアログの IDD_SEARCHING と、それを操作するた
めの CSearchingDlg クラスができました」
『おー』
「では次に、今作ったダイアログを表示するようにしてみます。ダイアログ
エディタで最初の方のダイアログ――多分 IDD_SEARCHDLG_DIALOG っていう
ID だと思うけど、そっちを表示して」
『ほい。【ResourceView】で IDD_SEARCHDLG_DIALOG をダブルクリックして
と』
「【TODO: ダイアログのコントロールをここに配置】を削除して、代わりに
ボタンをひとつ貼り付けて」
『 ID とキャプションは?』
「【IDC_SEARCH_START】、【検索開始(&S)】で。作ったら、ボタンを右クリ
ックして、メニューの【イベント...】を選んで」
『いつものイベントハンドラねー』
「そう。左側が【BN_CLICKED】、右下が【IDC_SEARCH_START】の状態で、
【追加と編集】ボタンを押して」
『【メンバ関数の追加】ってダイアログが出て【OnSearchStart】って表示
されてる』
「そのまま【OK】を押して」
『イベントハンドラできたよ』
void CSearchDlgDlg::OnSearchStart()
{
// TODO: この位置に(以下略)
}
「ここに、さっきの【検索中】ダイアログを表示するコードを追加しま
す。まず、この関数のファイル SearchDlgDlg.cpp の上の方に以下のコード
を追加してください」
#include "stdafx.h"
#include "SearchDlg.h"
#include "SearchDlgDlg.h"
// ↓この下の行を追加。
#include "SearchingDlg.h"
// ↑この上の行を追加。
『 SearchingDlg.h を include するってことね』
「 #include については Version 2.12 ( No.023 ) を参考に。【検索中】
ダイアログを使うってことは CSearchingDlg を使うってことだから、その
ヘッダーファイル SearchingDlg.h を #include する、ってところかな」
『う”、でもちょっとわかんないかも……』
「それは少し先でしっかり説明するから」
『う”、それも嫌な予感……』
「では、最後にダイアログを表示するコードを追加します。先ほどのイベン
トハンドラを次のように修正してください」
void CSearchDlgDlg::OnSearchStart()
{
// 【検索中】ダイアログを表示します。
CSearchingDlg cDlg;
cDlg.DoModal();
}
『あ、そっか、ダイアログだから DoModal() で表示できるんだね』
「 Version 3.3 ( No.028 ) のだね。このあたりは普通のダイアログと同
じ。とりあえず試してみて」
『ビルドして実行、【検索開始】ボタンっと。うん、【検索中】ダイアログ
が出た!』
「キャンセルボタンを押すと閉じるよね」
『うん、閉じた。これに機能足していけばいいんだよね』
「そういうこと。次に、マルチスレッドを使わない検索機能を追加します」
『え? それじゃ意味ないじゃん』
「そうなんだけど、実際に作るときにはこういうふうに作った方がいいか
ら」
『どゆこと?』
「火美ちゃんはファイルの検索機能って作ったことある?」
『ない……』
「マルチスレッドも本格的に使うのは初めてだから、両方とも初めてってこ
とになるでしょ。そういうのはうまく動かない時に〈どちらが原因か〉って
いうのがわかりにくいから」
『いっぺんに作らないで別々に作ってく、ってこと?』
「そういうこと。というわけで、さっきのイベントハンドラの前に
CountMatchFile() っていう関数を追加して、この中で検索してみます」
int CountMatchFile
( const char * const p_pchFolderPath
, const char * const p_pchFileName
)
{
int iNum = 0;
// 検索するフォルダをカレントディレクトリにします。
SetCurrentDirectory( p_pchFolderPath );
// 最初のファイルを取得します。
WIN32_FIND_DATA stWin32FindData;
HANDLE hHandle = NULL;
hHandle = FindFirstFile( "*.*", &stWin32FindData );
do
{
TRACE( "File: %s\n", stWin32FindData.cFileName );
// 一致チェックをします。
if( _stricmp( stWin32FindData.cFileName, p_pchFileName ) == 0 )
{
// 一致しました。
++iNum;
}
// 次のファイルを取得します。
}while( FindNextFile( hHandle, &stWin32FindData ) );
// ハンドルを閉じます。
FindClose( hHandle );
return iNum;
}
void CSearchDlgDlg::OnSearchStart()
{
int iCount = 0;
iCount = CountMatchFile( "C:\\WINNT", "NOTEPAD.EXE" );
TRACE( "一致数: %d\n", iCount );
// 【検索中】ダイアログを表示します。
// CSearchingDlg cDlg;
// cDlg.DoModal();
}
『うわ、なんか見たことない関数がいっぱいある!』
「ファイル検索で使うのは4つの API 」
// 検索するフォルダをカレントディレクトリにします。
SetCurrentDirectory( p_pchFolderPath );
「まずこの SetCurrentDirectory() で、検索するフォルダを
【カレントディレクトリ】にします」
『カレントディレクトリ……? 聞いたことあるような……』
「うーん、ウィンドウズだとあまり使わないからね……まぁ、これから操作
をするフォルダをこの API で変えてるってこと。 "C:\\WINNT" みたいに
フォルダのフルパスを渡せばOK」
『うん』
「次はファイルの取得」
// 最初のファイルを取得します。
WIN32_FIND_DATA stWin32FindData;
HANDLE hHandle = NULL;
hHandle = FindFirstFile( "*.*", &stWin32FindData );
「 FindFirstFile() は、さっきカレントディレクトリに指定したフォルダ
からファイルひとつ取得します」
『ひとつだけ?』
「そう、ひとつだけ。取得したファイルの情報は、第2引数で渡した
stWin32FindData に格納されます」
『あ、だからポインタなんだ』
「第1引数の "*.*" は、ファイルのワイルドカード」
『あ、これ知ってるよ。エクスプローラーで検索するときもよく使うもん、
*.txt みたいに』
「そう、それと同じ。この例だと〈拡張子の付いたファイル全部〉になりま
す。でもこの API はファイルひとつだけ。残りは FindNextFile() で取得
します」
}while( FindNextFile( hHandle, &stWin32FindData ) );
「 FindNextFile() は、残りのファイルをひとつずつ取得します」
『ひとつずつ?』
「そう、呼ぶたびにひとつずつ、第2引数に格納します。全部取得してもう
取得するファイルがないと 0 を返してきます」
『あー、だから do while なんだ』
「最後にハンドルを閉じます」
// ハンドルを閉じます。
FindClose( hHandle );
『これは他と同じねー』
「というわけで次回に続く!」
/*
Preview Next Story!
*/
『ファイル検索のとこ、なんか早すぎない?』
「うん、だから次回ちゃーんと説明します」
『あ、なんだ』
「それに、これ、このままじゃ使い道ほとんどないし」
『そうなの?』
「だって1フォルダしか検索しないから、ファイル1個しかないでしょ」
『あー……個数数える意味ほとんどないね』
「というわけで次回」
< Version 14.27 フォルダを潜るファイル検索 >
『につづく!』
「でも次回は難しいよー?」
『そういうのもう信用しない』
「う”……」