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

SHBrowseForFolderA

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

シグネチャ

// SHELL32.dll  (ANSI / -A)
#include <windows.h>

ITEMIDLIST* SHBrowseForFolderA(
    BROWSEINFOA* lpbi
);

パラメーター

名前方向説明
lpbiBROWSEINFOA*inダイアログ ボックスの表示に使用する情報を格納した BROWSEINFO 構造体へのポインター。

戻り値の型: ITEMIDLIST*

公式ドキュメント

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

戻り値

型: 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. 作成した IFolderFilter へのポインターを渡して IFolderFilterSite::SetFilter を呼び出します。その後、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 ヘッダーは、SHBrowseForFolder を、UNICODE プリプロセッサ定数の定義に基づいてこの関数の ANSI 版または Unicode 版を自動的に選択するエイリアスとして定義します。エンコーディング非依存のエイリアスの使用を、エンコーディング非依存でないコードと混在させると、不一致が生じ、コンパイル エラーや実行時エラーの原因となる場合があります。詳細については、「Conventions for Function Prototypes」を参照してください。

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

各言語での呼び出し定義

// SHELL32.dll  (ANSI / -A)
#include <windows.h>

ITEMIDLIST* SHBrowseForFolderA(
    BROWSEINFOA* lpbi
);
[DllImport("SHELL32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
static extern IntPtr SHBrowseForFolderA(
    IntPtr lpbi   // BROWSEINFOA*
);
<DllImport("SHELL32.dll", CharSet:=CharSet.Ansi, ExactSpelling:=True)>
Public Shared Function SHBrowseForFolderA(
    lpbi As IntPtr   ' BROWSEINFOA*
) As IntPtr
End Function
' lpbi : BROWSEINFOA*
Declare PtrSafe Function SHBrowseForFolderA Lib "shell32" ( _
    ByVal lpbi As LongPtr) As LongPtr
' VBA7前提(PtrSafe)。32bit Office では LongPtr→Long。Integer=16bit / Long=32bit / LongLong=64bit。
import ctypes
from ctypes import wintypes

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

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

var (
	shell32 = windows.NewLazySystemDLL("SHELL32.dll")
	procSHBrowseForFolderA = shell32.NewProc("SHBrowseForFolderA")
)

// lpbi (BROWSEINFOA*)
r1, _, err := procSHBrowseForFolderA.Call(
	uintptr(lpbi),
)
_ = err  // syscall.Errno (valid when the call sets last-error)
_ = r1   // ITEMIDLIST*
function SHBrowseForFolderA(
  lpbi: Pointer   // BROWSEINFOA*
): Pointer; stdcall;
  external 'SHELL32.dll' name 'SHBrowseForFolderA';
result := DllCall("SHELL32\SHBrowseForFolderA"
    , "Ptr", lpbi   ; BROWSEINFOA*
    , "Ptr")   ; return: ITEMIDLIST*
●SHBrowseForFolderA(lpbi) = DLL("SHELL32.dll", "void* SHBrowseForFolderA(void*)")
# 呼び出し: SHBrowseForFolderA(lpbi)
# lpbi : BROWSEINFOA* -> "void*"
# なでしこ1は32bit・ANSI(Shift_JIS)。文字列=char*(ANSI)、ポインタ/ハンドル=void*(4byte)。
const std = @import("std");

extern "shell32" fn SHBrowseForFolderA(
    lpbi: [*c]BROWSEINFOA // BROWSEINFOA*
) callconv(std.os.windows.WINAPI) [*c]ITEMIDLIST;
proc SHBrowseForFolderA(
    lpbi: ptr BROWSEINFOA  # BROWSEINFOA*
): ptr ITEMIDLIST {.importc: "SHBrowseForFolderA", stdcall, dynlib: "SHELL32.dll".}
pragma(lib, "shell32");
extern(Windows)
ITEMIDLIST* SHBrowseForFolderA(
    BROWSEINFOA* lpbi   // BROWSEINFOA*
);
ccall((:SHBrowseForFolderA, "SHELL32.dll"), stdcall, Ptr{ITEMIDLIST},
      (Ptr{BROWSEINFOA},),
      lpbi)
# lpbi : BROWSEINFOA* -> Ptr{BROWSEINFOA}
# stdcall は 32bit のみ意味を持つ(x64 では無視)。
local ffi = require("ffi")
ffi.cdef[[
void* SHBrowseForFolderA(
    void* lpbi);
]]
local shell32 = ffi.load("shell32")
-- shell32.SHBrowseForFolderA(lpbi)
-- lpbi : BROWSEINFOA*
-- 構造体/GUIDへのポインタは cdef が通るよう void* で表記(実型は各引数コメント参照)。値渡し構造体・enum は対応する typedef を cdef に追加すること。
const koffi = require('koffi');
const lib = koffi.load('SHELL32.dll');
const SHBrowseForFolderA = lib.func('__stdcall', 'SHBrowseForFolderA', 'void *', ['void *']);
// SHBrowseForFolderA(lpbi)
// lpbi : BROWSEINFOA* -> 'void *'
// 出力ポインタは koffi.out(...) で包む。構造体は koffi.struct で定義。
const lib = Deno.dlopen("SHELL32.dll", {
  SHBrowseForFolderA: { parameters: ["pointer"], result: "pointer" },
});
// lib.symbols.SHBrowseForFolderA(lpbi)
// lpbi : BROWSEINFOA* -> "pointer"
// 文字列は "buffer"。ANSI(-A) は new TextEncoder() で UTF-8/ANSI バイト列(末尾に \x00)を渡す。
// 値渡し構造体は { struct: [ ...field types... ] } を使用。
<?php
$ffi = FFI::cdef(<<<C
void* SHBrowseForFolderA(
    void* lpbi);
C, "SHELL32.dll");
// $ffi->SHBrowseForFolderA(lpbi);
// lpbi : BROWSEINFOA*
// 構造体/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.ASCII_OPTIONS);
    Pointer SHBrowseForFolderA(
        Pointer lpbi   // BROWSEINFOA*
    );
}
@[Link("shell32")]
lib LibSHELL32
  fun SHBrowseForFolderA = SHBrowseForFolderA(
    lpbi : BROWSEINFOA*   # BROWSEINFOA*
  ) : ITEMIDLIST*
end
# 構造体/GUID/enum は lib 内に対応する型定義が必要。
# 呼出規約: x64 は規約統一のため OK。x86(32bit)は WINAPI=stdcall だが Crystal の fun に stdcall 付与構文がなく非対応。
import 'dart:ffi';
import 'package:ffi/ffi.dart';

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

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

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

(cffi:defcfun ("SHBrowseForFolderA" shbrowse-for-folder-a :convention :stdcall) :pointer
  (lpbi :pointer))   ; BROWSEINFOA*
; isize/usize(INT_PTR/SIZE_T)は x64 前提で :int64/:uint64。x86 では :int32/:uint32。
use Win32::API;
my $SHBrowseForFolderA = Win32::API::More->new('SHELL32',
    'LPVOID SHBrowseForFolderA(LPVOID lpbi)');
# my $ret = $SHBrowseForFolderA->Call($lpbi);
# lpbi : BROWSEINFOA* -> LPVOID
# 値渡し構造体は pack() した文字列、または Win32::API::Struct を使用。

関連項目

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