SHBrowsForFolder() のラッパクラス

フォルダ選択ダイアログって結構使い方に癖があって、使うのが面倒くさいですよね?
何でまたこんな仕様になってしまったのか不思議です。まあ、それが拡張性を持たせたってこと何でしょうけど。

使いにくいので、ラッパクラスを作ってみました。
使い方は、MFCの CFileDialog クラスを参考に、「見た感じダイアログっぽい」ように作ってあります。

ソースはここにありますので、ご自由にご利用ください。
※ただし、このソースを使用したことによって発生したいかなる不都合・不利益等につきまして、作者は一切責任を負わないものとしてください。

クラス名:CBrowseDialog
ファイル:browsedialog.h
環境:MFC/Win32
// 
//    フォルダ選択ダイアログラップクラス
//    CBrowseDialog
//    
//    SHBrowseForFolder()をラップしたクラスです。
//    
//
//    作者:あいそび
//    日付:2004/02/19
//    
// 使用法:
// 
//     CBrowseDialog dlg("c:\\windows\\", "選択してね");
//    if(dlg.DoModal() == IDOK)
//    {
//        CString strPath = dlg.GetPathName();    // 選択パスを取得
//    }
//
// 
// v5.0 (Shell32.dll:Windows2000以降)について
//    v5.0の印が付いているものは、ビルド時に新しいPlatformSDKのヘッダファイルが必要です。
//    フラグなどは、数値で代用することができますが、クライアント側に新しいバージョンの
//    Shell32.dll が入っている必要があります。
//    無い場合は期待道理に動作しません(多分)。
//
//

#pragma once

const CString INVALID_CHARS_FOR_PATH = "*?\"<>|";
const CString INVALID_CHARS_FOR_FNAME = "\\/:" + INVALID_CHARS_FOR_PATH;



#ifndef BIF_NEWDIALOGSTYLE
#define BIF_NEWDIALOGSTYLE 0x0040 // Use the new dialog layout with the ability to resize
#endif // Caller needs to call OleInitialize() before using this API

#ifndef BIF_USENEWUI
#define BIF_USENEWUI           (BIF_NEWDIALOGSTYLE | BIF_EDITBOX)
#endif


class CBrowseDialog
{
public:
    CBrowseDialog(const CString strDefPath = ""
        , const CString strMessage = "フォルダを選択してください"
        , CWnd* pWnd = AfxGetMainWnd())
    {
        if(pWnd)
        {
            m_hWnd = pWnd->GetSafeHwnd();
        }
        m_strDefPath = strDefPath;
        m_strMessage = strMessage;
        m_pIDL = NULL;
        m_uiFlags    = BIF_RETURNONLYFSDIRS;
        m_bRequireExisting = TRUE;
    }
    virtual ~CBrowseDialog(){}

    // ツリーのルートとするITEMIDLISTをセット
    void SetRootIDL(ITEMIDLIST* pIDL){ m_pIDL = pIDL; }

    // フラグをセット (*1 と *2 は判別できなくなるので排他とします)
    //    BIF_RETURNONLYFSDIRS    - *1 フォルダのみ選択可能
    //    BIF_BROWSEINCLUDEFILES    - *1 ファイルも表示
    //    BIF_BROWSEFORCOMPUTER    - *2 コンピュータを選択
    //    BIF_BROWSEFORPRINTER    - *2 プリンタを選択
    //    BIF_DONTGOBELOWDOMAIN    - ネットワークドメイン以下を表示しない
    //    BIF_USENEWUI            - *1 新しいUIを使用(v5.0) =(0x0040 | 0x0010)
    void SetFlags(UINT uiFlags)
    { 
        ASSERT(!(uiFlags & (BIF_RETURNONLYFSDIRS | BIF_BROWSEINCLUDEFILES | BIF_USENEWUI)
             &&  uiFlags & (BIF_BROWSEFORCOMPUTER | BIF_BROWSEFORPRINTER)));
        // *1 と *2 は排他とします(本当は排他ではないですが、ここではそうします)。
        // コンピュータ名などを選んだのか、ディレクトリを選んだのか判別が難しいため。
        // BIF_USENEWUI を指定すると、コンピュータ・プリンタだけの選択がうまくできない。
        m_uiFlags = uiFlags; 
    }

    // 存在しないファイル名を許可するかどうか設定
    void SetRequireExisting(BOOL bRequire)
    {
        m_bRequireExisting = bRequire;
    }

    // 結果のパスを取得
    const CString& GetPathName(){ return m_strPathName; }

