场景

1.起因是创建标准选择目录对话框时使用了 SHCreateItemFromParsingName 函数, 这个函数支持vista以上系统. 之后再winxp上运行就报错:

无法定位程序输入点 SHCreateItemFromParsingName 于动态链接库 SHELL32.dll 上.

static wchar_t* SelectFolder2(HWND hParent,const wchar_t* current_dir)
{
    CComPtr<IFileOpenDialog> dialog;
    HRESULT hr = dialog.CoCreateInstance(__uuidof(FileOpenDialog));
    if(FAILED(hr) || dialog == NULL)
        return NULL;  

    CComPtr<IShellItem> shellItem;

    hr = ::SHCreateItemFromParsingName(current_dir, 0, IID_IShellItem, reinterpret_cast<void**>(&shellItem));
    dialog->SetFolder(shellItem);
    dialog->SetOptions(FOS_PICKFOLDERS);
    hr = dialog->Show(hParent);
    if(S_OK == hr)
    {
        CComPtr<IShellItem> itemResult;
        hr = dialog->GetResult(&itemResult);  

        WCHAR* buffer = NULL;
        hr = itemResult->GetDisplayName(SIGDN_FILESYSPATH, &buffer);
        if(SUCCEEDED(hr) && buffer != NULL)
        {
            std::wstring temp;
            temp.append(buffer);
            if(buffer[wcslen(buffer)-1]!=L'\\')
            {
                temp.append(L"\\");
            }
            CoTaskMemFree(buffer);
            return BASUtilityString::Wcsdup(temp.c_str());
        }
        return NULL;
    }
    return NULL;
}  

2.如果使用SHBrowseForFolder来创建目录会有一个BUG,就是创建的目录不会及时刷新, 也获取不到输入的目录名, 导致在使用这个意味存在的目录时会有问题. 所以在win7上改成了使用IFileOpenDialog创建目录的方式. 如果有在winxp,win7上都通用的方式请留言告诉我.

说明

1.解决兼容winxp, win7的办法是不要使用 SHCreateItemFromParsingName 函数, 或者在判断是win7时才使用LoadLibrary调用SHCreateItemFromParsingName(这个办法未验证过). 下边是通过使用Winxp API SHParseDisplayName来获取IShellItem, 并接收 具备记录功能的 IBindCtx 类型作为参数. 有时候真想说微软有些细节真做不好.

2.注意CComPtr的使用方式, 和之前说的shared_ptr一样的原理, 不需要过多的if嵌套语句了. 使用智能指针的方式释放malloc出来的堆空间


