SDK Index Previous page Next page

SHBrowseForFolderの初期フォルダの指定


ファイルのコピーなどをするときに、コピー先のフォルダを指定する必要があると思います。 そういうときに使うのが、SHBrowseForFolderAPIですが、このAPIで、初期フォルダを指定しようとすると以外と悩んでしまいます。そこで今回は、このAPIで初期フォルダの指定するプログラムを作ります。(と言ってもたいして難しくないですが) まず、VC++のAPIヘルプを見てみましょう

WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder( LPBROWSEINFO lpbi );

となっていますね。なんかややこしいようですが、要するに、BROWSEINFO構造体のポインタを渡せば ITEMLISTのポインタがかえってくるということです。

BROWSEINFO構造体は、ヘルプでは

typedef struct _browseinfo { 
    HWND hwndOwner;          // see below 
    LPCITEMIDLIST pidlRoot;  // see below 
    LPSTR pszDisplayName;    // see below 
    LPCSTR lpszTitle;        // see below 
    UINT ulFlags;            // see below 
    BFFCALLBACK lpfn;        // see below 
    LPARAM lParam;           // see below 
    int iImage;              // see below 
} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO; 

となっています。それぞれのメンバの意味は

HWND hwndOwner; ウインドウハンドル

LPCITEMIDLIST pidlRoot;

ルートにするITEMIDLISTのポインタこれを指定すると、ITEMIDLISTの示す場所が一番上に来てそれより上はなくなる。

LPSTR pszDisplayName; 選択されたフォルダ名(フルパスではない)が格納される。
LPCSTR lpszTitle; ダイアログに表示するメッセージ
UINT ulFlags; フラグ(下記参照)
BFFCALLBACK lpfn; コールバック関数のアドレス
LPARAM lParam; コールバック関数に渡す引数の値
int iImage; すみませんよく分かりません

使用できるフラグ

BIF_BROWSEFORCOMPUTER コンピュータ名しか選択できない
BIF_BROWSEFORPRINTER プリンタしか選択できない
BIF_DONTGOBELOWDOMAIN ?Does not include network folders below the domain level in the tree view control.(誰か訳して)
BIF_RETURNFSANCESTORS file system ancestors(?)以外選択できない
BIF_RETURNONLYFSDIRS フォルダ以外選択できない
BIF_STATUSTEXT ステータス領域を持つ。コールバックはメッセージを送ることでこれを設定できる。

と言うようになっています。(間違ってるかも 英語は嫌いだ)

ITEMIDLISTは、実は僕もよく分かっていませんが、フォルダとかプリンタとかの名前とかの情報が入っているものだと思います。

初期フォルダの指定

簡単な関数の説明をしましたが、これだけでは初期フォルダを指定することはできません。
では、何が足りないのか?

実は、フォルダを指定するには、コールバック関数を作らなければいけません。
コールバック関数と書くと難しそうに思いますが、ふつうの関数とほとんどかわりません。
まずは、今回使うコールバック関数を見て下さい

/*コールバック関数*/

int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData)
{
    if(uMsg==BFFM_INITIALIZED){
        SendMessage(hwnd,BFFM_SETSELECTION,(WPARAM)TRUE,lpData);
    }
    return 0;
}
見れば分かると思いますが、ウインドウプロシージャと全く同じ形です。
初期フォルダを指定するには、この関数にBFFM_INITIALIZEDメッセージが来たときウインドウに、 BFFM_SETSELECTIONメッセージを送ります。 そのとき、wParamをFALSEにしたとき、lParamにはITEMIDLISTのポインタを指定し、 TRUEにしたときは、フォルダのパスの格納されている文字列変数のポインタを指定します。

指定方法が分かったところで、ディレクトリを指定する関数を作ってみます。

void GetFolder(HWND hdlg)
{
    char    dst_file[MAX_PATH];
    BROWSEINFO  binfo;
    LPITEMIDLIST idlist;

    GetDlgItemText(hdlg, IDC_DESFILE, dst_file,NAMESIZE);
    binfo.hwndOwner=hdlg;
    binfo.pidlRoot=NULL;
    binfo.pszDisplayName=dst_file;
    binfo.lpszTitle="結合先のフォルダの指定";
    binfo.ulFlags=BIF_RETURNONLYFSDIRS; 
    binfo.lpfn=&BrowseCallbackProc;               //コールバック関数を指定する
    binfo.lParam=(LPARAM)dst_file;                //コールバックに渡す引数
    binfo.iImage=(int)NULL;
    idlist=SHBrowseForFolder(&binfo);
    SHGetPathFromIDList(idlist,dst_file);         //ITEMIDLISTからパスを得る
    CoTaskMemFree(idlist);                        //ITEMIDLISTの解放     99/11/03訂正
}
99/11/03追加
アイテムIDリストの解放にはCoTaskMemFreeという関数を使います。またこの関数の代わりに
void _SHFree(LPITEMIDLIST lpIDList)
{
    IMalloc *pMalloc;
    SHGetMalloc(&pMalloc);
    if(pMalloc){
        pMalloc->lpVtbl->Free(pMalloc,lpIDList);
        pMalloc->lpVtbl->Release(pMalloc);
    }
}
と言う関数を作って、これを呼び出してもかまいません。

これで、初期フォルダを指定する関数ができました。

今回は説明しませんでしたが、コールバック関数のメッセージには他にもいくつかあるので調べてみて下さい。


to sdk prev next