フォルダ選択コモンダイアログボックスについてご質問です

[戻る]
悩む人 2002/10/30(水) 23:34:36
初めまして。フォルダ選択コモンダイアログボックスについて質問があります。

SHBrowseForFolder関数を使用してフォルダ選択コモンダイアログボックスを呼び出そうとし
ていますが、上手くいきません。症状としましては、メニューの「開く」をクリックした時に
WM_COMMANDメッセージを捕まえて関数を呼び出します。すると、フォーカスがどこかへ消えて
しまい、アプリケーションが応答不能になります。この時ALTキーかF10キーを押すとフォルダ
選択コモンダイアログボックスが出現します。(ダイアログボックス出現後は問題なくパスの取
得ができます)
これをメニューの「開く」を押した瞬間にフォルダ選択コモンダイアログボックスを出したいの
です。
デバッグで追いかけたところ、フォーカスが消えた時点ではSHBrowseForFolder関数で止まっ
ていました。ALTキーかF10キーを押すとSHBrowseForFolder関数から抜けます。

ソースはWEB上にあったいくつかのサンプルソースをそのまま使用してみましたが、どれも同じ
結果でした。Windows 98、NT、2000で試しましたがやはり同じでした。
インクルードヘッダは以下の通りです。
#include <windows.h>
#include <stdio.h>
#include "resource.h"
#include <commctrl.h>
#include <winnetwk.h>
#include <shlobj.h>

プロジェクトの設定では以下のライブラリにリンクしてあります。
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib 
shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Comctl32.lib

何かまだ足りないことがあったらお教え下さい。
よろしくお願いします。

悩む人 2002/10/31(木) 09:53:56
ソースを以下に記載させていただきます。
SDKのスケルトンプログラムにフォルダ選択ダイアログの関数をつけただけのものです。
不要な部分は外しました。
これでRETURNキーを押すことでダイアログを出す”つもり”です。(この状態では出ません)

******************************************************************************

#include <windows.h>
#include <commctrl.h>
#include <shlobj.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int CALLBACK SHMyProc(HWND, UINT, LPARAM, LPARAM);
void OpenFolder(HWND);

int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPreInst, LPSTR lpszCmdLine, int
nCmdShow)
{
 HWND    hWnd;
 MSG
    msg;
 WNDCLASS myProg;
 if(!hPreInst){
  myProg.style    
    = CS_HREDRAW | CS_VREDRAW;
  myProg.lpfnWndProc        = WndProc;
  myProg.cbClsExtra        = 0;
  myProg.cbWndExtra        = 0;
  myProg.hInstance        = hInst;
  myProg.hIcon    
    = NULL;
  myProg.hCursor    
    = LoadCursor(NULL, IDC_ARROW);
  myProg.hbrBackground    = (HBRUSH)GetStockObject(WHITE_BRUSH);
  myProg.lpszMenuName        = MAKEINTRESOURCE(IDR_MENU1);
  myProg.lpszClassName    = "ウィンドウ";
  if(!RegisterClass(&myProg)){
   return FALSE;
  }
 }
 hWnd = CreateWindow("ウィンドウ",
  "フォルダ選択テストプログラム",
  WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  NULL,
  NULL,
  hInst,
  NULL);
 ShowWindow(hWnd, nCmdShow);
 UpdateWindow(hWnd);

 while(GetMessage(&msg, NULL, 0, 0)){
   TranslateMessage(&msg);
   DispatchMessage(&msg);
 }
 return(msg.wParam);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
 switch(msg){
 case WM_CREATE:
  break;
 case WM_KEYDOWN:
  if(wp == VK_RETURN){
   OpenFolder(hWnd);
  }
  break;
 case WM_DESTROY:
  PostQuitMessage(0);
  break;
 default:
  return(DefWindowProc(hWnd, msg, wp, lp));
 }
 return(0L);
}

void OpenFolder(HWND hWnd)
{
 BROWSEINFO bi;
 TCHAR szDir[MAX_PATH];
 LPITEMIDLIST pidl;
 LPMALLOC pMalloc;

 // Shell の標準のアロケータを取得
 if (SUCCEEDED(SHGetMalloc(&pMalloc)))
 {
  // BROWSEINFO 構造体を埋める
  ZeroMemory(&bi,sizeof(bi));
  bi.hwndOwner = hWnd;  // オーナーウィンドウハンドルを設定
  bi.pidlRoot = 0;
  bi.pszDisplayName = 0;
  bi.lpszTitle = "タイトルです。";
  bi.ulFlags = BIF_RETURNONLYFSDIRS;
  bi.lpfn = NULL;

  // フォルダの参照ダイアログボックスの表示
  pidl = SHBrowseForFolder(&bi);

  if (pidl)
  {
  // PIDL をファイルシステムのパスに変換
   if (SHGetPathFromIDList(pidl,szDir))
   {
   // ここにフォルダが選択された時の処理を記述してください。
   // 例として、選択されたフォルダのパスを表示します。
    MessageBox(hWnd, szDir, "選択フォルダ", MB_OK);
   }
  // SHBrowseForFolder によって割り当てられた PIDL を解放
  // In not C++:
  //  pMalloc->lpVtbl->Free(pMalloc, pidl);
  pMalloc->Free(pidl);
  }

 // Shell のアロケータを開放
 // In not C++:
 //  pMalloc->lpVtbl->Release(pMalloc);
  pMalloc->Release();
 }
}

