フォルダ選択ダイアログボックスを使ってみる

フォルダ選択ダイアログボックス

Windows シェルは、フォルダをユーザー操作によって選択するための機能を提供しています。これは、フォルダを選択するダイアログボックスを表示して、ユーザーに選択させるというものです。

手順は以下のようになります。

  1. SHGetMalloc 関数を使って、シェルの IMalloc インターフェースへのポインタを取得する。
  2. デスクトップ以外をルートフォルダにする場合は、 SHGetSpecialFolderLocation 関数でルートとするフォルダのPIDLを取得する。
  3. BROWSEINFO 構造体に情報を格納して SHBrowseForFolder 関数を呼び出し、選択されたアイテムのPIDLを取得する。
  4. PIDL を使った操作を行なう。(SHGetPathFromIDList 関数など。)
  5. IMalloc::Free メソッドでアイテムIDリスト用に確保されていたメモリを解放する。
  6. IMalloc インターフェースを解放する。

ダイアログボックスの表示

フォルダ選択ダイアログボックスの表示には、 SHBrowseForFolder 関数を使います。この関数は、ユーザーによるフォルダ選択操作ができる[フォルダの参照]ダイアログボックスを表示し、ユーザーが指定したアイテムのPIDLを返すものです。

まずは、 BROWSEINFO 構造体に必要な情報を格納します。

typedef struct _browseinfo { 
    HWND          hwndOwner; 
    LPCITEMIDLIST pidlRoot; 
    LPTSTR        pszDisplayName; 
    LPCTSTR       lpszTitle; 
    UINT          ulFlags; 
    BFFCALLBACK   lpfn; 
    LPARAM        lParam; 
    int           iImage; 
} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;

hwndOwner メンバには、ダイアログボックスのオーナーウィンドウのハンドルを指定します。 pidlRoot メンバには、表示するダイアログボックスの中のツリービューのルートフォルダをPIDLで指定しますが、デスクトップをルートとする場合は 0 (NULL) を指定できます。 pszDisplayName メンバには、選択されたアイテムの表示文字列を格納する文字列変数のアドレスを指定します。 lpszTitle メンバには、ツリーの上部に表示する文字列のアドレスを指定します。 ulFlags メンバには、どのようなフォルダを取得できるようにするのかを指定します。例えば、ファイルシステムディレクトリのみを取得したい場合には 0x0001 (BIF_RETURNONLYFSDIRS) を指定します。その他のメンバはここでは使用しないので、0 を格納しておきます

構造体に情報を格納できたら、SHBrowseForFolder 関数を呼び出します。

WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolderA(
    LPBROWSEINFO  lpbi
);

lpbi には BROWSEINFO 構造体のアドレスを指定します。この関数の戻り値として、選択されたアイテムのPIDLが返ります。キャンセルされた場合には、戻り値は 0 になります。

    ; BROWSEINFO 構造体
    mref bmscr, 67             ; ウィンドウの BMSCR 構造体
    binfo.0 = bmscr.13         ; オーナーウィンドウハンドル
    binfo.1 = 0                ; デスクトップフォルダをルートに
    sdim buf, 260
    getptr  binfo.2, buf
    sztitle = "フォルダを選択してください。"
    getptr  binfo.3, sztitle   ; 表示文字列
    binfo.4 = 0x4008           ; BIF_RETURNFSANCESTORS | BIF_BROWSEINCLUDEFILES
    binfo.5 = 0, 0, 0

    ; SHBrowseForFolderA 関数呼び出し
    getptr  pm, binfo
    dllproc "SHBrowseForFolderA", pm, 1, D_SHELL
    pidl = stat                ; 選択アイテムのPIDL

フォルダ選択ダイアログボックス サンプルスクリプト

フォルダ選択ダイアログボックスで選択されたアイテムのパス名を表示するサンプルです。

    #include "llmod.as"
    #include "rrmod/com/lollipop.as"

    ; IMalloc インターフェース取得
    getptr  pm, pMalloc
    dllproc "SHGetMalloc", pm, 1, D_SHELL
    if (stat < 0)|(pMalloc == 0){ ; 戻り値は HRESULT 型(負のときエラー)
        dialog "IMalloc インターフェースを取得できませんでした", 1
        end
    }

    ; BROWSEINFO 構造体
    mref bmscr, 67             ; ウィンドウの BMSCR 構造体
    binfo.0 = bmscr.13         ; オーナーウィンドウハンドル
    binfo.1 = 0                ; デスクトップフォルダをルートに
    sdim buf, 260
    getptr  binfo.2, buf
    sztitle = "フォルダを選択してください。"
    getptr  binfo.3, sztitle   ; 表示文字列
    binfo.4 = 0x0003           ; BIF_RETURNONLYFSDIRS| BIF_DONTGOBELOWDOMAIN
    binfo.5 = 0, 0, 0

    ; フォルダ選択ダイアログボックス表示
    ; SHBrowseForFolderA 関数呼び出し
    getptr  pm, binfo
    dllproc "SHBrowseForFolderA", pm, 1, D_SHELL
    pidl = stat                ; 選択アイテムのPIDL
    if (pidl == 0) {
        dialog "キャンセルされました"
        goto *lb_free
    }

    ; PIDLからパス名取得
    sdim  pathname, 260           ; パス名を格納する変数
    pm.0 = pidl                   ; PIDL
    getptr  pm.1, pathname
    dllproc "SHGetPathFromIDListA", pm, 2, D_SHELL
    if stat == 0 {                ; 戻り値は BOOL 型(0 のときエラー)
        dialog "PIDL からパス名を取得できませんでした", 1
        goto *lb_free
    }

    dialog "パス名は[" + pathname + "]です"

*lb_free
    if pMalloc {
        ; IMalloc->Free(pidl);
        pm = pidl                     ; PIDL
        icall  pMalloc, 5, pm, 1

        ; インターフェース解放
        release  pMalloc
    }

    end