Win32 API 日本語リファレンス
ホームUI.Shell › SHBrowseForFolderW

SHBrowseForFolderW

関数
フォルダー選択ダイアログ(Unicode)を表示する。
DLLSHELL32.dll文字セットUnicode (-W)呼出規約winapi対応OSWindows XP 以降

シグネチャ

// SHELL32.dll  (Unicode / -W)
#include <windows.h>

ITEMIDLIST* SHBrowseForFolderW(
    BROWSEINFOW* lpbi
);

パラメーター

名前方向説明
lpbiBROWSEINFOW*inダイアログ ボックスの表示に使用される情報を含む BROWSEINFO 構造体へのポインターです。

戻り値の型: ITEMIDLIST*

公式ドキュメント

ユーザーが Shell フォルダーを選択できるダイアログ ボックスを表示します。(Unicode)

戻り値

型: PIDLIST_ABSOLUTE

名前空間のルートを基準とした、選択されたフォルダーの位置を指定する PIDL を返します。ユーザーがダイアログ ボックスの キャンセル ボタンを選択した場合、戻り値は NULL になります。

返される PIDL は、フォルダーではなくフォルダー ショートカットのものである場合があります。このケースの詳細については、「解説」セクションを参照してください。

解説(Remarks)

Windows Vista 以降では、SHBrowseForFolder 関数ではなく、FOS_PICKFOLDERS オプションを指定した IFileDialog の使用を推奨します。これはフォルダー選択モードの「ファイルを開く」ダイアログを使用するもので、推奨される実装方法です。

SHBrowseForFolder を呼び出す前に、Component Object Model (COM) を初期化する必要があります。CoInitializeEx を使用して COM を初期化する場合は、その dwCoInit パラメーターに COINIT_APARTMENTTHREADED フラグを設定する必要があります。また、常にアパートメント スレッドを使用する CoInitialize または OleInitialize を使用することもできます。ドラッグ アンド ドロップ機能が必要な場合は、必要な OLE と COM の両方を初期化する OleInitialize を推奨します。

注意 CoInitializeExCOINIT_MULTITHREADED フラグを指定して COM を初期化した場合、呼び出し元アプリケーションが BROWSEINFO 構造体で BIF_USENEWUI または BIF_NEWDIALOGSTYLE フラグを使用していると、SHBrowseForFolder は失敗します。
SHBrowseForFolder によって返された IDList が不要になったときに CoTaskMemFree を呼び出して解放するのは、呼び出し元アプリケーションの責任です。

ダイアログ ボックスには 2 種類のスタイルがあります。古いスタイルは既定で表示され、サイズ変更できません。新しいスタイルは、ダイアログ ボックス内でのドラッグ アンド ドロップ機能、並べ替え、削除、ショートカット メニュー、新しいフォルダーを作成する機能、その他のショートカット メニュー コマンドなど、多くの追加機能を提供します。最初は古いダイアログ ボックスよりも大きいですが、ユーザーがサイズを変更できます。新しいスタイルのダイアログ ボックスを指定するには、BROWSEINFO 構造体の ulFlags メンバーに BIF_USENEWUI フラグを設定します。

BROWSEINFO 構造体の lpfn メンバーで指定されるコールバック関数を実装すると、ダイアログ ボックスへのハンドルを受け取ります。このウィンドウ ハンドルの用途の 1 つは、ダイアログ ボックスのレイアウトや内容を変更することです。古いスタイルのダイアログ ボックスはサイズ変更できないため、変更は比較的簡単です。新しいスタイルのダイアログ ボックスの変更ははるかに困難で、推奨されません。古いスタイルとはサイズやレイアウトが異なるだけでなく、その寸法やコントロールの位置は、ユーザーがサイズを変更するたびに変化します。

BROWSEINFO 構造体の ulFlags メンバーに BIF_RETURNONLYFSDIRS フラグが設定されている場合、OK ボタンは "\server\share" 項目やディレクトリ項目だけでなく、"\server" 項目に対しても有効なままになります。ただし、ユーザーが "\server" 項目を選択した場合、SHBrowseForFolder が返した PIDL を SHGetPathFromIDList に渡すと失敗します。

カスタム フィルタリング

