SHBrowseForFolderW 函数 (shlobj_core.h)

显示允许用户选择 Shell 文件夹的对话框。

语法

PIDLIST_ABSOLUTE SHBrowseForFolderW(
  [in] LPBROWSEINFOW lpbi
);

参数

[in] lpbi

类型: LPBROWSEINFO

指向 BROWSEINFO 结构的指针,该结构包含用于显示对话框的信息。

返回值

类型: PIDLIST_ABSOLUTE

返回一个 PIDL,指定所选文件夹相对于命名空间根目录的位置。 如果用户在对话框中选择“ 取消 ”按钮,则返回值为 NULL

返回的 PIDL 可能是文件夹快捷方式而不是文件夹的。 有关此情况的完整讨论,请参阅“备注”部分。

注解

对于 Windows Vista 或更高版本,建议将 IFileDialog 与 FOS_PICKFOLDERS 选项一起使用,而不是 SHBrowseForFolder 函数。 这会在选取文件夹模式下使用“打开文件”对话框,并且是首选实现。

在调用 SHBrowseForFolder 之前,必须 (COM) 初始化组件对象模型。 如果使用 CoInitializeEx 初始化 COM,则必须在其 dwCoInit 参数中设置COINIT_APARTMENTTHREADED标志。 还可以使用 CoInitializeOleInitialize,它们始终使用单元线程。 如果需要拖放功能,建议使用 OleInitialize ,因为它初始化所需的 OLE 和 COM。

注意如果使用具有 COINIT_MULTITHREADED 标志的 CoInitializeEx 初始化 COM,则调用应用程序在 BROWSEINFO 结构中使用 BIF_USENEWUI 或 BIF_NEWDIALOGSTYLE 标志时,SHBrowseForFolder 将失败。
 
调用应用程序负责调用 CoTaskMemFree ,以在不再需要时释放 SHBrowseForFolder 返回的 IDList。

有两种可用的对话框样式。 较旧的样式默认显示,且不可调整大小。 较新的样式提供了许多附加功能,包括对话框中的拖放功能、重新排序、删除、快捷菜单、创建新文件夹的功能以及其他快捷菜单命令。 最初,它比旧对话框大,但用户可以调整其大小。 若要使用较新的样式指定对话框,请在 BROWSEINFO 结构的 ulFlags 成员中设置BIF_USENEWUI标志。

如果实现在 BROWSEINFO 结构的 lpfn 成员中指定的回调函数,则会收到对话框的句柄。 此窗口句柄的一个用途是修改对话框的布局或内容。 由于它不可调整大小,因此修改旧样式对话框相对简单。 修改较新的样式对话框要困难得多,不建议这样做。 它不仅具有与旧样式不同的大小和布局,而且每次用户调整其大小时,其控件的尺寸和位置都会发生变化。

如果在 BROWSEINFO 结构的 ulFlags 成员中设置了BIF_RETURNONLYFSDIRS标志,则“确定”按钮仍对“\server”项以及“\server\share”和目录项保持启用状态。 但是,如果用户选择“\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 及更早版本系统。 默认情况下,只要未在 BROWSEINFO 结构中设置BIF_NOTRANSLATETARGETS标志,Windows XP 及更高版本系统将返回快捷方式目标的 PIDL 而不是快捷方式本身。
 
如果 SHBrowseForFolder 将 PIDL 返回到快捷方式,则向 SHGetPathFromIDList 发送该 PIDL 将返回快捷方式本身的路径,而不是其目标的路径。 可以使用 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 版本。 将非特定编码别名与非非特定编码的代码混合使用可能会导致不匹配,从而导致编译或运行时错误。 有关详细信息,请参阅 函数原型的约定

要求

要求
最低受支持的客户端 Windows XP [仅限桌面应用]
最低受支持的服务器 Windows 2000 Server [仅限桌面应用]
目标平台 Windows
标头 shlobj_core.h (包括 Shlobj.h、Shlobj_core.h)
Library Shell32.lib
DLL Shell32.dll (4.0 或更高版本)

另请参阅

打开并另存为对话框