此文介绍如何打开本地文本文件、文件夹对话框
方法一:
效果图:
源码
#include <iostream>
#include <objbase.h>
#include <ShObjIdl.h>
using namespace std;
int main()
{
cout << "start=====" << endl;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
IFileOpenDialog* pFileOpen;
// Create the FileOpenDialog object.
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
if (SUCCEEDED(hr))
{
// Show the Open dialog box.
hr = pFileOpen->Show(NULL);
// Get the file name from the dialog box.
if (SUCCEEDED(hr))
{
IShellItem* pItem;
hr = pFileOpen->GetResult(&pItem);
if (SUCCEEDED(hr))
{
PWSTR pszFilePath;
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
// Display the file name to the user.
if (SUCCEEDED(hr))
{
MessageBoxW(NULL, pszFilePath, L"File Path", MB_OK);
CoTaskMemFree(pszFilePath);
}
pItem->Release();
}
}
pFileOpen->Release();
}
CoUninitialize();
}
return 0;
}
出现错误
1、2、IID_IFileOpenDialog' was not declared in this scope
将
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
改为
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
2、 error: invalid use of incomplete type 'IFileDialog {aka struct IFileDialog}'
原因:默认情况下,MinGW将NTDDI_VERSION宏的值设置为0x05020000 (NTDDI_WS03),这会禁用shobjidl.h中的IFileDialog定义。宏的值必须至少为0x06000000 (NTDDI_VISTA)才能启用定义。下面列出了这些宏的可能值及其含义:
将以下内容放在源文件的开头(在include之前)应该可以修复编译错误:
#define NTDDI_VERSION 0x0A000006 //NTDDI_WIN10_RS5
#define _WIN32_WINNT 0x0A00 // _WIN32_WINNT_WIN10, the _WIN32_WINNT macro must also be defined when defining NTDDI_VERSION
如果您没有链接所需的库,则可能仍然存在链接错误。你列出的程序需要ole32.dll和uuid.dll,所以编译命令看起来像这样:
方法二:
效果图:
选择要尝试的文件对话框类型
打开对话框效果
源码
ShellHelpers.h
#pragma once
#pragma once
#define STRICT_TYPED_ITEMIDS // in case you do IDList stuff you want this on for better type saftey
#define UNICODE 1
#include <windows.h>
#include <windowsx.h> // for WM_COMMAND handling macros
#include <shlobj.h> // shell stuff
#include <shlwapi.h> // QISearch, easy way to implement QI
#include <propkey.h>
#include <propvarutil.h>
#include <strsafe.h>
#include <objbase.h>
#pragma comment(lib, "shlwapi.lib") // link to this
#pragma comment(lib, "comctl32.lib") // link to this
#pragma comment(lib, "propsys.lib") // link to this
// set up common controls v6 the easy way
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
__inline HRESULT ResultFromKnownLastError() { const DWORD err = GetLastError(); return err == ERROR_SUCCESS ? E_FAIL : HRESULT_FROM_WIN32(err); }
// map Win32 APIs that follow the return BOOL/set last error protocol
// into HRESULT
//
// example: MoveFileEx()
__inline HRESULT ResultFromWin32Bool(BOOL b)
{
return b ? S_OK : ResultFromKnownLastError();
}
#if (NTDDI_VERSION >= NTDDI_VISTA)
__inline HRESULT ShellExecuteItem(HWND hwnd, PCWSTR pszVerb, IShellItem* psi)
{
// how to activate a shell item, use ShellExecute().
PIDLIST_ABSOLUTE pidl;
HRESULT hr = SHGetIDListFromObject(psi, &pidl);
if (SUCCEEDED(hr))
{
SHELLEXECUTEINFO ei = { sizeof(ei) };
ei.fMask = SEE_MASK_INVOKEIDLIST;
ei.hwnd = hwnd;
ei.nShow = SW_NORMAL;
ei.lpIDList = pidl;
ei.lpVerb = pszVerb;
hr = ResultFromWin32Bool(ShellExecuteEx(&ei));
CoTaskMemFree(pidl);
}
return hr;
}
__inline HRESULT GetItemFromView(IFolderView2* pfv, int iItem, REFIID riid, void** ppv)
{
*ppv = NULL;
HRESULT hr = S_OK;
if (iItem == -1)
{
hr = pfv->GetSelectedItem(-1, &iItem); // Returns S_FALSE if none selected
}
if (S_OK == hr)
{
hr = pfv->GetItem(iItem, riid, ppv);
}
else
{
hr = E_FAIL;
}
return hr;
}
// set the icon for your window using WM_SETICON from one of the set of stock system icons
// caller must call ClearDialogIcon() to free the HICON that is created
__inline void SetDialogIcon(HWND hdlg, SHSTOCKICONID siid)
{
SHSTOCKICONINFO sii = { sizeof(sii) };
if (SUCCEEDED(SHGetStockIconInfo(siid, SHGFI_ICON | SHGFI_SMALLICON, &sii)))
{
SendMessage(hdlg, WM_SETICON, ICON_SMALL, (LPARAM)sii.hIcon);
}
if (SUCCEEDED(SHGetStockIconInfo(siid, SHGFI_ICON | SHGFI_LARGEICON, &sii)))
{
SendMessage(hdlg, WM_SETICON, ICON_BIG, (LPARAM)sii.hIcon);
}
}
#endif
// free the HICON that was set using SetDialogIcon()
__inline void ClearDialogIcon(HWND hdlg)
{
DestroyIcon((HICON)SendMessage(hdlg, WM_GETICON, ICON_SMALL, 0));
DestroyIcon((HICON)SendMessage(hdlg, WM_GETICON, ICON_BIG, 0));
}
__inline HRESULT SetItemImageImageInStaticControl(HWND hwndStatic, IShellItem* psi)
{
HBITMAP hbmp = NULL;
HRESULT hr = S_OK;
if (psi)
{
IShellItemImageFactory* psiif;
hr = psi->QueryInterface(IID_PPV_ARGS(&psiif));
if (SUCCEEDED(hr))
{
RECT rc;
GetWindowRect(hwndStatic, &rc);
const UINT dxdy = min(rc.right - rc.left, rc.bottom - rc.top); // make it square
const SIZE size = { dxdy, dxdy };
hr = psiif->GetImage(size, SIIGBF_RESIZETOFIT, &hbmp);
psiif->Release();
}
}
if (SUCCEEDED(hr))
{
HGDIOBJ hgdiOld = (HGDIOBJ)SendMessage(hwndStatic, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
if (hgdiOld)
{
DeleteObject(hgdiOld); // if there was an old one clean it up
}
}
return hr;
}
__inline HRESULT SHILCloneFull(PCUIDLIST_ABSOLUTE pidl, PIDLIST_ABSOLUTE* ppidl)
{
*ppidl = ILCloneFull(pidl);
return *ppidl ? S_OK : E_OUTOFMEMORY;
}
__inline HRESULT SHILClone(PCUIDLIST_RELATIVE pidl, PIDLIST_RELATIVE* ppidl)
{
*ppidl = ILClone(pidl);
return *ppidl ? S_OK : E_OUTOFMEMORY;
}
__inline HRESULT SHILCombine(PCIDLIST_ABSOLUTE pidl1, PCUIDLIST_RELATIVE pidl2, PIDLIST_ABSOLUTE* ppidl)
{
*ppidl = ILCombine(pidl1, pidl2);
return *ppidl ? S_OK : E_OUTOFMEMORY;
}
__inline HRESULT GetItemAt(IShellItemArray* psia, DWORD i, REFIID riid, void** ppv)
{
*ppv = NULL;
IShellItem* psi = NULL; // avoid error C4701
HRESULT hr = psia ? psia->GetItemAt(i, &psi) : E_NOINTERFACE;
if (SUCCEEDED(hr))
{
hr = psi->QueryInterface(riid, ppv);
psi->Release();
}
return hr;
}
#define MAP_ENTRY(x) {L#x, x}
__inline HRESULT ShellAttributesToString(SFGAOF sfgaof, PWSTR* ppsz)
{
*ppsz = NULL;
static const struct { PCWSTR pszName; SFGAOF sfgaof; } c_rgItemAttributes[] =
{
// note, SFGAO_HASSUBFOLDER is too expesnive to compute
// and has been excluded from this list
MAP_ENTRY(SFGAO_STREAM),
MAP_ENTRY(SFGAO_FOLDER),
MAP_ENTRY(SFGAO_FILESYSTEM),
MAP_ENTRY(SFGAO_FILESYSANCESTOR),
MAP_ENTRY(SFGAO_STORAGE),
MAP_ENTRY(SFGAO_STORAGEANCESTOR),
MAP_ENTRY(SFGAO_LINK),
MAP_ENTRY(SFGAO_CANCOPY),
MAP_ENTRY(SFGAO_CANMOVE),
MAP_ENTRY(SFGAO_CANLINK),
MAP_ENTRY(SFGAO_CANRENAME),
MAP_ENTRY(SFGAO_CANDELETE),
MAP_ENTRY(SFGAO_HASPROPSHEET),
MAP_ENTRY(SFGAO_DROPTARGET),
MAP_ENTRY(SFGAO_ENCRYPTED),
MAP_ENTRY(SFGAO_ISSLOW),
MAP_ENTRY(SFGAO_GHOSTED),
MAP_ENTRY(SFGAO_SHARE),
MAP_ENTRY(SFGAO_READONLY),
MAP_ENTRY(SFGAO_HIDDEN),
MAP_ENTRY(SFGAO_REMOVABLE),
MAP_ENTRY(SFGAO_COMPRESSED),
MAP_ENTRY(SFGAO_BROWSABLE),
MAP_ENTRY(SFGAO_NONENUMERATED),
MAP_ENTRY(SFGAO_NEWCONTENT),
};
WCHAR sz[512] = {};
PWSTR psz = sz;
size_t cch = ARRAYSIZE(sz);
StringCchPrintfEx(psz, cch, &psz, &cch, 0, L"0x%08X", sfgaof);
for (int i = 0; i < ARRAYSIZE(c_rgItemAttributes); i++)
{
if (c_rgItemAttributes[i].sfgaof & sfgaof)
{
StringCchPrintfEx(psz, cch, &psz, &cch, 0, L", %s", c_rgItemAttributes[i].pszName);
}
}
return SHStrDup(sz, ppsz);
}
template <class T> void SafeRelease(T** ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
// assign an interface pointer, release old, capture ref to new, can be used to set to zero too
template <class T> HRESULT SetInterface(T** ppT, IUnknown* punk)
{
SafeRelease(ppT);
return punk ? punk->QueryInterface(ppT) : E_NOINTERFACE;
}
// remote COM methods are dispatched in the context of an exception handler that consumes
// all SEH exceptions including crahses and C++ exceptions. this is undesirable as it
// means programs will continue to run after such an exception has been thrown,
// leaving the process in a inconsistent state.
//
// this applies to COM methods like IDropTarget::Drop()
//
// this code turns off that behavior
__inline void DisableComExceptionHandling()
{
IGlobalOptions* pGlobalOptions;
HRESULT hr = CoCreateInstance(CLSID_GlobalOptions, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGlobalOptions));
if (SUCCEEDED(hr))
{
#if (NTDDI_VERSION >= NTDDI_WIN7)
hr = pGlobalOptions->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE_ANY);
#else
hr = pGlobalOptions->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE);
#endif
pGlobalOptions->Release();
}
}
__inline void GetWindowRectInClient(HWND hwnd, RECT* prc)
{
GetWindowRect(hwnd, prc);
MapWindowPoints(GetDesktopWindow(), GetParent(hwnd), (POINT*)prc, 2);
}
// retrieve the HINSTANCE for the current DLL or EXE using this symbol that
// the linker provides for every module, avoids the need for a global HINSTANCE variable
// and provides access to this value for static libraries
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
__inline HINSTANCE GetModuleHINSTANCE() { return (HINSTANCE)&__ImageBase; }
FileName2.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 <windows.h>
#define STRICT_TYPED_ITEMIDS
#include <shlobj.h>
#include <shlwapi.h>
#include "ShellHelpers.h"
#include <strsafe.h>
#include <new>
const DWORD c_idAdd = 601;
const DWORD c_idDone = 602;
#define IDC_PICKITEM 100
#define IDC_PICKCONTAINER 101
#define IDC_FILEOPENBASKETPICKER 102
#define IDC_PICKFILESANDFOLDERS 103
/* Utility Classes and Functions *************************************************************************************************/
/*
Usage:
CItemIterator itemIterator(psi);
while (itemIterator.MoveNext())
{
IShellItem2 *psi;
hr = itemIterator.GetCurrent(IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
// Perform action on psi
psi->Release();
}
}
*/
class CItemIterator
{
public:
CItemIterator(IShellItem* psi) : _hr(SHGetIDListFromObject(psi, &_pidlFull)), _psfCur(NULL)
{
_Init();
}
CItemIterator(PCIDLIST_ABSOLUTE pidl) : _hr(SHILCloneFull(pidl, &_pidlFull)), _psfCur(NULL)
{
_Init();
}
~CItemIterator()
{
CoTaskMemFree(_pidlFull);
SafeRelease(&_psfCur);
}
bool MoveNext()
{
bool fMoreItems = false;
if (SUCCEEDED(_hr))
{
if (NULL == _pidlRel)
{
fMoreItems = true;
_pidlRel = _pidlFull; // First item - Might be empty if it is the desktop
}
else if (!ILIsEmpty(_pidlRel))
{
PCUITEMID_CHILD pidlChild = (PCUITEMID_CHILD)_pidlRel; // Save the current segment for binding
_pidlRel = ILNext(_pidlRel);
// If we are not at the end setup for the next iteration
if (!ILIsEmpty(_pidlRel))
{
const WORD cbSave = _pidlRel->mkid.cb; // Avoid cloning for the child by truncating temporarily
_pidlRel->mkid.cb = 0; // Make this a child
IShellFolder* psfNew;
_hr = _psfCur->BindToObject(pidlChild, NULL, IID_PPV_ARGS(&psfNew));
if (SUCCEEDED(_hr))
{
_psfCur->Release();
_psfCur = psfNew; // Transfer ownership
fMoreItems = true;
}
_pidlRel->mkid.cb = cbSave; // Restore previous ID size
}
}
}
return fMoreItems;
}
HRESULT GetCurrent(REFIID riid, void** ppv)
{
*ppv = NULL;
if (SUCCEEDED(_hr))
{
// Create the childID by truncating _pidlRel temporarily
PUIDLIST_RELATIVE pidlNext = ILNext(_pidlRel);
const WORD cbSave = pidlNext->mkid.cb; // Save old cb
pidlNext->mkid.cb = 0; // Make _pidlRel a child
_hr = SHCreateItemWithParent(NULL, _psfCur, (PCUITEMID_CHILD)_pidlRel, riid, ppv);
pidlNext->mkid.cb = cbSave; // Restore old cb
}
return _hr;
}
HRESULT GetResult() const { return _hr; }
PCUIDLIST_RELATIVE GetRelativeIDList() const { return _pidlRel; }
private:
void _Init()
{
_pidlRel = NULL;
if (SUCCEEDED(_hr))
{
_hr = SHGetDesktopFolder(&_psfCur);
}
}
HRESULT _hr;
PIDLIST_ABSOLUTE _pidlFull;
PUIDLIST_RELATIVE _pidlRel;
IShellFolder* _psfCur;
};
HRESULT GetIDListName(IShellItem* psi, PWSTR* ppsz)
{
*ppsz = NULL;
HRESULT hr = E_FAIL;
WCHAR szFullName[2048];
szFullName[0] = 0;
PWSTR pszOutput = szFullName;
size_t cchOutput = ARRAYSIZE(szFullName);
CItemIterator itemIterator(psi);
while (itemIterator.MoveNext())
{
IShellItem2* psi;
hr = itemIterator.GetCurrent(IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
PWSTR pszName;
hr = psi->GetDisplayName(SIGDN_PARENTRELATIVE, &pszName);
if (SUCCEEDED(hr))
{
// Ignore errors, this is for debugging only
StringCchCatEx(pszOutput, cchOutput, L"[", &pszOutput, &cchOutput, 0);
StringCchCatEx(pszOutput, cchOutput, pszName, &pszOutput, &cchOutput, 0);
StringCchCatEx(pszOutput, cchOutput, L"]", &pszOutput, &cchOutput, 0);
CoTaskMemFree(pszName);
}
psi->Release();
}
}
if (SUCCEEDED(hr))
{
hr = SHStrDup(szFullName, ppsz);
}
return hr;
}
HRESULT GetSelectionFromSite(IUnknown* punkSite, BOOL fNoneImpliesFolder, IShellItemArray** ppsia)
{
*ppsia = NULL;
IFolderView2* pfv;
HRESULT hr = IUnknown_QueryService(punkSite, SID_SFolderView, IID_PPV_ARGS(&pfv));
if (SUCCEEDED(hr))
{
hr = pfv->GetSelection(fNoneImpliesFolder, ppsia);
pfv->Release();
}
return hr;
}
void DeletePerUserDialogState()
{
IFileDialog* pfd;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Delete window size, MRU and other saved data for testing initial case
pfd->ClearClientData();
pfd->Release();
}
}
void ReportSelectedItems(IUnknown* punkSite, IShellItemArray* psia)
{
DWORD cItems;
HRESULT hr = psia->GetCount(&cItems);
for (DWORD i = 0; SUCCEEDED(hr) && (i < cItems); i++)
{
IShellItem* psi;
hr = psia->GetItemAt(i, &psi);
if (SUCCEEDED(hr))
{
PWSTR pszName;
hr = GetIDListName(psi, &pszName);
if (SUCCEEDED(hr))
{
HWND hwnd;
IUnknown_GetWindow(punkSite, &hwnd);
int nButton;
const TASKDIALOG_COMMON_BUTTON_FLAGS buttonFlags = (i == (cItems - 1)) ? TDCBF_OK_BUTTON : TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;
WCHAR szMsg[128];
StringCchPrintf(szMsg, ARRAYSIZE(szMsg), L"Item %d of %d added to basket", i + 1, cItems);
if (SUCCEEDED(TaskDialog(hwnd, 0, L"Items Addded to Basket", szMsg, pszName, buttonFlags, NULL, &nButton)))
{
hr = (nButton == IDCANCEL) ? HRESULT_FROM_WIN32(ERROR_CANCELLED) : S_OK;
}
CoTaskMemFree(pszName);
}
psi->Release();
}
}
}
void ReportSelectedItemsFromSite(IUnknown* punkSite)
{
IShellItemArray* psia;
HRESULT hr = GetSelectionFromSite(punkSite, TRUE, &psia);
if (SUCCEEDED(hr))
{
ReportSelectedItems(punkSite, psia);
psia->Release();
}
}
/* Picking a file ****************************************************************************************************************/
void PickItem()
{
IFileDialog* pfd;
if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd))))
{
if (SUCCEEDED(pfd->Show(NULL)))
{
IShellItem* psi;
if (SUCCEEDED(pfd->GetResult(&psi)))
{
PWSTR pszPath;
if (SUCCEEDED(GetIDListName(psi, &pszPath)))
{
MessageBox(NULL, pszPath, L"Selected Item", MB_OK);
CoTaskMemFree(pszPath);
}
psi->Release();
}
}
pfd->Release();
}
}
/* Picking a container ***********************************************************************************************************/
void PickContainer()
{
IFileDialog* pfd;
if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd))))
{
DWORD dwOptions;
if (SUCCEEDED(pfd->GetOptions(&dwOptions)))
{
pfd->SetOptions(dwOptions | FOS_PICKFOLDERS);
}
if (SUCCEEDED(pfd->Show(NULL)))
{
IShellItem* psi;
if (SUCCEEDED(pfd->GetResult(&psi)))
{
PWSTR pszPath;
if (SUCCEEDED(GetIDListName(psi, &pszPath)))
{
MessageBox(NULL, pszPath, L"Selected Container", MB_OK);
CoTaskMemFree(pszPath);
}
psi->Release();
}
}
pfd->Release();
}
}
/* Picking Files in Basket Mode **************************************************************************************************/
class CFileOpenBasketPickerCallback : public IFileDialogEvents, public IFileDialogControlEvents
{
public:
CFileOpenBasketPickerCallback()
{
}
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CFileOpenBasketPickerCallback, IFileDialogEvents),
QITABENT(CFileOpenBasketPickerCallback, IFileDialogControlEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
// This class makes special assumptions about how it is used, specifically
// 1) This class will only reside on the stack.
// 2) Components that consume this object have well-defined reference lifetimes.
// In this case, this is only consumed by the file dialog advise and unadvise.
// Unadvising will release the file dialog's only reference to this object.
//
// Do not do this for heap allocated objects.
IFACEMETHODIMP_(ULONG) AddRef() { return 3; }
IFACEMETHODIMP_(ULONG) Release() { return 2; }
// IFileDialogEvents
IFACEMETHODIMP OnFileOk(IFileDialog* pfd)
{
// if this button is in the "Add" mode then do this, otherwise return S_OK
IFileOpenDialog* pfod;
HRESULT hr = pfd->QueryInterface(IID_PPV_ARGS(&pfod));
if (SUCCEEDED(hr))
{
IShellItemArray* psia;
hr = pfod->GetSelectedItems(&psia);
if (SUCCEEDED(hr))
{
ReportSelectedItems(pfd, psia);
psia->Release();
}
pfod->Release();
}
return S_FALSE; // S_FALSE keeps the dialog up; return S_OK to allow it to dismiss.
}
IFACEMETHODIMP OnFolderChanging(IFileDialog* /* pfd */, IShellItem* /* psi */)
{
return E_NOTIMPL;
}
IFACEMETHODIMP OnFolderChange(IFileDialog* /* pfd */)
{
return E_NOTIMPL;
}
IFACEMETHODIMP OnSelectionChange(IFileDialog* pfd)
{
// Update the text of the Open/Add button here based on the selection
IShellItem* psi;
HRESULT hr = pfd->GetCurrentSelection(&psi);
if (SUCCEEDED(hr))
{
SFGAOF attr;
hr = psi->GetAttributes(SFGAO_FOLDER | SFGAO_STREAM, &attr);
if (SUCCEEDED(hr) && (SFGAO_FOLDER == attr))
{
pfd->SetOkButtonLabel(L"Open");
}
else
{
pfd->SetOkButtonLabel(L"Add");
}
psi->Release();
}
return S_OK;
}
IFACEMETHODIMP OnShareViolation(IFileDialog* /* pfd */, IShellItem* /* psi */, FDE_SHAREVIOLATION_RESPONSE* /* pResponse */) { return E_NOTIMPL; }
IFACEMETHODIMP OnTypeChange(IFileDialog* /* pfd */) { return E_NOTIMPL; }
IFACEMETHODIMP OnOverwrite(IFileDialog* /* pfd */, IShellItem* /* psi */, FDE_OVERWRITE_RESPONSE* /* pResponse */) { return E_NOTIMPL; }
// IFileDialogControlEvents
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* /* pfdc */, DWORD /* dwIDCtl */, DWORD /* dwIDItem */) { return E_NOTIMPL; }
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize* pfdc, DWORD dwIDCtl)
{
switch (dwIDCtl)
{
case c_idDone:
IFileDialog* pfd;
if (SUCCEEDED(pfdc->QueryInterface(&pfd)))
{
pfd->Close(S_OK);
pfd->Release();
}
break;
default:
break;
}
return S_OK;
}
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize* /* pfdc */, DWORD /* dwIDCtl */, BOOL /* bChecked */) { return E_NOTIMPL; }
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize* /* pfdc */, DWORD /* dwIDCtl */) { return E_NOTIMPL; }
};
// This sample demonstrates how to use the file dialog in a modal way such that
// users can easily pick multiple files. It does this by overriding the normal "Open" button
// with an "Add" button that passes the selection back to this app.
//
// One case this sample does not support is selecting folders this way. This has
// the issue of the "Open" button being overloaded for "navigate into the folder" and "add the folder."
// One way to deal with this is to add a new button, "Add Folder", the PickFilesAndFolders sample demonstrates this.
void FileOpenBasketPicker()
{
IFileOpenDialog* pfd;
if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd))))
{
CFileOpenBasketPickerCallback foacb;
DWORD dwCookie;
if (SUCCEEDED(pfd->Advise(&foacb, &dwCookie)))
{
DWORD dwOptions;
if (SUCCEEDED(pfd->GetOptions(&dwOptions)))
{
pfd->SetOptions(dwOptions | FOS_ALLOWMULTISELECT | FOS_ALLNONSTORAGEITEMS);
}
IFileDialog2* pfd2;
if (SUCCEEDED(pfd->QueryInterface(&pfd2)))
{
pfd2->SetCancelButtonLabel(L"Done");
pfd2->Release();
}
else
{
IFileDialogCustomize* pfdc;
if (SUCCEEDED(pfd->QueryInterface(&pfdc)))
{
pfdc->AddPushButton(c_idDone, L"Done");
pfdc->Release();
}
}
pfd->SetTitle(L"File Open Modal Basket Picker Sample");
// We do not process the results of the dialog since
// the selected items are passed back via OnFileOk()
pfd->Show(NULL); // hr intentionally ignored
pfd->Unadvise(dwCookie);
}
pfd->Release();
}
}
/* Picking Files and Folders in Basket Mode **************************************************************************************/
class CPickFilesAndFoldersCallback : public IFileDialogEvents, public IFileDialogControlEvents
{
public:
CPickFilesAndFoldersCallback()
{
}
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CPickFilesAndFoldersCallback, IFileDialogEvents),
QITABENT(CPickFilesAndFoldersCallback, IFileDialogControlEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
// This class makes special assumptions about how it is used, specifically
// 1) This class will only reside on the stack.
// 2) Components that consume this object have well-defined reference lifetimes.
// In this case, this is only consumed by the file dialog advise and unadvise.
// Unadvising will release the file dialog's only reference to this object.
//
// Do not do this for heap allocated objects.
IFACEMETHODIMP_(ULONG) AddRef() { return 3; }
IFACEMETHODIMP_(ULONG) Release() { return 2; }
// IFileDialogEvents
IFACEMETHODIMP OnFileOk(IFileDialog* pfd)
{
ReportSelectedItemsFromSite(pfd);
return S_FALSE; // S_FALSE keeps the dialog up, return S_OK to allows it to dismiss
}
IFACEMETHODIMP OnFolderChanging(IFileDialog* /* pfd */, IShellItem* /* psi */)
{
return E_NOTIMPL;
}
IFACEMETHODIMP OnFolderChange(IFileDialog* /* pfd */)
{
return E_NOTIMPL;
}
IFACEMETHODIMP OnSelectionChange(IFileDialog* pfd)
{
// Design for the text of the "Add" button
// ---------------------------------------
// Single select item "Add file"
// Single select folder "Add folder"
// Multiselect "Add items"
// Null select "Add current folder"
IFileDialogCustomize* pfdc;
if (SUCCEEDED(pfd->QueryInterface(&pfdc)))
{
// GetSelectionFromSite() fails on no selection
// When that happens, default to the current folder.
PCWSTR pszLabel = L"Add current folder";
IShellItemArray* psia;
if (SUCCEEDED(GetSelectionFromSite(pfd, FALSE, &psia)))
{
DWORD count;
if (SUCCEEDED(psia->GetCount(&count)))
{
if (count == 1)
{
IShellItem* psi;
if (SUCCEEDED(psia->GetItemAt(0, &psi)))
{
SFGAOF attributes;
if (S_OK == psi->GetAttributes(SFGAO_FOLDER, &attributes))
{
pszLabel = L"Add folder";
}
else
{
pszLabel = L"Add file";
}
psi->Release();
}
}
else if (count > 1)
{
pszLabel = L"Add items";
}
}
psia->Release();
}
pfdc->SetControlLabel(c_idAdd, pszLabel);
pfdc->Release();
}
return S_OK;
}
IFACEMETHODIMP OnShareViolation(IFileDialog* /* pfd */, IShellItem* /* psi */, FDE_SHAREVIOLATION_RESPONSE* /* pResponse */) { return E_NOTIMPL; }
IFACEMETHODIMP OnTypeChange(IFileDialog* /* pfd */) { return E_NOTIMPL; }
IFACEMETHODIMP OnOverwrite(IFileDialog* /* pfd */, IShellItem* /* psi */, FDE_OVERWRITE_RESPONSE* /* pResponse */) { return E_NOTIMPL; }
// IFileDialogControlEvents
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize* /* pfdc */, DWORD /* dwIDCtl */, DWORD /* dwIDItem */) { return E_NOTIMPL; }
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize* pfdc, DWORD dwIDCtl)
{
switch (dwIDCtl)
{
case c_idAdd:
// Instead of using IFileDialog::GetCurrentSelection(), we need to get the
// selection from the view to handle the "no selection implies folder" case
ReportSelectedItemsFromSite(pfdc);
break;
case c_idDone:
{
IFileDialog* pfd;
if (SUCCEEDED(pfdc->QueryInterface(&pfd)))
{
pfd->Close(S_OK);
pfd->Release();
}
}
break;
default:
break;
}
return S_OK;
}
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize* /* pfdc */, DWORD /* dwIDCtl */, BOOL /* bChecked */) { return E_NOTIMPL; }
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize* /* pfdc */, DWORD /* dwIDCtl */) { return E_NOTIMPL; }
};
void PickFilesAndFolders()
{
IFileOpenDialog* pfd;
if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd))))
{
CPickFilesAndFoldersCallback foacb;
DWORD dwCookie;
if (SUCCEEDED(pfd->Advise(&foacb, &dwCookie)))
{
DWORD dwOptions;
if (SUCCEEDED(pfd->GetOptions(&dwOptions)))
{
pfd->SetOptions(dwOptions | FOS_ALLOWMULTISELECT | FOS_ALLNONSTORAGEITEMS);
}
IFileDialogCustomize* pfdc;
if (SUCCEEDED(pfd->QueryInterface(&pfdc)))
{
// The spacing pads the button a bit.
pfdc->AddPushButton(c_idAdd, L" Add current folder ");
pfdc->Release();
}
IFileDialog2* pfd2;
if (SUCCEEDED(pfd->QueryInterface(&pfd2)))
{
pfd2->SetCancelButtonLabel(L"Done");
pfd2->Release();
}
else
{
// pre Win7 we need to add a 3rd button, ugly but workable
IFileDialogCustomize* pfdc;
if (SUCCEEDED(pfd->QueryInterface(&pfdc)))
{
pfdc->AddPushButton(c_idDone, L"Done");
pfdc->Release();
}
}
pfd->SetTitle(L"Pick Files and Folder Sample");
// the items selected are passed back via OnFileOk()
// so we don't process the results of the dialog
pfd->Show(NULL); // hr intentionally ignored
pfd->Unadvise(dwCookie);
}
pfd->Release();
}
}
// Application entry point
//int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
int main()
{
HRESULT hr = OleInitialize(0);
if (SUCCEEDED(hr))
{
TASKDIALOGCONFIG taskDialogParams = { sizeof(taskDialogParams) };
taskDialogParams.dwFlags = TDF_USE_COMMAND_LINKS | TDF_ALLOW_DIALOG_CANCELLATION;
TASKDIALOG_BUTTON const buttons[] =
{
{ IDC_PICKITEM, L"Pick File" },
{ IDC_PICKCONTAINER, L"Pick Folder" },
{ IDC_FILEOPENBASKETPICKER, L"Pick Files (Basket Mode)" },
{ IDC_PICKFILESANDFOLDERS, L"Pick Files and Folders (Basket Mode)" },
};
taskDialogParams.pButtons = buttons;
taskDialogParams.cButtons = ARRAYSIZE(buttons);
taskDialogParams.pszMainInstruction = L"Pick the file dialog samples you want to try";
taskDialogParams.pszWindowTitle = L"Common File Dialog Modes";
while (SUCCEEDED(hr))
{
int selectedId;
hr = TaskDialogIndirect(&taskDialogParams, &selectedId, NULL, NULL);
if (SUCCEEDED(hr))
{
if (selectedId == IDCANCEL)
{
break;
}
else if (selectedId == IDC_PICKITEM)
{
PickItem();
}
else if (selectedId == IDC_PICKCONTAINER)
{
PickContainer();
}
else if (selectedId == IDC_FILEOPENBASKETPICKER)
{
FileOpenBasketPicker();
}
else if (selectedId == IDC_PICKFILESANDFOLDERS)
{
PickFilesAndFolders();
}
}
}
OleUninitialize();
}
return 0;
}