static wchar_t* SelectFolder2(HWND hParent,const wchar_t* current_dir)
{
    wchar_t* folder = NULL;
    CComPtr<IBindCtx> pbcItemContext;   // this will be used by the data source for caching
    HRESULT hr = CreateBindCtx(0, &pbcItemContext);
    if (FAILED(hr))
    {
        std::cout << "CreateBindCtx" << std::endl;
        return NULL;
    }

    CComPtr<IBindCtx> pbcParse;
    hr = CreateBindCtxWithParam(STR_ITEM_CACHE_CONTEXT, pbcItemContext, &pbcParse);
    if (FAILED(hr))
    {
        std::cout << "CreateBindCtxWithParam" << std::endl;
        return NULL;
    }

    CComPtr<IFileSystemBindData2> pfsbd;
    hr = CreateFileSystemBindData(&pfsbd);
    if (FAILED(hr))
    {
        std::cout << "CreateFileSystemBindData" << std::endl;
        return NULL;
    }

    hr = pbcParse->RegisterObjectParam(STR_FILE_SYS_BIND_DATA, pfsbd);
    if (FAILED(hr))
    {
        std::cout << "RegisterObjectParam" << std::endl;
        return NULL;
    }

    WIN32_FIND_DATA fd1 = {};   // a file
    hr = pfsbd->SetFindData(&fd1);

    CComPtr<IFileOpenDialog> dialog;
    hr = dialog.CoCreateInstance(__uuidof(FileOpenDialog));
    if(FAILED(hr) || dialog == NULL)
    {
        std::cout << "dialog.CoCreateInstance: " << GetLastError() << std::endl;
        return NULL;
    }

    CComPtr<IShellItem> shellItem;
    PIDLIST_ABSOLUTE pidl;

    static const wchar_t* kEmtpy = L"C:\\asdz1";
    HRESULT hresult = ::SHParseDisplayName((current_dir)?current_dir:kEmtpy, pbcParse, &pidl, SFGAO_FOLDER, 0);
    if (FAILED(hresult))
    {
        std::cout << "SHParseDisplayName" << std::endl;
        return NULL;
    }

    std::shared_ptr<ITEMIDLIST> wm_ptr(pidl,[](PIDLIST_ABSOLUTE pidl)
    {
        ILFree(pidl);
    });

    hresult = ::SHCreateShellItem(NULL, NULL, pidl, &shellItem);
    if (FAILED(hresult))
    {
        std::cout << "SHCreateShellItem" << std::endl;
        return NULL;
    }

    dialog->SetFolder(shellItem);
    dialog->SetOptions(FOS_PICKFOLDERS);
    hr = dialog->Show(hParent);
    if(S_OK == hr)
    {
        CComPtr<IShellItem> itemResult;
        hr = dialog->GetResult(&itemResult);  

        WCHAR* buffer = NULL;
        hr = itemResult->GetDisplayName(SIGDN_FILESYSPATH, &buffer);
        if(SUCCEEDED(hr) && buffer != NULL)
        {
            std::wstring temp;
            temp.append(buffer);
            if(buffer[wcslen(buffer)-1]!=L'\\')
            {
                temp.append(L"\\");
            }
            CoTaskMemFree(buffer);
            folder = BASUtilityString::Wcsdup(temp.c_str());
        }
    }
    return folder;
}

ParsingWithParameters.cpp

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved

#include "ShellHelpers.h"
#include <strsafe.h>
#include <new>

// file system bind data is a parameter passed to IShellFolder::ParseDisplayName() to
// provide the item information to the file system data source. this will enable
// parsing of items that do not exist and avoiding accessing the disk in the parse operation

// {fc0a77e6-9d70-4258-9783-6dab1d0fe31e}
static const CLSID CLSID_UnknownJunction = { 0xfc0a77e6, 0x9d70, 0x4258, {0x97, 0x83, 0x6d, 0xab, 0x1d, 0x0f, 0xe3, 0x1e} };

class CFileSysBindData : public IFileSystemBindData2
{
public:
    CFileSysBindData() : _cRef(1), _clsidJunction(CLSID_UnknownJunction)
    {
        ZeroMemory(&_fd, sizeof(_fd));
        ZeroMemory(&_liFileID, sizeof(_liFileID));
    }

    // IUnknown
    IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        static const QITAB qit[] = {
            QITABENT(CFileSysBindData, IFileSystemBindData),
            QITABENT(CFileSysBindData, IFileSystemBindData2),
            { 0 },
        };
        return QISearch(this, qit, riid, ppv);
    }

    IFACEMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&_cRef);
    }

    IFACEMETHODIMP_(ULONG) Release()
    {
        long cRef = InterlockedDecrement(&_cRef);
        if (!cRef)
            delete this;
        return cRef;
    }

    // IFileSystemBindData
    IFACEMETHODIMP SetFindData(const WIN32_FIND_DATAW *pfd)
    {
        _fd = *pfd;
        return S_OK;
    }

    IFACEMETHODIMP GetFindData(WIN32_FIND_DATAW *pfd)
    {
        *pfd = _fd;
        return S_OK;
    }

    // IFileSystemBindData2
    IFACEMETHODIMP SetFileID(LARGE_INTEGER liFileID)
    {
        _liFileID = liFileID;
        return S_OK;
    }

    IFACEMETHODIMP GetFileID(LARGE_INTEGER *pliFileID)
    {
        *pliFileID = _liFileID;
        return S_OK;
    }

    IFACEMETHODIMP SetJunctionCLSID(REFCLSID clsid)
    {
        _clsidJunction = clsid;
       return S_OK;
    }

    IFACEMETHODIMP GetJunctionCLSID(CLSID *pclsid)
    {
        HRESULT hr;
        if (CLSID_UnknownJunction == _clsidJunction)
        {
            *pclsid = CLSID_NULL;
            hr = E_FAIL;
        }
        else
        {
            *pclsid = _clsidJunction;   // may be CLSID_NULL (no junction handler case)
            hr = S_OK;
        }
        return hr;
    }

