ツリービューの再帰結合

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
オカピーα
記事: 120
登録日時: 13年前

ツリービューの再帰結合

#1

投稿記事 by オカピーα » 12年前

こんにちは。Win32API狂のオカピーです。
最近はツリービューを極めようとしていまして
ディレクトリツリーをつくろうとしています。

で、以下のコードを見てください。
※無限ループが発生します。コンパイラの「デバッグの中止」で強制終了してください。

main.cpp

コード:

#include <windows.h>
#include <commctrl.h>
#include "resource.h"
#include <shlwapi.h>
#include <windowsx.h>

#pragma comment(lib, "comctl32.lib")      // ツリービューの作成に必要
#pragma comment(lib, "shlwapi.lib")

HWND hList;	//ファイル列挙リストハンドル。不可視。
char *lpDirectoryName = "Projects";
char szOpenDirectory[1024];
// このコード モジュールに含まれる関数の宣言を転送します:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

char szModulePath[MAX_PATH];
char szAddBuf[MAX_PATH];
TCHAR filename[MAX_PATH+10];

HTREEITEM InsertTreeItem(HWND hwndTreeView, HTREEITEM hitemParent, LPTSTR lpszName, BOOL bChild);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
    MSG msg;
    MyRegisterClass(hInstance);

    // アプリケーションの初期化を実行します:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    // メイン メッセージ ループ:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int) msg.wParam;
}

//
//  関数: MyRegisterClass()
//
//  目的: ウィンドウ クラスを登録します。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL , IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = MAKEINTRESOURCE(IDC_HP);
    wcex.lpszClassName = TEXT("HP");
    wcex.hIconSm = LoadIcon(NULL , IDI_APPLICATION);

    return RegisterClassEx(&wcex);
}

//
//   関数: InitInstance(HINSTANCE, int)
//
//   目的: メイン ウィンドウを作成します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hWnd = CreateWindow(TEXT("HP"), TEXT("HP"), WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