*****************************************************************************

悩む人 2002/10/31(木) 13:32:01
あ、すみません。
これそのままコンパイル通らないですね。

myProg.lpszMenuName        = MAKEINTRESOURCE(IDR_MENU1);
           ↓
myProg.lpszMenuName        = NULL;

にして下さい。

tetsu 2002/10/31(木) 13:56:37
TETSUです。

>myProg.lpszMenuName        = NULL;

に変更して、試してみたらRETURN押下で
フォルダ選択ダイアログが表示されて
フォルダのPATHも表示されましたけど??

>これでRETURNキーを押すことでダイアログを
>出す”つもり”です。(この状態では出ません)

ということは、悩む人さんの環境では
ダイアログは表示されないのでしょうか?

こちらの環境はWIN2K+SP2 VC6++ です。

悩む人 2002/10/31(木) 15:16:14
TETSUさんへ
ご報告ありがとうございます。
私の環境はWindowsNT 4.0+SP4とVC++6.0です。
あとWindows95 VC6.0、Windows98 VC6.0 での環境でも
やはりALTキーかF10キーを押さねばダイアログが表示されません
でした。

プロジェクトの設定に何か特別なことはされていないのでしょうか?

tetsu 2002/10/31(木) 17:06:11
TETSUです。

>プロジェクトの設定に何か特別なことはされていないのでしょうか?

特に特別なことはしていません。


>プロジェクトの設定では以下のライブラリにリンクしてあります。
>kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib 
>shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib 
>Comctl32.lib

ライブラリのリンク情報も上記と同じです。

環境によって変化するのでしょうか?

WIN2k + SP2 VCなし、WIN98SE VCなしの環境でも
動作しました。

tetsu 2002/10/31(木) 17:23:35
TETSUです。

実行手順を追記します。

1.VC6を立ち上げ、WIN32 Applicationより
  空のプロジェクトを作る
2.ソースファイルをコピーしtest.cppを作成
3.myProg.lpszMenuName = NULL; に修正し、コンパイル
4.実行するとダイアログ表示

5.Comctl32.libがリンクされていなかったので
  リンクして再コンパイル
6.実行するとダイアログ表示

です。特別なことはしてないつもりです。

悩む人 2002/10/31(木) 19:19:48
TETSUさんへ

原因個所が分かりました。
私はBBSにアップしてから不要処理を削除したので気づきませんでしたが、
BBSにアップしたソースを実行したらキチンとダイアログボックスが表示されました。
そこで元ソースと差分をとって一つずつ検証した結果、WM_PAINTメッセージが
ひっかかることが分かったのです。

ウィンドウプロシージャのcase文の中に、以下の2文を加えてみて下さい。

case WM_PAINT:
 break;

恐らく私と同じ症状に陥るはずです。
なぜそうなるのかは分かりませんが、ダイアログに行くはずのWM_PAINTメッセージを
親ウィンドウががめてしまい、ダイアログが描画されないのではないか?、と考えら
れます。

WM_PAINTメッセージの個所に
return(DefWindowProc(hWnd, msg, wp, lp));
と書き込んでおくと、またきちんとダイアログが表示されます。

フラグ操作をして、ダイアログ呼び出しの時はDefまかせにすることも可能だと
は思いますが、普通はそんなことしないと思うし、姑息な気もしますよね。
(ろくにソースも書けないくせに生意気言ってすみません)
このソースにはまだ何か私の勘違いが含まれているものと思われますので、
もう少し考えてみたいと思います。

tetsu 2002/11/01(金) 09:07:38
TETSUです。

>デバッグで追いかけたところ、フォーカスが消えた時点ではSHBrowseForFolder関数で
>止まっていました。ALTキーかF10キーを押すとSHBrowseForFolder関数から抜けます。

動作を確認しました。

>フラグ操作をして、ダイアログ呼び出しの時はDefまかせにすることも可能だと

これも一つの対策方法だとは思いますが、他にもよい方法があるかもしれませんね。
(力不足ですみません。)

なぜALT、F10でダイアログが表示されるのですかね?

悩む人 2002/11/01(金) 19:38:08
TETSUさん、ご確認ありがとうございます。
会社で使う必要なツールで急を要していたので、とりあえず
フラグ操作で回避しました。
しかし終了時のMessageBoxで同様の症状が起こってしまい、
やはり根本的解決は必要なようです。

>なぜALT、F10でダイアログが表示されるのですかね?
なぜでしょうね?
ALTとF10は押すとフォーカス(?)がメニューバーに
いったりきたりするので、何らかのメッセージが発生するのかも
しれません。(WindowsのアプリケーションはALTとF10に
何らかの反応を示すようです)

それにしても、このソースに潜むバグは一体どこにあるのでしょう・・・

古いスレッドには追加発言できません。