private:
    long _cRef;
    WIN32_FIND_DATAW _fd;
    LARGE_INTEGER _liFileID;
    CLSID _clsidJunction;
};

HRESULT CreateFileSystemBindData(IFileSystemBindData2 **ppfsbd)
{
    *ppfsbd = new (std::nothrow) CFileSysBindData();
    return *ppfsbd ? S_OK : E_OUTOFMEMORY;
}

// "simple parsing" allows you to pass the WIN32_FILE_DATA to the file system data source
// to avoid it having to access the file. this avoids the expense of getting the
// information from the file and allows you to parse items that may not necessarily exist
//
// the find data is passed to the data source via the bind context constructed here

HRESULT CreateFileSysBindCtx(const WIN32_FIND_DATAW *pfd, IBindCtx **ppbc)
{
    *ppbc = NULL;

    IFileSystemBindData2 *pfsbd;
    HRESULT hr = CreateFileSystemBindData(&pfsbd);
    if (SUCCEEDED(hr))
    {
        hr = pfsbd->SetFindData(pfd);
        if (SUCCEEDED(hr))
        {
            IBindCtx *pbc;
            hr = CreateBindCtx(0, &pbc);
            if (SUCCEEDED(hr))
            {
                BIND_OPTS bo = {sizeof(bo), 0, STGM_CREATE, 0};
                hr = pbc->SetBindOptions(&bo);
                if (SUCCEEDED(hr))
                {
                    hr = pbc->RegisterObjectParam(STR_FILE_SYS_BIND_DATA, pfsbd);
                    if (SUCCEEDED(hr))
                    {
                        hr = pbc->QueryInterface(IID_PPV_ARGS(ppbc));
                    }
                }
                pbc->Release();
            }
        }
        pfsbd->Release();
    }
    return hr;
}

class CDummyUnknown : public IPersist
{
public:
    CDummyUnknown(REFCLSID clsid) : _cRef(1), _clsid(clsid)
    {
    }

    // IUnknown
    IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        static const QITAB qit[] =
        {
            QITABENT(CDummyUnknown, IPersist),
            { 0 },
        };
        return QISearch(this, qit, riid, ppv);
    }

    IFACEMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&_cRef);
    }

    IFACEMETHODIMP_(ULONG) Release()
    {
        long cRef = InterlockedDecrement(&_cRef);
        if (!cRef)
            delete this;
        return cRef;
    }

    // IPersist
    IFACEMETHODIMP GetClassID(CLSID *pclsid)
    {
        *pclsid = _clsid;
        return S_OK;
    }

private:
    long _cRef;
    CLSID _clsid;
};

// create a bind context with a named object

HRESULT CreateBindCtxWithParam(PCWSTR pszParam, IUnknown *punk, IBindCtx **ppbc)
{
    *ppbc = NULL;

    HRESULT hr = CreateBindCtx(0, ppbc);
    if (SUCCEEDED(hr))
    {
        hr = (*ppbc)->RegisterObjectParam(const_cast<PWSTR>(pszParam), punk);
        if (FAILED(hr))
        {
            (*ppbc)->Release();
            *ppbc = NULL;
        }
    }
    return hr;
}

// create a bind context with a dummy unknown parameter that is used to pass flag values
// to operations that accept bind contexts

