SHBrowseForFolderA 函数 (shlobj_core.h)

显示一个对话框,使用户能够选择 Shell 文件夹。

语法

PIDLIST_ABSOLUTE SHBrowseForFolderA(
  [in] LPBROWSEINFOA 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。

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

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

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

如果在 BROWSEINFO 结构的 ulFlags 成员中设置了BIF_RETURNONLYFSDIRS标志,则“\server”项以及“\server\share”和目录项将保持启用“确定”按钮 确定 按钮。 但是,如果用户选择“\server”项,则传递由 SHBrowseForFolder 返回的 PIDLSHGetPathFromIDList 失败。

自定义筛选

从 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 和更早版本的系统。 默认情况下,只要BIF_NOTRANSLATETARGETS标志未在 BROWSEINFO 结构中设置,Windows XP 和更高版本的系统将返回快捷方式目标的 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 版本。 将中性编码别名与不中性编码的代码混合使用可能会导致编译或运行时错误不匹配。 有关详细信息,请参阅函数原型的 约定。

要求

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

另请参阅

打开并另存为对话框