Windows XP 以降、SHBrowseForFolder はダイアログ ボックスの内容に対するカスタム フィルタリングをサポートしています。カスタム フィルターを作成するには、次の手順に従います。
  1. lpbi パラメーターが指す BROWSEINFO 構造体の ulFlags メンバーに BIF_NEWDIALOGSTYLE フラグを設定します。
  2. 同じ BROWSEINFO 構造体の lpfn メンバーにコールバック関数を指定します。
  3. BFFM_INITIALIZED メッセージと BFFM_IUNKNOWN メッセージを受け取るようにコールバック関数をコーディングします。BFFM_IUNKNOWN メッセージを受け取ると、コールバック関数の lParam パラメーターには、ダイアログ ボックスの IUnknown 実装へのポインターが含まれます。その IUnknown に対して QueryInterface を呼び出して、IFolderFilterSite のインスタンスへのポインターを取得します。
  4. IFolderFilter を実装するオブジェクトを作成します。
  5. IFolderFilterSite::SetFilter を呼び出し、自分の IFolderFilter へのポインターを渡します。これにより、IFolderFilter のメソッドを使用して、ツリーに項目を含めたり除外したりできるようになります。
  6. フィルターが作成されると、IFolderFilterSite インターフェイスは不要になります。それ以上使用しない場合は、IFolderFilterSite::Release を呼び出します。

ショートカットの取り扱い

注意 このセクションは Windows 2000 以前のシステムにのみ適用されます。既定では、Windows XP 以降のシステムは、BROWSEINFO 構造体に BIF_NOTRANSLATETARGETS フラグが設定されていない限り、ショートカット自体ではなくショートカットのターゲットの PIDL を返します。
SHBrowseForFolder がショートカットへの PIDL を返した場合、その PIDL を SHGetPathFromIDList に渡すと、ターゲットのパスではなくショートカット自体のパスが返されます。ショートカットのターゲットへのパスは、この例に示すように IShellLink インターフェイスを使用して取得できます。
#include 

// Macros for interface casts
#ifdef __cplusplus
#define IID_PPV_ARG(IType, ppType) IID_##IType, reinterpret_cast(static_cast(ppType))
#else
#define IID_PPV_ARG(IType, ppType) &IID_##IType, (void**)(ppType)
#endif

// Retrieves the UIObject interface for the specified full PIDL
STDAPI SHGetUIObjectFromFullPIDL(LPCITEMIDLIST pidl, HWND hwnd, REFIID riid, void **ppv)
{
    LPCITEMIDLIST pidlChild;
    IShellFolder* psf;

    *ppv = NULL;

    HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
    if (SUCCEEDED(hr))
    {
        hr = psf->GetUIObjectOf(hwnd, 1, &pidlChild, riid, NULL, ppv);
        psf->Release();
    }
    return hr;
}
 
#define ILSkip(pidl, cb)       ((LPITEMIDLIST)(((BYTE*)(pidl))+cb))
#define ILNext(pidl)           ILSkip(pidl, (pidl)->mkid.cb)
 
HRESULT SHILClone(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl)
{
    DWORD cbTotal = 0;

    if (pidl)
    {
        LPCITEMIDLIST pidl_temp = pidl;
        cbTotal += sizeof (pidl_temp->mkid.cb);

        while (pidl_temp->mkid.cb) 
        {
            cbTotal += pidl_temp->mkid.cb;
            pidl_temp += ILNext (pidl_temp);
        }
    }
    
    *ppidl = (LPITEMIDLIST)CoTaskMemAlloc(cbTotal);
    
    if (*ppidl)
        CopyMemory(*ppidl, pidl, cbTotal);
 
    return  *ppidl ? S_OK: E_OUTOFMEMORY;
}
 
// Get the target PIDL for a folder PIDL. This also deals with cases of a folder  
// shortcut or an alias to a real folder.
STDAPI SHGetTargetFolderIDList(LPCITEMIDLIST pidlFolder, LPITEMIDLIST *ppidl)
{
    IShellLink *psl;
    
    *ppidl = NULL;
    
    HRESULT hr = SHGetUIObjectFromFullPIDL(pidlFolder, NULL, IID_PPV_ARG(IShellLink, &psl));
    
    if (SUCCEEDED(hr))
    {
        hr = psl->GetIDList(ppidl);
        psl->Release();
    }
    
    // It's not a folder shortcut so get the PIDL normally.
    if (FAILED(hr))
        hr = SHILClone(pidlFolder, ppidl);
    
    return hr;
}