HRESULT CreateBindCtxWithParam(PCWSTR pszParam, IBindCtx **ppbc)
{
    *ppbc = NULL;

    IUnknown *punk = new (std::nothrow) CDummyUnknown(CLSID_NULL);
    HRESULT hr = punk ? S_OK : E_OUTOFMEMORY;
    if (SUCCEEDED(hr))
    {
        hr = CreateBindCtxWithParam(pszParam, punk, ppbc);
        punk->Release();
    }
    return hr;
}

// STR_FILE_SYS_BIND_DATA and IFileSystemBindData enable passing the file system information
// that the file system data source needs to perform a parse. this eliminates
// the IO that results when parsing an item and lets items that don't exist to be parsed.
// the helper CreateFileSysBindCtx() internally implements IFileSystemBindData and
// stores an object in the bind context with thh WIN32_FIND_DATA that it is provided

void DemonstrateFileSystemParsingParameters()
{
    WIN32_FIND_DATA fd = {};
    fd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;    // a file (not a folder)
    fd.nFileSizeLow  = (DWORD)-1; // file size is null or an unknown value
    fd.nFileSizeHigh = (DWORD)-1;

    IBindCtx *pbcParse;
    HRESULT hr = CreateFileSysBindCtx(&fd, &pbcParse);
    if (SUCCEEDED(hr))
    {
        // this item does not exist, but it can be parsed given the
        // parameter provided in the bind context

        IShellItem2 *psi;
        hr = SHCreateItemFromParsingName(L"c:\\a.txt", pbcParse, IID_PPV_ARGS(&psi));
        if (SUCCEEDED(hr))
        {
            psi->Release();
        }

        // this is useful for resources on the network where the IO
        // in these cases is more costly
        hr = SHCreateItemFromParsingName(L"\\\\Server\\Share\\file.txt", pbcParse, IID_PPV_ARGS(&psi));
        if (SUCCEEDED(hr))
        {
            psi->Release();
        }
        pbcParse->Release();
    }
}

// STR_ITEM_CACHE_CONTEXT provides a context that can be used for caching to a
// data source to speed up parsing of multiple items. the file system data source
// uses this so any clients that will be parsing multiple items should provide
// this to speed up the parsing function.
//
// the cache context object is itself a bind context stored in the bind context under
// the name STR_ITEM_CACHE_CONTEXT passed to the parse operation via the bind
// context that it is stored in

void DemonstrateParsingItemCacheContext()
{
    IBindCtx *pbcItemContext;   // this will be used by the data source for caching
    HRESULT hr = CreateBindCtx(0, &pbcItemContext);
    if (SUCCEEDED(hr))
    {
        IBindCtx *pbcParse; // passed to the parse
        hr = CreateBindCtxWithParam(STR_ITEM_CACHE_CONTEXT, pbcItemContext, &pbcParse);
        if (SUCCEEDED(hr))
        {
            IFileSystemBindData2 *pfsbd;
            hr = CreateFileSystemBindData(&pfsbd);
            if (SUCCEEDED(hr))
            {
                hr = pbcParse->RegisterObjectParam(STR_FILE_SYS_BIND_DATA, pfsbd);
                if (SUCCEEDED(hr))
                {
                    // do lots of parsing here passing pbcParse each time

                    WIN32_FIND_DATA fd1 = {};   // a file
                    hr = pfsbd->SetFindData(&fd1);
                    if (SUCCEEDED(hr))
                    {
                        IShellItem2 *psi;
                        hr = SHCreateItemFromParsingName(L"C:\\folder\\file.txt", pbcParse, IID_PPV_ARGS(&psi));
                        if (SUCCEEDED(hr))
                        {
                            psi->Release();
                        }
                    }

                    WIN32_FIND_DATA fd2 = {};   // a file
                    hr = pfsbd->SetFindData(&fd2);
                    if (SUCCEEDED(hr))
                    {
                        IShellItem2 *psi;
                        hr = SHCreateItemFromParsingName(L"C:\\folder\\file.doc", pbcParse, IID_PPV_ARGS(&psi));
                        if (SUCCEEDED(hr))
                        {
                            psi->Release();
                        }
                    }
                }
                pfsbd->Release();
            }
            pbcParse->Release();
        }
        pbcItemContext->Release();
    }
}