//
//  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:  メイン ウィンドウのメッセージを処理します。
//
//  WM_CREATE  - ウインドウ作成時の処理
//  WM_SIZE    - ウインドウサイズ変更時の処理
//  WM_COMMAND - アプリケーション メニューの処理
//  WM_DESTROY - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    static HWND hTree;  // ツリービューのハンドル
    static HTREEITEM hTop, hChild1, hChild2;
    TV_INSERTSTRUCT tv;
	char szListBuf[MAX_PATH];
	int n,c,nList;
	HANDLE hSearch;
	WIN32_FIND_DATA fd;
	HTREEITEM hRootFirst,hRootParent;
	TVITEM getti;
	TCHAR szRootDir[MAX_PATH * 4] , szRootBuf[MAX_PATH] ,szRootFirstBuf[MAX_PATH];
	BOOL bFirst = TRUE;

    switch (message)
    {
        case WM_CREATE:
			CreateDirectory(lpDirectoryName, NULL);
			GetModuleFileNameA(NULL, 
				szModulePath, 
				sizeof(szModulePath)/sizeof(szModulePath[0]));
			for ( c=0; c<2; c++ ){
			PathRemoveFileSpec(szModulePath);
			}
			PathAppend(szModulePath,lpDirectoryName);
			SetCurrentDirectory(szModulePath);
			MessageBox(NULL,szModulePath,"テスト",MB_OK);
            // コモンコントロールの初期化
            InitCommonControls();
            hTree = CreateWindowEx(0, WC_TREEVIEW, TEXT(""),
                        WS_CHILD | WS_VISIBLE
                        | TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_INFOTIP,
                        0, 0, 0, 0,
                        hWnd, (HMENU)ID_TREE1, 
                        (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
            memset(LPTSTR(&tv), '\0', sizeof(tv));
	        tv.hInsertAfter = TVI_LAST;
			tv.item.mask = TVIF_TEXT | TVIF_CHILDREN;
            tv.hParent = TVI_ROOT;
            tv.item.pszText = TEXT("Projects");
			tv.item.cChildren = 1;
			hTop = TreeView_InsertItem(hTree, &tv);
            break;

        case WM_SIZE:
            MoveWindow(hTree, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;

        case WM_COMMAND:
            wmId = LOWORD(wParam);
            wmEvent = HIWORD(wParam);
            // 選択されたメニューの解析:
            switch (wmId)
            {
                case IDM_EXIT:
                    DestroyWindow(hWnd);
                    break;
                default:
                    return DefWindowProc(hWnd, message, wParam, lParam);
            }
            break;
		case WM_NOTIFY:
			if (((LPNMHDR)lParam)->code == TVN_ITEMEXPANDING) {
				LPTVITEM lpItem = &((LPNMTREEVIEW)lParam)->itemNew;
				
				if (!(lpItem->state & TVIS_EXPANDEDONCE)) {
					hRootFirst = TreeView_GetSelection(hTree);

					getti.mask = TVIF_TEXT;
					getti.pszText = szRootFirstBuf;
					getti.cchTextMax = 256;
					getti.hItem = hRootFirst;
					TreeView_GetItem(hTree, &getti);

							while( lstrcmp( szRootBuf, TEXT("Projects")) ){
								hRootParent = TreeView_GetParent(hTree, hRootFirst);
								hRootFirst = hRootParent;

								getti.mask = TVIF_TEXT;
								getti.pszText = szRootBuf;
								getti.cchTextMax = 256;
								getti.hItem = hRootParent;

								TreeView_GetItem(hTree, &getti);
								if(bFirst){
									lstrcat(szRootBuf,szRootFirstBuf);
									bFirst = FALSE;
									continue;
								}

								GetCurrentDirectory(
									sizeof(szRootDir)/sizeof(szRootDir[0]),
									szRootDir);
								PathAppend(szRootDir,szRootBuf);
								SetCurrentDirectory(szRootDir);
							}
									// 全てのファイルを列挙する
									hSearch = FindFirstFile( TEXT("*.*"), &fd );
									if( hSearch == INVALID_HANDLE_VALUE )
									{
										return 0;
									}
									while(1)
									{

										// 列挙されたファイル名を書き出す
										lstrcpy( filename, fd.cFileName ); // ファイル名をバッファにコピー
										if( lstrcmp( filename,TEXT(".")) == 0 || lstrcmp( filename,TEXT("..")) == 0){
											FindNextFile( hSearch, &fd );
											continue;
										}
										tv.hInsertAfter = TVI_LAST;
										tv.item.mask = TVIF_TEXT | TVIF_CHILDREN;
										tv.item.cChildren = 0;
										tv.hParent = hTop;
										tv.item.pszText = filename;
										TreeView_InsertItem(hTree, &tv);
										
										if( !FindNextFile( hSearch, &fd ) )
										{
											if( GetLastError() == ERROR_NO_MORE_FILES )
											{
												break;
											}
											else
											{
												MessageBox( hWnd, TEXT("エラー"), TEXT("エラー"), MB_OK );
												break;
											}
										}
									}

									FindClose( hSearch );
							}
						}
				break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
resource.h

コード:

#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDC_HP 109
#define ID_TREE1 151
main.rc

コード:

#include "resource.h"

/////////////////////////////////////////////////////////////////////////////
//
// メニュー
//

IDC_HP MENU
BEGIN
    POPUP "ファイル(&F)"
    BEGIN
        MENUITEM "アプリケーションの終了(&X)", IDM_EXIT
    END
    POPUP "ヘルプ(&H)"
    BEGIN
        MENUITEM "バージョン情報(&A)...", IDM_ABOUT
    END
END

やりたいことの概要を説明しますね。

これは、見てのとおりディレクトリツリーです。
exeファイルと同じパスに「Projects」フォルダをつくり、
その中身を列挙するというないようです。
構想としてはこうなります。

Projects
┣ test
┃ ┣ test2.txt
┃ ┗ test2.bmp
┣ test.txt
┣ test.bmp
┗ test.doc

で、例えば「Projects」を展開しますよね。もちろんtest(これはフォルダ)やら
test.txt、text.bmpなどがズラッと出ます。(これらはファイル)
で、これの取得はカレントディレクトリを使用しているわけなんですよ。
引き続き例えば「test」をダブルクリックします。
ということはカレントディレクトリをProjects\\testに移さないといけないんです。
つまり、「カレントディレクトリ + Projectsに到達するまでのすべてのカラム名」を
つくりたいわけなんですが・・・ 無限ループです(´・ω・`)

八方塞です・・・ どなたか教えてください・・・^^;
C,C++,C#,これらを極めることを「3C政策」と言う

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: ツリービューの再帰結合

#2

投稿記事 by softya(ソフト屋) » 12年前

コードを見ていませんが、デバッグ力を身につけたほうが良い時期に来ていると思います。
デバッガで追いかけて、どうして無限ループになっているか考察してみてください。

※ あと無意味なインデントがあるので凄く見づらいです。
それに、WM_NOTIFY:でディレクトリ探索しているのでディレクトリが深くなると応答しないプログラムに分類されるますのでスレッド化も考えたほうが良いでしょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
へにっくす
記事: 634
登録日時: 13年前
住所: 東京都

Re: ツリービューの再帰結合

#3

投稿記事 by へにっくす » 12年前

まず言えることは、ちゃんと初期化しましょう。たとえば、WndProc関数の直後にある、以下の変数ですが、

コード:

    int wmId, wmEvent;
    static HWND hTree;  // ツリービューのハンドル
    static HTREEITEM hTop, hChild1, hChild2;
    TV_INSERTSTRUCT tv;
    char szListBuf[MAX_PATH];
    int n,c,nList;
    HANDLE hSearch;
    WIN32_FIND_DATA fd;
    HTREEITEM hRootFirst,hRootParent;
    TVITEM getti;
    TCHAR szRootDir[MAX_PATH * 4] , szRootBuf[MAX_PATH] ,szRootFirstBuf[MAX_PATH];
このようにとりあえず全部0で初期化してください(他の変数もですよ!)。C/C++言語ではこう明示しないと、どんな値が入ってるか不明なのです。
(コンパイルオプションで0で初期化するものがありますが、普通設定しません)

コード:

	int wmId = 0, wmEvent = 0;
	static HWND hTree = NULL;  // ツリービューのハンドル
	static HTREEITEM hTop = NULL, hChild1 = NULL, hChild2 = NULL;
	TV_INSERTSTRUCT tv = {0};
	char szListBuf[MAX_PATH] = {0};
	int n = 0,c = 0,nList = 0;
	HANDLE hSearch = NULL;
	WIN32_FIND_DATA fd = {0};
	HTREEITEM hRootFirst = NULL,hRootParent = NULL;
	TVITEM getti = {0};
	TCHAR szRootDir[MAX_PATH * 4] = {0}, szRootBuf[MAX_PATH]={0},szRootFirstBuf[MAX_PATH]={0};
あと無限ループですが、
while( lstrcmp( szRootBuf, TEXT("Projects")) )
文の直後でhRootFirstの親を取り出して、その親のハンドルをhRootFirstに上書きしてます。
意味ないよ?
written by へにっくす

閉鎖

“C言語何でも質問掲示板” へ戻る