- 締切済み
ディレクトリツリービューを有するWin32SDKアプリケーション
左にエクスプローラ風のフォルダツリービューのペインを有し、ファイルをクリックすると右ペインで表示編集などの処理をする、ごく一般的な2ペインアプリケーションをMSVC++6.0 Win32SDKで作成すべく、インターネットで検索しましたが、MFCやVBの例はあるもののWin32SDKのサンプルコードを見つけることが出来ませんでした。 コモンコントロールでエクスプローラ風のフォルダツリービューのコードを自作するとなると極端な労力を要します。 そこで"Win32SDKプログラム"のフォルダツリービューの適当なサンプルコードの所在をご存知でしたらよろしくお願いします。
- みんなの回答 (3)
- 専門家の回答
みんなの回答
- machongola
- ベストアンサー率60% (434/720)
こんばんは。取り敢えず以下をヒントに作成して見ました。 http://www.sm.rim.or.jp/~shishido/subdir.html どちらにしろ、ハードディスク内を全検索する訳には行きませんので、最初は検索対象ドライブと+2階層まで、その後はツリーがクリックされる度に+2階層までを調べながら、こっそりとツリーを付け加えていきます(でないと、動かなくなる)。 後は、ツリービューに+マーク等の表示をさせるようにして下さい。以下参考程度に。 #include<windows.h> #include<commctrl.h> #include<tchar.h> #include<crtdbg.h> #include"resource.h" typedef struct { HTREEITEM hTreeItem;//ツリーアイテム union { LPVOID pVoid;//キャスト防ぎ LPTSTR sPath;//ツリーアイテムのパス文字列 }; } DATA, *LPDATA; typedef struct { union { LPVOID pVoid;//キャスト防ぎ LPDATA pData;//DATA型の配列 }; INT nCount;//割り当てられた配列の個数 } ITEMPATHTABLE, *LPITEMPATHTABLE; static ITEMPATHTABLE _S_pathTable = {0}; //パステーブルのメモリ解放 static VOID TreeView_FreePathTable(LPITEMPATHTABLE tbl) { const ITEMPATHTABLE zero = {0}; for(INT i = 0; i < tbl->nCount; ++i) ::free(tbl->pData[i].sPath); ::free(tbl->pData); *tbl = zero; } //パス文字列でパステーブルを検索して対応するツリーアイテムのハンドルを見つける static HTREEITEM TreeView_FindItemFromPath(LPITEMPATHTABLE tbl, LPCTSTR sPath) { for(INT i = 0; i < tbl->nCount; ++i) if(::_tcscmp(tbl->pData[i].sPath, sPath) == 0) return tbl->pData[i].hTreeItem; return NULL; } //上の逆 static LPCTSTR TreeView_FindPathFromItem(LPITEMPATHTABLE tbl, HTREEITEM hTreeItem) { for(INT i = 0; i < tbl->nCount; ++i) if(tbl->pData[i].hTreeItem == hTreeItem) return tbl->pData[i].sPath; return NULL; } //検索されたパスの文字列をテーブルに記録していく static BOOL TreeView_SavePath(LPITEMPATHTABLE tbl, LPCTSTR sPath, HTREEITEM hTreeItem) { if(tbl->nCount == 0) tbl->pVoid = ::malloc(sizeof(DATA) * (++tbl->nCount)); else tbl->pVoid = ::realloc(tbl->pVoid, sizeof(DATA) * (++tbl->nCount)); LPDATA pData = &tbl->pData[tbl->nCount - 1]; pData->hTreeItem = hTreeItem; pData->pVoid = ::malloc((::_tcslen(sPath) + 1) * sizeof(TCHAR)); ::_tcscpy(pData->sPath, sPath); return TRUE; } //ツリーアイテムの作成 static HTREEITEM AddTreeItem(HWND hTreeView, LPCTSTR sItemName, HTREEITEM hItemParent = NULL) { //親ツリーがNULLの場合はルートになる TVINSERTSTRUCT tvis = {hItemParent == NULL ? TVI_ROOT : hItemParent, TVI_LAST}; tvis.item.mask = TVIF_TEXT | TVIF_PARAM; tvis.item.pszText = (LPTSTR)sItemName; tvis.item.cchTextMax = ::_tcslen(sItemName); tvis.item.lParam = TRUE;//何か適当に return TreeView_InsertItem(hTreeView, &tvis); } //ディレクトリであるかどうかの確認 static BOOL IsDirectory(const WIN32_FIND_DATA& wfd) { //違う if(::_tcscmp(wfd.cFileName, TEXT(".")) == 0) return FALSE; //違う if(::_tcscmp(wfd.cFileName, TEXT("..")) == 0) return FALSE; //FILE_ATTRIBUTE_DIRECTORYフラグがあればディレクトリ return (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); } //ツリービューにディレクトリを追加する static VOID AddDirectory(HWND hTreeView, LPTSTR sSearchDirectory, LPCTSTR sOption, HTREEITEM hItemParent = NULL, INT level = 0) { //再帰は2段階まで if(level == 2) return; //末尾に\が無ければ付け加える if(sSearchDirectory[::_tcslen(sSearchDirectory) - 1] != TCHAR('\\')) ::_tcscat(sSearchDirectory, TEXT("\\")); //フルパスと検索コマンドを足した文字を作成する TCHAR sCmdPath[MAX_PATH]; ::_tcscpy(sCmdPath, sSearchDirectory); ::_tcscat(sCmdPath, sOption); //win32に渡して検索ハンドルを開く WIN32_FIND_DATA wfd; HANDLE hSearch = ::FindFirstFile(sCmdPath, &wfd); //しくじった if(hSearch == INVALID_HANDLE_VALUE) { ::FindClose(hSearch); return; } do { //ディレクトリであるかどうか if(::IsDirectory(wfd)) { //検索パスと見つかったディレクトリ文字を足す TCHAR sFullPath[MAX_PATH]; ::_tcscpy(sFullPath, sSearchDirectory); ::_tcscat(sFullPath, wfd.cFileName); //パステーブルからsFullPathと一致する項目を検索する HTREEITEM hTreeItem = ::TreeView_FindItemFromPath(&_S_pathTable, sFullPath); //まだ登録されていない if(hTreeItem == NULL) { //ツリーアイテムを作成する hTreeItem = ::AddTreeItem(hTreeView, wfd.cFileName, hItemParent); //ツリーアイテムとsFullPathをパステーブルに登録する ::TreeView_SavePath(&_S_pathTable, sFullPath, hTreeItem); } //再帰する ::AddDirectory(hTreeView, sFullPath, sOption, hTreeItem, level + 1); } }while(::FindNextFile(hSearch, &wfd)); //検索ハンドルを閉じる ::FindClose(hSearch); } //プロシージャ int CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_INITDIALOG: { //ドライブ監査 LPCTSTR sDrives[] = {TEXT("C:\\"), TEXT("D:\\")}; for(INT i = 0; i < sizeof(sDrives) / sizeof(sDrives[0]); ++i) { HWND hTreeView = ::GetDlgItem(hDlg, IDC_TREE1); //作業用バッファに入れる(再帰で利用されるから) TCHAR sSearchPath[MAX_PATH] = {TCHAR('\0')}; ::_tcscpy(sSearchPath, sDrives[i]); //ドライブのルートツリーアイテム作成 HTREEITEM hTreeItemDrive = ::AddTreeItem(hTreeView, sDrives[i]); //パスとツリーアイテムハンドルを登録 ::TreeView_SavePath(&_S_pathTable, sDrives[i], hTreeItemDrive); //ドライブ内を検索してディレクトリへ侵入する ::AddDirectory(hTreeView, sSearchPath, TEXT("*.*"), hTreeItemDrive); } } break; case WM_VSCROLL: break; case WM_CLOSE: ::DestroyWindow(hDlg); break; case WM_DESTROY: ::TreeView_FreePathTable(&_S_pathTable); ::PostQuitMessage(0); break; case WM_NOTIFY: { //ツリーアイテムの+ボタンがクリックされたら LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam; if(pnmtv->hdr.code == TVN_ITEMEXPANDING) { //アイテムに対応したパス文字列を取り出す LPCTSTR sItemPath = ::TreeView_FindPathFromItem(&_S_pathTable, pnmtv->itemNew.hItem); //作業用バッファに入れる(再帰で利用されるから) TCHAR sSearchPath[MAX_PATH] = {TCHAR('\0')}; ::_tcscpy(sSearchPath, sItemPath); //取り出したパスを使用して検索する ::AddDirectory(pnmtv->hdr.hwndFrom, sSearchPath, TEXT("*.*")); } } break; default: return FALSE; } return TRUE; } //見ての通り int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR lpCmdLine, int nCmdShow) { MSG msg; HWND hWnd = ::CreateDialog(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc); ::ShowWindow(hWnd, nCmdShow); while(GetMessage(&msg, NULL, 0, 0)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } const INT result = ::_CrtDumpMemoryLeaks(); return msg.wParam; }
- zwi
- ベストアンサー率56% (730/1282)
「エクスプローラーを作る」その1~その4(未完成) http://hp.vector.co.jp/authors/VA016117/winsdk.html ぐらいですかね。WC_TREEVIEWで探すと他にもあるかも知れませんが。
お礼
ありがとうございます。これも見ましたがシェルインターフェースを使っていて少し難しいです。 もう少しシンプルにFindFirstFile() ~ FindNextFile() を再帰的に呼び出す方法でディレクトリツリーが作成できないかと考えています。 コモンコントロールでツリービューを作る例は多数あるのですが、ディレクトリツリーを最後まで完成したサンプルはなかなか見当たりません。 外国物でもよいのでわかりやすい例があればよろしくお願いします。
- zwi
- ベストアンサー率56% (730/1282)
C++で良いんでしたら、WTLでの実装例はあります。 http://homepage1.nifty.com/Roy_/Software/WTL/WTL.htm それとも直にWin32APIを使っていないとダメですかね?
お礼
ありがとうございます。少しコードを眺めましたが難しいですね。 当方WTLは使ったことがないのでWTLの実装と勉強から始めなければならず、出来たら直WIN32APIのサンプルコードがあるとありがたいのですが・・・・ Visual C++ 2008 Express Editionでコンパイルできるサンプルコードを探しています。
お礼
詳細なコードをお示し頂きありがとうございます。 ファイル数が多い場合やフォルダの階層が深い場合にやはり極端に起動が遅く動かなくなる問題があるのですね。 再帰によるフォルダーツリーのコードが無闇に公開されない理由の一端を垣間見た気がします。 当方も以下のスキームを準備していたのですが、ハードディスクを全検索した場合などに問題を生じました。 クリックされる度に動的にツリーを付加するのは実によいアイデアで、大いに参考にさせていただきたいと思います。 どうも有難うございました。 -------------------------------------------------------------- //★ グローバル変数の設定 TV_INSERTSTRUCT tv; HTREEITEM hParent; //★ 初期データを設定 void InitTree(HWND hTree, char *pszBasePath) { memset((char *)&tv,'\0',sizeof(tv)); tv.hInsertAfter= TVI_LAST; tv.item.mask= TVIF_TEXT; SetCurrentDirectory(pszBasePath); Set_TVdata(hTree, TVI_ROOT); return; } //★ 再起でフォルダを Tree View に展開 void Set_TVdata(HWND hTree, HTREEITEM hPar) { HTREEITEM hParent; WIN32_FIND_DATA fd; tv.hParent = hPar; HANDLE hFind = FindFirstFile("*", &fd); do { if (lstrcmp(fd.cFileName, "..") != 0 && lstrcmpi(fd.cFileName, ".") != 0) { // ディレクトリの場合 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { tv.hParent = hPar; tv.item.pszText= fd.cFileName; hParent= TreeView_InsertItem(hTree,&tv); SetCurrentDirectory(fd.cFileName); // 再帰の起動 Set_TVdata(hTree,hParent); SetCurrentDirectory(".."); tv.hParent= hPar; } // ファイルの場合 else { tv.item.pszText= fd.cFileName; TreeView_InsertItem(hTree,&tv); } } } while(FindNextFile(hFind, &fd)); FindClose(hFind); tv.hParent = hPar; return; } --------------------------------------------------------------