// STR_PARSE_PREFER_FOLDER_BROWSING indicates that an item referenced via an http or https
// protocol should be parsed using the file system data source that supports such items
// via the WebDAV redirector. the default parsing these name forms is handled by
// the internet data source. this option lets you select the file system data source instead.
//
// note, unlike the internet data source the file system parsing operation verifies the
// resource is accessable (issuing IOs to the file system) so these will be slower
// than the default parsing behavior.
//
// providing this enables accessing these items as file system items using the file system
// data source getting the behavior you would if you provided a file system path (UNC in this case)

void DemonstratePreferFolderBrowsingParsing()
{
    IBindCtx *pbcParse;
    HRESULT hr = CreateBindCtxWithParam(STR_PARSE_PREFER_FOLDER_BROWSING, &pbcParse);
    if (SUCCEEDED(hr))
    {
        IShellItem2 *psi;
        hr = SHCreateItemFromParsingName(L"http://unknownserver/abc/file.extension", pbcParse, IID_PPV_ARGS(&psi));
        if (SUCCEEDED(hr))
        {
            // the network path is valid
            SFGAOF sfgaof;
            hr = psi->GetAttributes(SFGAO_FILESYSTEM, &sfgaof); // will return SFGAO_FILESYSTEM
            psi->Release();
        }

        // in combination with the file system bind context data this avoids the IO
        // and still parses the item as a file system item

        IFileSystemBindData2 *pfsbd;
        hr = CreateFileSystemBindData(&pfsbd);
        if (SUCCEEDED(hr))
        {
            hr = pbcParse->RegisterObjectParam(STR_FILE_SYS_BIND_DATA, pfsbd);
            if (SUCCEEDED(hr))
            {
                WIN32_FIND_DATA fd = {};
                fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; // this is a folder

                hr = pfsbd->SetFindData(&fd);
                if (SUCCEEDED(hr))
                {
                    IShellItem2 *psi;
                    hr = SHCreateItemFromParsingName(L"http://unknownserver/dav/folder", pbcParse, IID_PPV_ARGS(&psi));
                    if (SUCCEEDED(hr))
                    {
                        SFGAOF sfgaof;
                        hr = psi->GetAttributes(SFGAO_FILESYSTEM, &sfgaof); // will return SFGAO_FILESYSTEM
                        psi->Release();
                    }
                }
            }
            pfsbd->Release();
        }
        pbcParse->Release();
    }
}

int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        DemonstrateFileSystemParsingParameters();
        DemonstrateParsingItemCacheContext();
        DemonstratePreferFolderBrowsingParsing();
        CoUninitialize();
    }
    return 0;
}

参考

[ParsingWithParameters.cpp](C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\winui\shell\appplatform\ParsingWithParameters\ParsingWithParameters.cpp)

SHParseDisplayName when path doesn’t exists

Replacement for ::SHCreateItemFromParsingName() on Windows XP