// Get the target folder for a folder PIDL. This deals with cases where a folder
// is an alias to a real folder, folder shortcuts, the My Documents folder, and 
// other items of that nature.
STDAPI SHGetTargetFolderPath(LPCITEMIDLIST pidlFolder, LPWSTR pszPath, UINT cchPath)
{
    LPITEMIDLIST pidlTarget;
    
    *pszPath = 0;

    HRESULT hr = SHGetTargetFolderIDList(pidlFolder, &pidlTarget);
    
    if (SUCCEEDED(hr))
    {
        SHGetPathFromIDListW(pidlTarget, pszPath);   // Make sure it is a path
        CoTaskMemFree(pidlTarget);
    }
    
    return *pszPath ? S_OK : E_FAIL;
}

// Retrieves the UIObject interface for the specified full PIDLstatic 
HRESULT SHGetUIObjectFromFullPIDL(LPCITEMIDLIST pidl, HWND hwnd, REFIID riid, void **ppv)
{    
    LPCITEMIDLIST pidlChild;    
    IShellFolder* psf;    
    *ppv = NULL;    
    
    HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (LPVOID*)&psf, &pidlChild);    
    if (SUCCEEDED(hr))    
    {        
        hr = psf->GetUIObjectOf(hwnd, 1, &pidlChild, riid, NULL, ppv);        
        psf->Release();    
    }    
    return hr;
}

static HRESULT SHILClone(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl)
{    
    DWORD cbTotal = 0;    
    if (pidl)
    {        
        LPCITEMIDLIST pidl_temp = pidl;        
        cbTotal += pidl_temp->mkid.cb;        
        
        while (pidl_temp->mkid.cb)         
        {            
            cbTotal += pidl_temp->mkid.cb;            
            pidl_temp = ILNext(pidl_temp);        
        }    
    }    
    
    *ppidl = (LPITEMIDLIST)CoTaskMemAlloc(cbTotal);    
    if (*ppidl)        
        CopyMemory(*ppidl, pidl, cbTotal);    
        
    return  *ppidl ? S_OK: E_OUTOFMEMORY;
}
    
// Get the target PIDL for a folder PIDL. This also deals with cases of a folder  
// shortcut or an alias to a real folder.
static HRESULT SHGetTargetFolderIDList(LPCITEMIDLIST pidlFolder, LPITEMIDLIST *ppidl)
{    
    IShellLink *psl;    
    *ppidl = NULL;    
    
    HRESULT hr = SHGetUIObjectFromFullPIDL(pidlFolder, NULL, IID_IShellLink, (LPVOID*)&psl);    
    if (SUCCEEDED(hr))    
    {        
        hr = psl->GetIDList(ppidl);        
        psl->Release();    
    }    
    
    // It's not a folder shortcut so get the PIDL normally.    
    if (FAILED(hr))        
        hr = SHILClone(pidlFolder, ppidl);    
    return hr;
}

// Get the target folder for a folder PIDL. This deals with cases where a folder
// is an alias to a real folder, folder shortcuts, the My Documents folder, 
// and so on.
STDAPI SHGetTargetFolderPath(LPCITEMIDLIST pidlFolder, LPWSTR pszPath, UINT cchPath)
{    
    LPITEMIDLIST pidlTarget;    
    *pszPath = 0;    
    
    HRESULT hr = SHGetTargetFolderIDList(pidlFolder, &pidlTarget);    
    if (SUCCEEDED(hr))    
    {        
        SHGetPathFromIDListW(pidlTarget, pszPath);   
        
        // Make sure it is a path        
        CoTaskMemFree(pidlTarget);    
    }    
    
    return *pszPath ? S_OK : E_FAIL;
}
メモ

