[ATL/WTL]_[初级]_[选择目录对话框]
场景
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]_[初级]_[选择目录对话框]的更多相关文章
- MFC_选择目录对话框_选择文件对话框_指定目录遍历文件
选择目录对话框 void C资源共享吧视频广告清理工具Dlg::OnBnClickedCls() { // 清空编辑框内容 m_Edit.SetWindowTextW(L""); ...
- CE选择目录对话框(转)
本文转载于http://blog.163.com/zhaojun_xf/blog/static/30050580201132221118479/ 在Wince下要打开目录对话框需要调用函数SHBrow ...
- [MFC]选择目录对话框和选择文件对话框
在MFC编程中经常会需要用到选择目录和选择文件的界面,以下总结一下本人常用的这两种对话框的生成方法: 选择目录对话框 //选择目录按钮void CDcPackerDlg::OnBnClickedDec ...
- [MFC]选择目录对话框和选择文件对话框 [转]
在MFC编程中经常会需要用到选择目录和选择文件的界面,以下总结一下本人常用的这两种对话框的生成方法: 选择目录对话框 { char szPath[MAX_PATH]; //存放选择的 ...
- 如何在VBS脚本中显示“选择文件对话框”或“选择目录对话框”
.选择文件[XP操作系统,不能用于Win2000或98],使用“UserAccounts.CommonDialog”对象向用户显示一个标准的“文件打开”对话框 Set objDialog = Crea ...
- [ATL/WTL]_[初级]_[关于graphics.DrawImage绘图时显示不正常的问题]
场景 1.使用win32绘图时, 最简单的api是使用 graphics.DrawImage(image,x,y)来绘制, 可是这个api有个坑,它的图片显示完整和设备分辨率(显卡)有关. 说明 1. ...
- [C/C++标准库]_[初级]_[转换UTC时间到local本地时间]
场景 1.如果有面向全球用户的网站, 一般在存储时间数据时存储的是UTC格式的时间, 这样时间是统一的, 并可以根据当地时区来进行准确的转换. 2.存储本地时间的问题就在于如果换了时区, 那么显示的时 ...
- [Zlib]_[初级]_[使用zlib库压缩和解压STL string]
场景 1.一般在使用文本json传输数据, 数据量特别大时,传输的过程就特别耗时, 因为带宽或者socket的缓存是有限制的, 数据量越大, 传输时间就越长. 网站一般使用gzip来压缩成二进制. 说 ...
- [C/C++11]_[初级]_[std::bind介绍和使用]
场景 1.C++11 引入了std::function 对象, 这个对象可以通过std::bind封装所有的函数, 并通过代理调用这个std::function的方式调用这个函数. 比如通过统一的方式 ...
随机推荐
- 原生java调用webservice的方法,不用生成客户端代码
原生java调用webservice的方法,不用生成客户端代码 2015年10月29日 16:46:59 阅读数:1455 <span style="font-family: Aria ...
- 用华为eNSP模拟器配置Hybrid、Trunk和Access三种链路类型端口
上一篇文章写到三层交换机实现多个VLAN之间互相通讯,有朋友提问要如何进行配置,可有案例分析.其实那天我在写的时候也有做过模拟,只是后来没有保存.今天重新模拟一次,并附上详细配置命令,希望能够帮助到大 ...
- Linux 系统的网络配置文件
系统的网络配置文件 方式一: 界面操作 setup -->界面配置网络,网关等 方式二: 修改配置文件 # 修改配置 vim /etc/sysconfig/network-scripts/ifc ...
- IntelliJ IDEA2017/2018 激活方法 破解补丁激活(亲测可用)(注册码方法以和谐)
IntelliJ IDEA2017 激活方法(注册码方法以和谐): 搭建自己的授权服务器,对大佬来说也很简单,我作为菜鸟就不说了,网上有教程. 我主要说第二种,现在,直接写入注册码,是不能成功激活的( ...
- c# Windows Service 桌面上显示UI
介绍 本文的目的是说明如何从Windows Vista中的服务正确启动交互式进程,以及演示如何以完全管理员权限启动该进程.交互式过程是能够在桌面上显示UI的过程. 本文介绍如何创建一个名为Loader ...
- CentOS服务器的加固方案
>>>Centos账户安全 对Centos的加固首先要控制用户的权限,用户权限主要涉及到/etc下的/passwd,/shadow和/group三个文件 /passwd文件主要是存储 ...
- 函数的调用 and 打印返回值 ret= mai() print(ret)
def mai(): # mai 函数名 (yan) 形式参数 print("老板,给我一包中华") return"给你" # 返回值-- 给你ret = ma ...
- 教你用 jVectorMap 制作属于自己的旅行足迹
jVectorMap JVectorMap 是一个优秀的.兼容性强的 jQuery 地图插件. 它可以工作在包括 IE6 在内的各款浏览器中,矢量图输出,除官方提供各国地图数据外,用户可以使用数据转换 ...
- MEMCACHE与REDIS
千万数据量的高并发,容灾. Redis 基于单线程, 持久性 多数据类型 内存中只存KEY Redis支持数据的备份,即master-slave模式的数据备份. Redis与Memcached的区别 ...
- DotNET中的幕后英雄:MSCOREE.DLL
现在做.NET Framework的开发的朋友应该是越来越多了,但是可能并非人人都对MSCOREE.DLL非常了解.而事实上,毫不夸张地说,MSCOREE.DLL是.NET Framework中最为核 ...