    // ダイアログ表示
    int DoModal()
    {
        BROWSEINFO bInfo;
        TCHAR* szDisplayName = m_strPathName.GetBuffer(MAX_PATH);

        // BROWSEINFO構造体に値を設定
        bInfo.hwndOwner = m_hWnd; // ダイアログの親ウインドウのハンドル
        bInfo.pidlRoot = m_pIDL; // ルートフォルダを示す
                                 // ITEMIDLISTのポインタ
                                 // (NULLの場合デスクトップフォルダが使われます)
        bInfo.pszDisplayName = szDisplayName; // 選択されたフォルダ名を
                                              // 受け取るバッファのポインタ
        bInfo.lpszTitle = m_strMessage; // ツリービューの上部に表示される文字列 
        bInfo.ulFlags = m_uiFlags | BIF_VALIDATE; // 表示されるフォルダの種類を
                                                  // 示すフラグ
        bInfo.lpfn = BrowseCallbackProc; // BrowseCallbackProc関数のポインタ
        bInfo.lParam = (LPARAM)this; // コールバック関数に渡す値

        // フォルダ選択ダイアログを表示
        HRESULT hr = ::CoInitialize(NULL);
        if(SUCCEEDED(hr))
        {
            LPITEMIDLIST pIDList = ::SHBrowseForFolder(&bInfo);
            int nRet = IDOK;
            if(pIDList == NULL)
            {
                nRet = IDCANCEL; // キャンセルの場合は空の文字列を返します
                szDisplayName[0] = '\0';
            }
            if(nRet == IDOK)
            {
                if(!(m_uiFlags & (BIF_BROWSEFORCOMPUTER | BIF_BROWSEFORPRINTER)))
                {
                    // ItemIDListをパス名に変換します
                    if(!::SHGetPathFromIDList(pIDList, szDisplayName))
                    {
                        // エラー処理
                        nRet = IDCANCEL;
                        szDisplayName[0] = '\0';
                    }
                }
                // コンピュータ・プリンタ名の場合は、変換の必要なし
            }
            m_strPathName.ReleaseBuffer();

            // 最後にpIDListのポイントしているメモリを開放します
            ::CoTaskMemFree(pIDList);
            ::CoUninitialize();
            return m_strPathName.IsEmpty() ? IDCANCEL : nRet;
        }
        else
        {
            AfxMessageBox("COMの初期化に失敗しました");
            return IDCANCEL;
        }

    }

protected:
    // コールバック関数
    static int CALLBACK BrowseCallbackProc(HWND hWnd, UINT uMsg
                                        , LPARAM lParam, LPARAM lpData)
    {
        return ((CBrowseDialog*)lpData)->BrowseCallbackImp(hWnd, uMsg, lParam);
    }

    // コールバック関数の実装
    int BrowseCallbackImp(HWND hWnd, UINT uMsg, LPARAM lParam)
    {
        if(uMsg == BFFM_INITIALIZED)
        {
            if(!m_strDefPath.IsEmpty())
                ::SendMessage(hWnd, BFFM_SETSELECTION, TRUE
                        , (LPARAM)(LPCTSTR)m_strDefPath);
        }
        else if(uMsg == BFFM_SELCHANGED)
        {
            ;
        }
        else if(uMsg == BFFM_VALIDATEFAILED)
        {
            CString strName((char*)lParam);
            if(m_bRequireExisting)
            {
                AfxMessageBox("既存の項目を選択してください");
                return TRUE;
            }
            if(strName.FindOneOf(INVALID_CHARS_FOR_FNAME) != -1)
            {
                AfxMessageBox("次の文字は名前として使用できません:\n\n" 
                                    + INVALID_CHARS_FOR_FNAME);
                return TRUE;
            }
        }

        return 0;
    }

    // 非公開メンバ
    HWND    m_hWnd;
    CString m_strMessage;
    CString m_strDefPath;
    ITEMIDLIST* m_pIDL;
    UINT    m_uiFlags;
    CString m_strPathName;
    BOOL    m_bRequireExisting;
};


使い方:こんな感じです。
// 初期表示フォルダ・メッセージ文字列を指定して構築
CBrowseDialog dlg("c:\\windows\\", "選択してね");
// フラグを設定
dlg.SetFlags(BIF_RETURNONLYFSDIRS // パスを取得
| BIF_BROWSEINCLUDEFILES // ファイルもツリーに含める
| BIF_USENEWUI); // Win2K以降の新しいUIを使用
if(dlg.DoModal() == IDOK)
{
CString strPath = dlg.GetPathName(); // パス取得
AfxMessageBox(strPath);
}

フォルダパスだけでなく、ファイルのパスも取得できますよ。
[PR]
by isoq | 2004-02-19 23:05 | C/C++/Win32
e87.com(千趣会イイハナ) 花を贈るなら日比谷花壇
<< ねむい Virtual PC 5.2 ... >>