shlobj_core.h ヘッダーは、UNICODE プリプロセッサ定数の定義に基づいて、この関数の ANSI 版または Unicode 版を自動的に選択するエイリアスとして SHBrowseForFolder を定義します。エンコーディング中立のエイリアスの使用を、エンコーディング中立でないコードと混在させると、不一致が生じてコンパイル エラーや実行時エラーが発生する可能性があります。詳細については、「Conventions for Function Prototypes」を参照してください。

出典・ライセンス: 上記「公式ドキュメント」の内容は Microsoft の Win32 API ドキュメント(MicrosoftDocs/sdk-api)を日本語に翻訳・改変したものです。© Microsoft Corporation. CC BY 4.0 で提供。
Microsoft 公式リファレンス: 英語 (en-us) · 日本語 (ja-jp) · 原文ソース (GitHub)

各言語での呼び出し定義

// SHELL32.dll  (Unicode / -W)
#include <windows.h>

ITEMIDLIST* SHBrowseForFolderW(
    BROWSEINFOW* lpbi
);
[DllImport("SHELL32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
static extern IntPtr SHBrowseForFolderW(
    IntPtr lpbi   // BROWSEINFOW*
);
<DllImport("SHELL32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True)>
Public Shared Function SHBrowseForFolderW(
    lpbi As IntPtr   ' BROWSEINFOW*
) As IntPtr
End Function
' lpbi : BROWSEINFOW*
Declare PtrSafe Function SHBrowseForFolderW Lib "shell32" ( _
    ByVal lpbi As LongPtr) As LongPtr
' Unicode(W): 文字列は ByVal As LongPtr とし StrPtr(unicodeStr) を渡す
' VBA7前提(PtrSafe)。32bit Office では LongPtr→Long。Integer=16bit / Long=32bit / LongLong=64bit。
import ctypes
from ctypes import wintypes

SHBrowseForFolderW = ctypes.windll.shell32.SHBrowseForFolderW
SHBrowseForFolderW.restype = ctypes.c_void_p
SHBrowseForFolderW.argtypes = [
    ctypes.c_void_p,  # lpbi : BROWSEINFOW*
]
require 'fiddle'
require 'fiddle/import'

lib = Fiddle.dlopen('SHELL32.dll')
SHBrowseForFolderW = Fiddle::Function.new(
  lib['SHBrowseForFolderW'],
  [
    Fiddle::TYPE_VOIDP,  # lpbi : BROWSEINFOW*
  ],
  Fiddle::TYPE_VOIDP)
# Wide strings: pass str.encode("UTF-16LE") + "\x00\x00"
#[link(name = "shell32")]
extern "system" {
    fn SHBrowseForFolderW(
        lpbi: *mut BROWSEINFOW  // BROWSEINFOW*
    ) -> *mut ITEMIDLIST;
}
// crates: windows-sys provides ready-made bindings for this API.
$sig = @"
[DllImport("SHELL32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SHBrowseForFolderW(IntPtr lpbi);
"@
$api = Add-Type -MemberDefinition $sig -Name 'SHELL32_SHBrowseForFolderW' -Namespace Win32 -PassThru
# $api::SHBrowseForFolderW(lpbi)
#uselib "SHELL32.dll"
#func global SHBrowseForFolderW "SHBrowseForFolderW" wptr
; SHBrowseForFolderW varptr(lpbi)   ; 戻り値は stat
; lpbi : BROWSEINFOW* -> "wptr"
; ※HSP3.7は #func のため戻り値はシステム変数 stat に格納されます。
出力引数:
#uselib "SHELL32.dll"
#cfunc global SHBrowseForFolderW "SHBrowseForFolderW" var
; res = SHBrowseForFolderW(lpbi)
; lpbi : BROWSEINFOW* -> "var"
; ※出力/バッファ引数は var 方式(変数を直接渡す)。varptr 方式にも切替可。
出力引数:
; ITEMIDLIST* SHBrowseForFolderW(BROWSEINFOW* lpbi)
#uselib "SHELL32.dll"
#cfunc global SHBrowseForFolderW "SHBrowseForFolderW" var
; res = SHBrowseForFolderW(lpbi)
; lpbi : BROWSEINFOW* -> "var"
; ※出力/バッファ引数は var 方式(変数を直接渡す)。varptr 方式にも切替可。
import (
	"golang.org/x/sys/windows"
	"unsafe"
)

var (
	shell32 = windows.NewLazySystemDLL("SHELL32.dll")
	procSHBrowseForFolderW = shell32.NewProc("SHBrowseForFolderW")
)

// lpbi (BROWSEINFOW*)
r1, _, err := procSHBrowseForFolderW.Call(
	uintptr(lpbi),
)
_ = err  // syscall.Errno (valid when the call sets last-error)
_ = r1   // ITEMIDLIST*
function SHBrowseForFolderW(
  lpbi: Pointer   // BROWSEINFOW*
): Pointer; stdcall;
  external 'SHELL32.dll' name 'SHBrowseForFolderW';
result := DllCall("SHELL32\SHBrowseForFolderW"
    , "Ptr", lpbi   ; BROWSEINFOW*
    , "Ptr")   ; return: ITEMIDLIST*
●SHBrowseForFolderW(lpbi) = DLL("SHELL32.dll", "void* SHBrowseForFolderW(void*)")
# 呼び出し: SHBrowseForFolderW(lpbi)
# lpbi : BROWSEINFOW* -> "void*"
# なでしこ1は32bit・ANSI(Shift_JIS)。文字列=char*(ANSI)、ポインタ/ハンドル=void*(4byte)。
# ※-W(Unicode)関数。なでしこ1はANSIのため -A 版の利用を推奨。
const std = @import("std");

extern "shell32" fn SHBrowseForFolderW(
    lpbi: [*c]BROWSEINFOW // BROWSEINFOW*
) callconv(std.os.windows.WINAPI) [*c]ITEMIDLIST;
// Unicode(-W): UTF-16LE のヌル終端バッファ([*c]const u16)を渡す。
proc SHBrowseForFolderW(
    lpbi: ptr BROWSEINFOW  # BROWSEINFOW*
): ptr ITEMIDLIST {.importc: "SHBrowseForFolderW", stdcall, dynlib: "SHELL32.dll".}
# Unicode(-W): WideCString は newWideCString("...") で生成。
pragma(lib, "shell32");
extern(Windows)
ITEMIDLIST* SHBrowseForFolderW(
    BROWSEINFOW* lpbi   // BROWSEINFOW*
);
ccall((:SHBrowseForFolderW, "SHELL32.dll"), stdcall, Ptr{ITEMIDLIST},
      (Ptr{BROWSEINFOW},),
      lpbi)
# lpbi : BROWSEINFOW* -> Ptr{BROWSEINFOW}
# stdcall は 32bit のみ意味を持つ(x64 では無視)。
# Unicode(-W): Cwstring には transcode(UInt16, "...") 等で UTF-16 を渡す。
local ffi = require("ffi")
ffi.cdef[[
void* SHBrowseForFolderW(
    void* lpbi);
]]
local shell32 = ffi.load("shell32")
-- shell32.SHBrowseForFolderW(lpbi)
-- lpbi : BROWSEINFOW*
-- 構造体/GUIDへのポインタは cdef が通るよう void* で表記(実型は各引数コメント参照)。値渡し構造体・enum は対応する typedef を cdef に追加すること。
-- Unicode(-W): uint16_t* には UTF-16LE のバッファ(ffi.new("uint16_t[?]", ...))を渡す。
const koffi = require('koffi');
const lib = koffi.load('SHELL32.dll');
const SHBrowseForFolderW = lib.func('__stdcall', 'SHBrowseForFolderW', 'void *', ['void *']);
// SHBrowseForFolderW(lpbi)
// lpbi : BROWSEINFOW* -> 'void *'
// 出力ポインタは koffi.out(...) で包む。構造体は koffi.struct で定義。
const lib = Deno.dlopen("SHELL32.dll", {
  SHBrowseForFolderW: { parameters: ["pointer"], result: "pointer" },
});
// lib.symbols.SHBrowseForFolderW(lpbi)
// lpbi : BROWSEINFOW* -> "pointer"
// 文字列は "buffer"。Unicode(-W) は new TextEncoder() ではなく UTF-16LE のバイト列(末尾に \x00\x00)を Uint8Array で渡す。
// 値渡し構造体は { struct: [ ...field types... ] } を使用。
<?php
$ffi = FFI::cdef(<<<C
void* SHBrowseForFolderW(
    void* lpbi);
C, "SHELL32.dll");
// $ffi->SHBrowseForFolderW(lpbi);
// lpbi : BROWSEINFOW*
// 構造体/GUIDへのポインタは cdef が通るよう void* で表記(実型は各引数コメント参照)。値渡し構造体・enum は対応する typedef を cdef に追加すること。
// WINAPI(stdcall): x64 では呼出規約が統一されるため問題なし。x86 では __stdcall 対応のラッパが必要な場合あり。
import com.sun.jna.*;
import com.sun.jna.ptr.*;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

public interface Shell32 extends StdCallLibrary {
    Shell32 INSTANCE = Native.load("shell32", Shell32.class, W32APIOptions.UNICODE_OPTIONS);
    Pointer SHBrowseForFolderW(
        Pointer lpbi   // BROWSEINFOW*
    );
}
// Unicode(-W): WString(入力)/char[](出力)で UTF-16 をマーシャリング。
@[Link("shell32")]
lib LibSHELL32
  fun SHBrowseForFolderW = SHBrowseForFolderW(
    lpbi : BROWSEINFOW*   # BROWSEINFOW*
  ) : ITEMIDLIST*
end
# 構造体/GUID/enum は lib 内に対応する型定義が必要。
# 呼出規約: x64 は規約統一のため OK。x86(32bit)は WINAPI=stdcall だが Crystal の fun に stdcall 付与構文がなく非対応。
import 'dart:ffi';
import 'package:ffi/ffi.dart';

typedef SHBrowseForFolderWNative = Pointer<Void> Function(Pointer<Void>);
typedef SHBrowseForFolderWDart = Pointer<Void> Function(Pointer<Void>);
final SHBrowseForFolderW = DynamicLibrary.open('SHELL32.dll')
    .lookupFunction<SHBrowseForFolderWNative, SHBrowseForFolderWDart>('SHBrowseForFolderW');
// lpbi : BROWSEINFOW* -> Pointer<Void>
// 文字列は package:ffi の "...".toNativeUtf16()/toNativeUtf8() で変換。
{$mode objfpc}{$H+}
function SHBrowseForFolderW(
  lpbi: Pointer   // BROWSEINFOW*
): Pointer; stdcall;
  external 'SHELL32.dll' name 'SHBrowseForFolderW';
import Foreign
import Foreign.C.Types
import Foreign.C.String

foreign import stdcall safe "SHBrowseForFolderW"
  c_SHBrowseForFolderW :: Ptr () -> IO (Ptr ())
-- lpbi : BROWSEINFOW* -> Ptr ()
-- 要 GHC(Windows)。stdcall は x64 では ccall として扱われる。ブロックする API は safe 呼び出し推奨。
open Ctypes
open Foreign

let shbrowseforfolderw =
  foreign "SHBrowseForFolderW"
    ((ptr void) @-> returning (ptr void))
(* lpbi : BROWSEINFOW* -> (ptr void) *)
(* foreign は cdecl 前提。x64 Windows では WINAPI と一致。構造体は ctypes structure を定義のこと。 *)
(cffi:define-foreign-library shell32 (t "SHELL32.dll"))
(cffi:use-foreign-library shell32)

(cffi:defcfun ("SHBrowseForFolderW" shbrowse-for-folder-w :convention :stdcall) :pointer
  (lpbi :pointer))   ; BROWSEINFOW*
; isize/usize(INT_PTR/SIZE_T)は x64 前提で :int64/:uint64。x86 では :int32/:uint32。
use Win32::API;
my $SHBrowseForFolderW = Win32::API::More->new('SHELL32',
    'LPVOID SHBrowseForFolderW(LPVOID lpbi)');
# my $ret = $SHBrowseForFolderW->Call($lpbi);
# lpbi : BROWSEINFOW* -> LPVOID
# 値渡し構造体は pack() した文字列、または Win32::API::Struct を使用。
# Unicode(-W): LPCWSTR/LPWSTR は Win32::API が UTF-16 変換を行う。

関連項目

文字セット違い
使用する型