701 lines
22 KiB
C++
701 lines
22 KiB
C++
// 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)
|
|
{
|
|
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;
|
|
}
|