[ATL/WTL]_[初级]_[选择目录对话框]的更多相关文章

  1. MFC_选择目录对话框_选择文件对话框_指定目录遍历文件

    选择目录对话框 void C资源共享吧视频广告清理工具Dlg::OnBnClickedCls() { // 清空编辑框内容 m_Edit.SetWindowTextW(L""); ...

  2. CE选择目录对话框(转)

    本文转载于http://blog.163.com/zhaojun_xf/blog/static/30050580201132221118479/ 在Wince下要打开目录对话框需要调用函数SHBrow ...

  3. [MFC]选择目录对话框和选择文件对话框

    在MFC编程中经常会需要用到选择目录和选择文件的界面,以下总结一下本人常用的这两种对话框的生成方法: 选择目录对话框 //选择目录按钮void CDcPackerDlg::OnBnClickedDec ...

  4. [MFC]选择目录对话框和选择文件对话框 [转]

      在MFC编程中经常会需要用到选择目录和选择文件的界面,以下总结一下本人常用的这两种对话框的生成方法: 选择目录对话框 {    char szPath[MAX_PATH];     //存放选择的 ...

  5. 如何在VBS脚本中显示“选择文件对话框”或“选择目录对话框”

    .选择文件[XP操作系统,不能用于Win2000或98],使用“UserAccounts.CommonDialog”对象向用户显示一个标准的“文件打开”对话框 Set objDialog = Crea ...

  6. [ATL/WTL]_[初级]_[关于graphics.DrawImage绘图时显示不正常的问题]

    场景 1.使用win32绘图时, 最简单的api是使用 graphics.DrawImage(image,x,y)来绘制, 可是这个api有个坑,它的图片显示完整和设备分辨率(显卡)有关. 说明 1. ...

  7. [C/C++标准库]_[初级]_[转换UTC时间到local本地时间]

    场景 1.如果有面向全球用户的网站, 一般在存储时间数据时存储的是UTC格式的时间, 这样时间是统一的, 并可以根据当地时区来进行准确的转换. 2.存储本地时间的问题就在于如果换了时区, 那么显示的时 ...

  8. [Zlib]_[初级]_[使用zlib库压缩和解压STL string]

    场景 1.一般在使用文本json传输数据, 数据量特别大时,传输的过程就特别耗时, 因为带宽或者socket的缓存是有限制的, 数据量越大, 传输时间就越长. 网站一般使用gzip来压缩成二进制. 说 ...

  9. [C/C++11]_[初级]_[std::bind介绍和使用]

    场景 1.C++11 引入了std::function 对象, 这个对象可以通过std::bind封装所有的函数, 并通过代理调用这个std::function的方式调用这个函数. 比如通过统一的方式 ...

随机推荐

  1. Oracle EBS 请求参数关联

  2. iOS设计模式 - 访问者

    iOS设计模式 - 访问者 原理图 说明 表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 1.Visitor 抽象访问者角色,为该对象结构中具 ...

  3. Shell中, 退出整个脚本

    常规做法 cat >test.sh<<EOF'' #!/bin/bash exit_script(){ exit 1 } echo "before exit" e ...

  4. DXperience 工具箱不显示/ Visual Studio 2012选择项打开崩溃

    1.移除NetFx40_LegacySecurityPolicy  节: 移除C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\I ...

  5. 从0开始搭建Element项目

    第一步:安装 Node.js/NPM 下载Node.js:https://nodejs.org/zh-cn/download/ 下载安装即可. 第二步:安装 vue-cli 打开 cmd 创建,在命令 ...

  6. css背景精华所在+前端页面开发流程

    background属性 background属性是css中应用比较多,且比较重要的一个属性,它是负责给盒子设置背景图片和背景颜色的,background是一个复合属性,它可以分解成如下几个设置项: ...

  7. Oracle 空间查询, 数据类型为 sdo_geometry

    因网上搜索到的相关资料大部分都是关于sdo_geometry的介绍和以及通过sql语句添加要素,查询要素等等.没有找到存储过程相关的例子,所以只好自己动手啦. 准备 环境:windowsxp系统,安装 ...

  8. hihocoder [Offer收割]编程练习赛61

    [Offer收割]编程练习赛61 A:最小排列 给定一个长度为m的序列b[1..m],再给定一个n,求一个字典序最小的1~n的排列A,使得b是A的子序列. 贪心即可,b是A的子序列,把不在b中的元素, ...

  9. ssh无密码登陆远程机,pssh轻批量工具

    #B(client)--------A(g_server)#A:   ssh-keygen -t rsa (g_server)#B:    scp -P 58422 root@g_server_ip: ...

  10. github与git基本操作(一)

    一.git上传本地项目到github 前提:github创建一个空仓库(得到“https://自己的仓库url地址”)1.第一步:就是要进入这个目录下,cmd2.第二步:输入git init3.第三步 ...