※ 修正版をこちらのページにアップしています。将来的にはこのページは削除される可能性があります。
さて、前回表示したファイル一覧を見ると気づくことが2つ(以上?)あります。
フォルダとして、「・」と「・・」があります。これは、どういった意味なのでしょうか?
表示上はディレクトリですが、実際には、そんな名前のフォルダはありません。
「・」は、「現在のディレクトリ」つまり、カレントディレクトリです。
「・・」は、「親ディレクトリ」つまり、カレントディレクトリです。
これは、
FindFirstFile()
の指定時に、ワイルドカードを、"*.*"のように指定して全てのファイルを検索すると、「現在のディレクトリ」と「親ディレクトリ」まで検索対象となるからなのでした。
でも、通常エクスプローラでフォルダ一覧を行うと出てきませんので取りたいと思います。
では、どのように取るか考えてみましょう。
@ いったん、リストビュウに作ってしまってから、「・」、「・・」の名前のフォルダを削除する。
A「・」、「・・」の名前だったら、そもそもリストビュウにアイテムを作成しない。
どちらでも、かまいませんが選択肢はたくさん用意しておいたほうがいいでしょう。
そうしないと、その方法で機能が実現できないときに行き詰ってしまいます。
私が思うに、こういったところから泥沼にはまってプログラムがいやになる人が多いようです。
できそうになかったらスパッとあきらめて別な方法を考える。これも大事な鉄則です。
さて、今回はAの方法で実現しようかなと思っています。
そのためには、loop 文の中での continue について知っておく必要があります。
continue はループのブロック終了の }の直前に処理をスキップします。その後ループの先頭に戻ります。
do { // ルートディレクトリ・カレントディレクトリは無視 if (strcmp(WinFindData.cFileName, "." ) == 0 || strcmp(WinFindData.cFileName, "..") == 0) { continue; } // 1項目(名前) // 2項目(サイズ) // 3項目(更新日時) nIndex++; } while (FindNextFile(hFind, &Win32FindData));
こういった、場合には nIndex++; のあとの } に処理がスキップされます。nIndex は、カウントアップされません。
そして、その後の while (FindNextFile(hFind, &Win32FindData)); が評価されます。
まさに、このためにあるようなロジックですね。
さて、2つ目は、表示されたデータが単純にアルファベット順に並んでいます。
通常エクスプローラでフォルダ一覧を行うとフォルダが、先に来てその後にファイルがアルファベット順に並んでいます。
これを行うには、どうしたらいいのでしょうか?
これも複数の方法を考えて見ましょう。
@いったん、リストビュウに作ってしまってから、フォルダ(アルファベット順)、ファイル(アルファベット順)に並べなおす。
Aリストビュウにアイテムを作成するときに、フォルダ(アルファベット順)、ファイル(アルファベット順)になるように、アイテムの iItem にセットする値を変える。
今回は、コールバックの比較関数を使う練習をしようと思いますので、@の方法で実現しようと思います。
メインウィンドウのメッセージ処理関数もコールバック関数でしたが、そもそもコールバック関数とは何でしょうか?
ウインドウプロシージャのときもそうでしたが、プログラム中で、直接、このコールバック関数を呼ぶようなことはしません。
つまり、ある状況が発生したときに、システムこの場合は、OS(この場合はWindows)が指定されたプログラムの中の関数を呼び出してくれるものなのです。
ある状況とは、どういう状況なのでしょうか?
それは、呼び出し先で何らかのイベントが発生した場合です。
この何らかのイベントを、プログラムの中ですべて把握するというのは、困難です。
そこで、イベントが発生したことを判断できるシステム、この場合はOS(この場合はWindows)に、あるイベントが発生したときはこの関数を
呼んでくださいとあらかじめ指定しておくのがコールバック関数なのです。
そうしておくと、リストビュウのソートを行った場合、システムから「aと b があるんですが、これはどちらが小さいんでしょうか(ソート条件)」とかいう連絡がコールバック関数に来ますので、コールバック関数では、「a と b なら a の方が小さいってことにしてください」と答えるわけです。
まずは、コールバック関数を書きましょう。
/* -------------------------------------------------------------------------- */ /* CALLBACK LVWCmpProc */ /* PARAM LPARAM lParam1 : 比較される 2 つのアイテム1 */ /* LPARAM lParam2 : 比較される 2 つのアイテム2 */ /* LPARAM lParamSort : アプリケーション定義(任意)*/ /* RETURN -1 : lParam1 のアイテムが lParam2 のアイテムより前 */ /* 0 : lParam1 のアイテムが lParam2 のアイテムと等しい */ /* 1 : lParam1 のアイテムが lParam2 のアイテムより後 */ /* lParamSortにはポインタ指定で出力先の指定が可能 */ /* -------------------------------------------------------------------------- */ static int CALLBACK LVWCmpProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { /* 変数宣言部 */ HWND hCtlLView; char szItemText1[_MAX_PATH]; char szItemText2[_MAX_PATH]; char szAttribute1[256]; char szAttribute2[256]; /* 関数本体部 */ hCtlLView = (HWND) lParamSort; // パラム1情報取得 ListView_GetItemText(hCtlLView, lParam1, 0, szItemText1, sizeof(szItemText1)); ListView_GetItemText(hCtlLView, lParam1, 1, szAttribute1, sizeof(szAttribute1)); // パラム2情報取得 ListView_GetItemText(hCtlLView, lParam2, 0, szItemText2, sizeof(szItemText2)); ListView_GetItemText(hCtlLView, lParam2, 1, szAttribute2, sizeof(szAttribute2)); if (strcmp(szAttribute1, "<DIR>") == 0 && strcmp(szAttribute2, "<DIR>") == 0) { return lstrcmpi(szItemText1, szItemText2); } else if (lstrcmpi(szAttribute1, "<DIR>") == 0) { return -1; } else if (lstrcmpi(szAttribute2, "<DIR>") == 0) { return 1; } else { return lstrcmpi(szItemText1, szItemText2); } }
C言語は、文字列はcharの配列でしか扱えないのでした。
ですから、szAttribute1 = "<DIR>" とは書けません。
そこで、
strcmp()
関数を使って比較しましょう。同じ場合には、0 が返ります。
Param1はリストビュウにおいて比較する第1項目のアイテムのインデックスであり、lParam2は第2項目のアイテムのインデックスです。
関数の結果として、-1 は、lParam1 のアイテムが lParam2 のアイテムより前、1 の場合は、lParam1 のアイテムが lParam2 のアイテムより後を返さないといけません。
そこで、以下のようにすればいいことがわかります。
※すみません、言いすぎました。解らなくても問題ありません。
@ szItemText1, szItemText2 の両方ともディレクトリだったら、その名前を比較する。
A szItemText1 だけがディレクトリの場合は無条件に、szItemText1
B szItemText2 だけがディレクトリの場合は無条件に、szItemText2
C szItemText1, szItemText2 の両方ともディレクトリだったら、その名前を比較する。
2つの文字列を比較するには、 lstrcmpi() を使います。
int lstrcmpi( LPCTSTR lpString1, // 最初の文字列 LPCTSTR lpString2 // 2番目の文字列 );
戻り値は、lpString1 パラメータが指す文字列が、lpString2 パラメータが指す文字列よりも小さい場合、-1、その逆の場合、1が返ってくるので、そのまま使えそうです。
なお等しい場合は、0 が返ります。
※ なお、lstrcmpi() 関数は Win32API の関数です。標準関数では、strcmp() という関数があります。
どちらを使っても問題ないと思います。
その次に、システムに対して、「リストビュウをソートして比較する必要がある場合、この関数を呼び出してください」と登録する必要があります。
そのためには、リストビュウに対して LVM_SORTITEMSEX メッセージを送ってやる必要があります。
送出するには、 やっぱり SendMessage() を使います。
LVM_SORTITEMSEX wParam = (WPARAM) (LPARAM) lParamSort // コールバック関数に渡される任意のソート情報 lParam = (LPARAM) (PFNLVCOMPARE) pfnCompare // 比較を行うコールバック関数のポインタ
これは、ちょっと解説の必要がありますね。
まず、wParam を見てください。 コールバック関数に渡される任意のソート情報って何でしょう?
通常、我々が関数を自作して自分のプログラムから呼び出す場合、引数は自分の好き勝手に設定できます。
ですが、コールバック関数の場合、システムがあらかじめ定義した引数になってしまいます。
リストビュウの比較のコールバック関数の場合、3つしかありません。
そのうち1つ目と2つめは、システムが使用するために使われています。
でも比較関数に持って行きたい自分が使うためのの引数もあるはずです。
こんなときに、wParam を使うとそれが、3番目の引数として比較関数に渡されるというものなのです。
でも、引数がひとつだけじゃなくて、何個もあるという場合もあるでしょう。そんなときは?
そう、構造体のメンバーにしてください。なるほど、考え付いた人は偉いですね。
もうひとつ、lParam は、関数のポインタ指定しなければいけませんが、気にすることはありません。
関数の名前を書けばそれでいいのです。
なぜなら、式の中では関数は、「関数のポインタ」に自動的に読み替えられるからです。
したがって "&" とかを付けてはいけません。
リストビュウを作ってしまって(さらにデータを表示してから)から、ソートを行うわけですから、LVM_SORTITEMSEX メッセージ はリストビュウの作成後に送出します。
そこで、ウインドウプロシージャを以下のように修正します。
※ 必要部分のみ抜粋です。
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* 変数宣言部 */
HINSTANCE hInstance;
HWND hCtlListView;
HANDLE hFind;
WIN32_FIND_DATA WinFindData;
SYSTEMTIME SystemTime;
FILETIME FileTime;
LVITEM LVItem;
LV_COLUMN LVColumn[] =
{
{LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_LEFT, 150, "名前", 0, 0},
{LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_RIGHT, 75, "サイズ", 0, 1},
{LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_LEFT, 110, "更新日時", 0, 2}
};
int nIndex = 0;
int i;
INITCOMMONCONTROLSEX ICC;
/* 関数本体部 */
switch (uMsg)
{
case WM_CREATE:
hInstance = (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE);
// コモンコントロール初期化
ICC.dwSize = sizeof(INITCOMMONCONTROLSEX);
ICC.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx(&ICC);
// リストビュウ作成
hCtlListView = CreateWindowEx(WS_EX_CLIENTEDGE,
WC_LISTVIEW,
NULL,
WS_CHILD | WS_VISIBLE | LVS_REPORT,
10,
30,
380,
320,
hWnd,
0,
hInstance,
NULL);
for (i = 0; i < (sizeof(LVColumn) / sizeof(LV_COLUMN)); i++)
{
ListView_InsertColumn(hCtlListView, i, &LVColumn[i]);
}
if ((hFind = FindFirstFile("*.*", &WinFindData)) == INVALID_HANDLE_VALUE)
{
if (GetLastError() != ERROR_NO_MORE_FILES)
{
MessageBox(NULL, "ファイルの検索に失敗しました", "ERR", MB_OK);
break;
}
}
do
{
// ルートディレクトリ・カレントディレクトリは無視
if (strcmp(WinFindData.cFileName, "." ) == 0 ||
strcmp(WinFindData.cFileName, "..") == 0)
{
continue;
}
// 1項目(名前)
LVItem.mask = LVIF_TEXT;
LVItem.iItem = nIndex;
LVItem.iSubItem = 0;
LVItem.pszText = WinFindData.cFileName;
ListView_InsertItem(hCtlListView, &LVItem);
// 2項目(サイズ)
LVItem.iSubItem = 1;
if (WinFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
wsprintf(LVItem.pszText, "%s", "<DIR>");
}
else
{
wsprintf(LVItem.pszText, "%d", WinFindData.nFileSizeHigh * MAXDWORD + WinFindData.nFileSizeLow);
}
ListView_SetItem(hCtlListView, &LVItem);
// 3項目(更新日時)
LVItem.iSubItem = 2;
// 協定世界時(UTC)を地域標準時に変更
FileTimeToLocalFileTime(&WinFindData.ftLastWriteTime, &FileTime);
FileTimeToSystemTime(&FileTime, &SystemTime);
wsprintf(LVItem.pszText, "%d/%02d/%02d %d:%02d", SystemTime.wYear, SystemTime.wMonth,
SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute);
ListView_SetItem(hCtlListView, &LVItem);
nIndex++;
}
while (FindNextFile(hFind, &WinFindData));
FindClose(hFind);
// ソート
ListView_SortItemsEx(hCtlListView, LVWCmpProc, hCtlListView);
break;
}
return 0L;
}
いつものごとく、LVM_SORTITEMSEX メッセージ の送出は、マクロを使用しています。
そして、3番目の項目にリストビュウのハンドルを指定することにより、コールバック関数で使えるようにしています。

さて、ソート処理自体はコールバック関数に任せてしまいましたが、実際はどのようになっているのでしょうか?
デバッグしながらどのようにコールバック関数が呼ばれるか調べてみました。
以下がその結果です。
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ||
| @ | Debug | ○ | ○ | ○ | ||||||||||||||||
| A | FTPManager.ncb | ● | ○ | ○ | ○ | |||||||||||||||
| B | FTPManager.rc | ○ | ● | ● | ○ | |||||||||||||||
| C | FTPManager.suo | ● | ○ | |||||||||||||||||
| D | FTPManager.sln | ○ | ● | ○ | ||||||||||||||||
| E | FTPManager.vcproj | ○ | ○ | ○ | ● | ● | ● | ● | ||||||||||||
| F | FTPManager.….user | ○ | ● | ○ | ||||||||||||||||
| G | lvwListItem.bmp | ● | ○ | |||||||||||||||||
| H | Release | ○ | ● | ● | ● | |||||||||||||||
| I | Resource.h | ○ | ● | ● | ● | ● | ||||||||||||||
| J | WinMain.cpp | ● |
| @ | D | B | @ | @ | F | E | I | H | H | H | H | H | @ | @ | @ | @ | @ | @ | |
| A | C | D | B | A | G | F | J | I | E | E | E | E | H | H | H | H | H | H | |
| C | D | B | G | J | I | F | F | A | A | A | A | A | |||||||
| C | D | J | I | G | E | B | B | B | |||||||||||
| C | J | I | F | E | D | D | |||||||||||||
| J | G | F | E | C | |||||||||||||||
| I | G | F | E | ||||||||||||||||
| J | I | G | F | ||||||||||||||||
| J | I | G | |||||||||||||||||
| J | I | ||||||||||||||||||
| J | |||||||||||||||||||
ソートには、いろいろな種類がありますが、この場合は、マージソートというソート方法がとられているようです。
最後に今回の「WinMain.cpp」の全文を載せておきます。
ここで、気をつけてほしいのは、LVWCmpProc() 関数は、WndProc() 関数の前に書くことです。
これは、コンパイルするときコンパイラが上から順にコンパイルするからです。
WndProc() 関数でソートを行おうとするときに LVWCmpProc のポインタを指定しますが、このときに LVWCmpProc() 関数が何者かを明らかにしておく必要があります。
ためしに、WndProc() 関数より後に LVWCmpProc() 関数を書いてしまうと、コンパイルエラーになります。
/* ========================================================================== */ /* FILE WinMain.cpp */ /* CREATE 2006/10/01 */ /* MODIFY 2007/02/01 */ /* REMARK */ /* COPYRIGHT 2000-2007, Latin. All Rights Reserved */ /* ========================================================================== */ #include <Windows.h> #include <WindowsX.h> #include <CommCtrl.h> #include "Resource.h" /* -------------------------------------------------------------------------- */ /* CALLBACK LVWCmpProc */ /* PARAM LPARAM lParam1 : 比較される 2 つのアイテム1 */ /* LPARAM lParam2 : 比較される 2 つのアイテム2 */ /* LPARAM lParamSort : アプリケーション定義(任意)*/ /* RETURN -1 : lParam1 のアイテムが lParam2 のアイテムより前 */ /* 0 : lParam1 のアイテムが lParam2 のアイテムと等しい */ /* 1 : lParam1 のアイテムが lParam2 のアイテムより後 */ /* lParamSortにはポインタ指定で出力先の指定が可能 */ /* -------------------------------------------------------------------------- */ static int CALLBACK LVWCmpProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { /* 変数宣言部 */ HWND hCtlLView; char szItemText1[_MAX_PATH]; char szItemText2[_MAX_PATH]; char szAttribute1[256]; char szAttribute2[256]; /* 関数本体部 */ hCtlLView = (HWND) lParamSort; // パラム1情報取得 ListView_GetItemText(hCtlLView, lParam1, 0, szItemText1, sizeof(szItemText1)); ListView_GetItemText(hCtlLView, lParam1, 1, szAttribute1, sizeof(szAttribute1)); // パラム2情報取得 ListView_GetItemText(hCtlLView, lParam2, 0, szItemText2, sizeof(szItemText2)); ListView_GetItemText(hCtlLView, lParam2, 1, szAttribute2, sizeof(szAttribute2)); if (strcmp(szAttribute1, "<DIR>") == 0 && strcmp(szAttribute2, "<DIR>") == 0) { return lstrcmpi(szItemText1, szItemText2); } else if (lstrcmpi(szAttribute1, "<DIR>") == 0) { return -1; } else if (lstrcmpi(szAttribute2, "<DIR>") == 0) { return 1; } else { return lstrcmpi(szItemText1, szItemText2); } } /* -------------------------------------------------------------------------- */ /* CALLBACK WndProc */ /* PARAM HWND hWnd : ウィンドウハンドル */ /* UINT uMsg : メッセージ */ /* WPARAM wParam : メッセージの追加情報 */ /* LPARAM lParam : メッセージの追加情報 */ /* RETURN LRESULT */ /* メッセージ処理の結果 */ /* REMARK */ /* -------------------------------------------------------------------------- */ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { /* 変数宣言部 */ HINSTANCE hInstance; HWND hCtlCombo; HWND hCtlListView; HANDLE hFind; WIN32_FIND_DATA WinFindData; SYSTEMTIME SystemTime; FILETIME FileTime; LVITEM LVItem; LV_COLUMN LVColumn[] = { {LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_LEFT, 150, "名前", 0, 0}, {LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_RIGHT, 75, "サイズ", 0, 1}, {LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM, LVCFMT_LEFT, 110, "更新日時", 0, 2} }; char szCurDir[MAX_PATH + 1]; char szDrives[128]; int nIndex = 0; int i; INITCOMMONCONTROLSEX ICC; /* 関数本体部 */ switch (uMsg) { case WM_COMMAND: switch (LOWORD(wParam)) { case IDM_EXIT: SendMessage(hWnd, WM_CLOSE, 0, 0L); break; default: return (DefWindowProc(hWnd, uMsg, wParam, lParam)); } break; case WM_CREATE: hInstance = (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE); hCtlCombo = CreateWindow("COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | CBS_SORT | CBS_DROPDOWN | CBS_AUTOHSCROLL, 10, 5, 450, 100, hWnd, 0, hInstance, NULL); SendMessage(hCtlCombo, WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(true , 0)); GetCurrentDirectory(MAX_PATH + 1, szCurDir); ComboBox_SetText(hCtlCombo, szCurDir); ComboBox_AddString(hCtlCombo, szCurDir); GetLogicalDriveStrings(sizeof(szDrives), szDrives); i = 0; while (szDrives[i] != '\0') { ComboBox_AddString(hCtlCombo, &szDrives[i]); // 次のドライブ名へ while (szDrives[i] != '\0') { i++; } i++; } // コモンコントロール初期化 ICC.dwSize = sizeof(INITCOMMONCONTROLSEX); ICC.dwICC = ICC_LISTVIEW_CLASSES; InitCommonControlsEx(&ICC); // リストビュウ作成 hCtlListView = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL, WS_CHILD | WS_VISIBLE | LVS_REPORT, 10, 30, 380, 320, hWnd, 0, hInstance, NULL); for (i = 0; i < (sizeof(LVColumn) / sizeof(LV_COLUMN)); i++) { ListView_InsertColumn(hCtlListView, i, &LVColumn[i]); } if ((hFind = FindFirstFile( "*.*", &WinFindData)) == INVALID_HANDLE_VALUE) { if (GetLastError() != ERROR_NO_MORE_FILES) { MessageBox(NULL, "ファイルの検索に失敗しました", "ERR", MB_OK); break; } } do { // ルートディレクトリ・カレントディレクトリは無視 if (strcmp(WinFindData.cFileName, "." ) == 0 || strcmp(WinFindData.cFileName, "..") == 0) { continue; } // 1項目(名前) LVItem.mask = LVIF_TEXT; LVItem.iItem = nIndex; LVItem.iSubItem = 0; LVItem.pszText = WinFindData.cFileName; ListView_InsertItem(hCtlListView, &LVItem); // 2項目(サイズ) LVItem.iSubItem = 1; if (WinFindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { wsprintf(LVItem.pszText, "%s", "<DIR>"); } else { wsprintf(LVItem.pszText, "%d", WinFindData.nFileSizeHigh * MAXDWORD + WinFindData.nFileSizeLow); } ListView_SetItem(hCtlListView, &LVItem); // 3項目(更新日時) LVItem.iSubItem = 2; // 協定世界時(UTC)を地域標準時に変更 FileTimeToLocalFileTime(&WinFindData.ftLastWriteTime, &FileTime); FileTimeToSystemTime(&FileTime, &SystemTime); wsprintf(LVItem.pszText, "%d/%02d/%02d %d:%02d", SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour, SystemTime.wMinute); ListView_SetItem(hCtlListView, &LVItem); nIndex++; } while (FindNextFile(hFind, &WinFindData)); FindClose(hFind); // ソート ListView_SortItemsEx(hCtlListView, LVWCmpProc, hCtlListView); break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, uMsg, wParam, lParam)); } return 0L; } /* -------------------------------------------------------------------------- */ /* WINAPI WinMain */ /* PARAM HINSTANCE hInstance : 現在のインスタンス */ /* HINSTANCE hPrevInst : 直前のインスタンス */ /* LPSTR lpCmdLine : コマンドライン引数 */ /* int nCmdShow : ウインドウの表示形式 */ /* RETURN int : */ /* メッセージの wParam パラメータ */ /* REMARK Win32 アプリケーションの初期エントリポイント */ /* Windows システムが呼び出す。 */ /* -------------------------------------------------------------------------- */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { /* 変数宣言部 */ HWND hWnd; WNDCLASSEX WndClass; MSG Msg; /* 関数本体部 */ // ウインドウクラス作成 WndClass.cbSize = sizeof(WndClass); WndClass.style = CS_HREDRAW | CS_VREDRAW; WndClass.lpfnWndProc = WndProc; WndClass.cbClsExtra = 0; WndClass.cbWndExtra = 0; WndClass.hInstance = hInstance; WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); WndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); WndClass.hCursor = LoadCursor(NULL, IDC_ARROW); WndClass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); WndClass.lpszMenuName = MAKEINTRESOURCE(IDM_MENUMAIN); WndClass.lpszClassName = "FTPManager"; // ウィンドウクラス登録 if (! RegisterClassEx(&WndClass)) { return FALSE; } // ウィンドウ作成 hWnd = CreateWindow(WndClass.lpszClassName, // 登録されているクラス名 "FTPManager", // ウィンドウ名 WS_OVERLAPPEDWINDOW, // ウィンドウスタイル CW_USEDEFAULT, // ウィンドウの横方向の位置 CW_USEDEFAULT, // ウィンドウの縦方向の位置 CW_USEDEFAULT, // ウィンドウの幅 CW_USEDEFAULT, // ウィンドウの高さ NULL, // 親ウィンドウまたはオーナーウィンドウのハンドル NULL, // メニューハンドルまたは子ウィンドウ ID hInstance, // アプリケーションインスタンスのハンドル NULL); // ウィンドウ作成データ if (! hWnd) { return FALSE; } // ウィンドウの表示 ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); // メッセージループ while (GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return (int) Msg.wParam; }
本日のソース: FTPManager107(VC8).zip FTPManager107(BCC).zip
アルゴリズムとは、ある問題を解くための解法ですね。
将棋とか碁では、定石というのがこれに似ています。
古来、多くの数学者が問題を解くために使用した方法が、いまでは、我々が簡単に使うことができます。
ですので、こういったアルゴリズムを使うとき、先人の苦労に思いを馳せるのも一興だと思います。
私は、数学の勉強が苦手でしたが、先人の一生の研究がわずかな時間で手に入れられるのですから、ある意味お得な教科かも知れません。
プログラムは、理系のやることだと思われている方が多いと思いますが、そんなことは、ありません。
美しいプログラム(エレガントなプログラム)を書くのに必要なのは、センスです。
センスは、一日に身につくものではありません。日々の精進が必要なのです。