Starting in 1996, Alexa Internet has been donating their crawl data to the Internet Archive. Flowing in every day, these data are added to the Wayback Machine after an embargo period.
CYABFFW dlg(_T("Please select a directory"), // Hint to user
BIF_USE_NEWUI, // Flags for the dlgthis, // Parent window
CSIDL_DRIVES); // Root of searchif (IDOK == dlg.DoModal())
{
CString s = dlg.GetPath();
// Do something with 's' ...
If you want to customize the dialog beyond what you can do with arguments to the constructor, you'll need to subclass CYABFFW. CYABFFW defines three virtual functions that can be overridden: OnInitBFFDialog(), OnBFFSelChanged(), or OnBFFValidateFailed(). These correspond to the custom messages SHBrowseForFolder() sends to its optional callback functions BFFM_INITIALIZED, BFFM_SELCHANGED, and BFFM_VALIDATEFAILED. Of course, you can also Mesage Map entries to your subclass and process any Windows Messages you're interested in. The demo project (see below) includes an example of doing this to add my "Recurse" checkbox.
The Code
YABFFW.h:
/**
* \class CYABFFW
*
* \brief MFC Dialog wrapping \c ::SHBrowseForFolder
*
*
* CYABFFW implements a different approach to wrapping \c
* SHBrowseForFolder in an MFC-friendly way. It actually
* subclasses \c CWnd, allowing you to hook into the MFC Message
* Mapping mechanism for customization. Naturally, it also takes
* care of some unpleasant chores associated with with \c
* SHBrowseForFolder, such as translating from an \c LPITEMIDLIST
* to a CString, and so on.
*
* Use it just like you would any other MFC-based Dialog:
*
\code
void CMyClass::CMyHandler()
{
YABFFW dlg(_T("Choose a folder"), BIF_USENEWUI, this,
CSIDL_DESKTOP);
if (IDOK == dlg.DoModal())
{
CString s = dlg.GetPath();
// Do something with 's' ...
}
\endcode
*
*
*/class CYABFFW : public CWnd
{
// Constructionpublic:
/// Construct a CYABFFW dialog
CYABFFW(const CString &strHint = CString(), UINT nFlags = 0U,
CWnd *pParentWnd = NULL,
int nRootFolder = CSIDL_DESKTOP);
/// Load the "hint" from Resource
CYABFFW(int nHint, UINT nFlags = 0U, CWnd *pParentWnd = NULL,
int nRootFolder = CSIDL_DESKTOP);
/// Construct a CYABFFW dialog rooted at an arbitrary folder
CYABFFW(const CString &strHint, const CString &strRoot,
UINT nFlags = 0U, CWnd *pParentWnd = NULL);
/// Load the "hint" from Resource, but root the browse/// operation at an arbitrary folder
CYABFFW(int nHint, const CString &strRoot, UINT nFlags = 0U,
CWnd *pParentWnd = NULL);
// Attributespublic:
/// Retrieve the display name of the selected item
CString GetDisplayName() const;
/// Retrieve the ITEMIDLIST of the selected item
LPCITEMIDLIST GetItemIdList() const;
/// Retrieve the path to the selected item
CString GetPath() const;
// Operationspublic:
/// Display the Dialog - returns IDOK or IDCANCELint DoModal();
/// En/Dis-able the "OK" buttonvoid EnableOk(BOOL bEnable);
/// Set the current selection in the tree controlvoid SetSelection(LPCITEMIDLIST pItemIdList);
/// Set the current selection in the tree controlvoid SetSelection(const CString &strPath);
/// Set the "hint" textvoid SetText(const CString &strNewText);
// Overridespublic:
/// Called when the BFF Dialog has initializedvirtualvoid OnInitBFFDialog();
/// Called when the selection has changed in the tree controlvirtualvoid OnBFFSelChanged(LPITEMIDLIST pNewSel);
/// Called when the user types an invalid namevirtual BOOL OnBFFValidateFailed(const CString &strBadName);
// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CYABFFW)//}}AFX_VIRTUAL// Implementationpublic:
virtual ~CYABFFW();
// Generated message map functionsprotected:
//{{AFX_MSG(CYABFFW) NOTE - the ClassWizard will add and remove// member functions here.//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
/// Static method to be used for the SHBFF callbackstaticint CALLBACK BrowseCallbackProc(HWND hWnd, UINT nMsg,
LPARAM lParam,
LPARAM lpData);
private:
/// Free the memory referenced by m_pItemIdListvoid FreeItemIdList(IMalloc *pMalloc = NULL);
/// CSIDL => ITEMIDLIST
LPITEMIDLIST ResolveCsidl(int nCsidl) const;
/// Textual filesystem path => ITEMIDLIST
LPITEMIDLIST ResolveFsPath(const CString &strPath) const;
private:
/// Display name of the selected item
CString m_strDisplayName;
/// Flags to be passed to the browse dialog
UINT m_nFlags;
/// "Hint" to be displayed above the tree control
CString m_strHint;
/// ITEMIDLIST identifying the selected Shell item
LPITEMIDLIST m_pItemIdList;
/// Parent CWnd (NULL => App main window)
CWnd *m_pParentWnd;
/// Selected path
CString m_strPath;
/// ITEMIDLIST identifying the root
LPITEMIDLIST m_pRoot;
};
/////////////////////////////////////////////////////////////////
#ifndef _DEBUG
inline CString CYABFFW::GetDisplayName() const
{ return m_strDisplayName; }
inline LPCITEMIDLIST CYABFFW::GetItemIdList() const
{ return m_pItemIdList; }
inline CString CYABFFW::GetPath() const
{ return m_strPath; }
#endif // not _DEBUG
YABFFW.cpp:
/**
* \brief Construct a CYABFFW dialog
*
* \sa SetText
*
*
* \param strHint Optional string to display in the dialog; can
* be changed later by SetText
*
* \param nFlags Optional Bit vector made up of \c BIF_* values
* controlling various aspects of the dialog's appearance &
* behavior
*
* \param pParentWnd Points to the parent or owner window object
* (of type CWnd) to which the dialog object belongs. If it is
* NULL, the dialog objects parent window is set to the main
* application window.
*
* \param nRootFolder Optional \c CSIDL value naming the root of
* the tree that the user may browse
*
*
*/
CYABFFW::CYABFFW(const CString &strHint /*= CString()*/,
UINT nFlags /*= 0U*/,
CWnd *pParentWnd /*= NULL*/,
int nRootFolder /*= CSIDL_DESKTOP*/) :
m_nFlags(nFlags),
m_strHint(strHint),
m_pItemIdList(NULL),
m_pParentWnd(pParentWnd),
m_pRoot(NULL)
{
ASSERT_NULL_OR_VALID(pParentWnd); // Paranoia
m_pRoot = ResolveCsidl(nRootFolder);
}
/**
* \brief Load the "hint" from Resource
*
* \sa SetText
*
*
* \param nHint Resource Id of a string to be loaded and used in
* the dialog; can be changed later by SetText
*
* \param nFlags Optional Bit vector made up of \c BIF_* values
* controlling various aspects of the dialog's appearance &
* behavior
*
* \param pParentWnd Points to the parent or owner window object
* (of type CWnd) to which the dialog object belongs. If it is
* NULL, the dialog objects parent window is set to the main
* application window.
*
* \param nRootFolder Optional \c CSIDL value naming the root of
* the tree that the user may browse
*
*
*/
CYABFFW::CYABFFW(int nHint,
UINT nFlags /*= 0U*/,
CWnd *pParentWnd /*= NULL*/,
int nRootFolder /*= CSIDL_DESKTOP*/) :
m_nFlags(nFlags),
m_pItemIdList(NULL),
m_pParentWnd(pParentWnd),
m_pRoot(NULL)
{
ASSERT_NULL_OR_VALID(pParentWnd); // Paranoiaif (!m_strHint.LoadString(nHint))
AfxThrowResourceException();
m_pRoot = ResolveCsidl(nRootFolder);
}
/**
* \brief Construct a CYABFFW dialog rooted at an arbitrary folder
*
* \sa SetText
*
*
* \param strHint String to display in the dialog; can be changed
* later by SetText.
*
* \param strRoot Path to a directory in the filesystem to root
* the browse operation at
*
* \param nFlags Optional Bit vector made up of \c BIF_* values
* controlling various aspects of the dialog's appearance &
* behavior
*
* \param pParentWnd Points to the parent or owner window object
* (of type CWnd) to which the dialog object belongs. If it is
* NULL, the dialog objects parent window is set to the main
* application window.
*
*
*/
CYABFFW::CYABFFW(const CString &strHint,
const CString &strRoot,
UINT nFlags /*= 0U*/,
CWnd *pParentWnd /*= NULL*/) :
m_nFlags(nFlags),
m_strHint(strHint),
m_pItemIdList(NULL),
m_pParentWnd(pParentWnd),
m_pRoot(NULL)
{
ASSERT_NULL_OR_VALID(pParentWnd); // Paranoia
m_pRoot = ResolveFsPath(strRoot);
}
/**
* \brief Load the "hint" from Resource, but root the browse
* operation at an arbitrary folder
*
* \sa SetText
*
*
* \param nHint Resource Id of a string to be loaded and used in
* the dialog; can be changed later by SetText
*
* \param strRoot Path to a directory in the filesystem to root
* the browse operation at
*
* \param nFlags Optional Bit vector made up of \c BIF_* values
* controlling various aspects of the dialog's appearance &
* behavior
*
* \param pParentWnd Points to the parent or owner window object
* (of type CWnd) to which the dialog object belongs. If it is
* NULL, the dialog objects parent window is set to the main
* application window.
*
*
*/
CYABFFW::CYABFFW(int nHint,
const CString &strRoot,
UINT nFlags /*= 0U*/,
CWnd *pParentWnd /*= NULL*/) :
m_nFlags(nFlags),
m_pItemIdList(NULL),
m_pParentWnd(pParentWnd),
m_pRoot(NULL)
{
ASSERT_NULL_OR_VALID(pParentWnd); // Paranoiaif (!m_strHint.LoadString(nHint))
AfxThrowResourceException();
m_pRoot = ResolveFsPath(strRoot);
}
#ifdef _DEBUG
CString CYABFFW::GetDisplayName() const
{
return m_strDisplayName;
}
/**
* \brief Retrieve the ITEMIDLIST of the selected item
*
*
* \return A constant pointer to an \c ITEMIDLIST naming the
* selected object.
*
*
* Note that the caller is \em not responsible for cleaning up
* this memory. The flip side is that this pointer's validity is
* bounded by the lifetime of its owning CYABFFW instance.
*
*
*/
LPCITEMIDLIST CYABFFW::GetItemIdList() const
{
return m_pItemIdList;
}
CString CYABFFW::GetPath() const
{
return m_strPath;
}
#endif // _DEBUGint CYABFFW::DoModal()
{
// We'll need this eventually ...
HRESULT hr;
IMalloc *pMalloc;
if (FAILED(hr = ::SHGetMalloc(&pMalloc)))
AfxThrowOleException(hr);
// Fill out a 'BROWSEINFO' structure to hand to 'SHBrowseFor-// Folder':
BROWSEINFO browseInfo;
::ZeroMemory(&browseInfo, sizeof(BROWSEINFO));
browseInfo.hwndOwner = (NULL == m_pParentWnd ? NULL :
m_pParentWnd->m_hWnd);
browseInfo.pidlRoot = m_pRoot;
// Use a CString for memory management
browseInfo.pszDisplayName =
m_strDisplayName.GetBufferSetLength(MAX_PATH);
browseInfo.lpszTitle = m_strHint;
browseInfo.ulFlags = m_nFlags;
browseInfo.lpfn = BrowseCallbackProc;
browseInfo.lParam = (long)this;
if (NULL != m_pItemIdList)
FreeItemIdList(); // Probably never happen, but ...if (NULL == (m_pItemIdList = ::SHBrowseForFolder(&browseInfo)))
{
// User Cancelled out - clean up & bail.
m_strDisplayName.ReleaseBuffer();
pMalloc->Release();
return IDCANCEL;
}
// Right - if we're here, the user actually selected an item.// Try to get a full path. This will fail if the selected item// is not part of the FileSystem.
::SHGetPathFromIDList(m_pItemIdList,
m_strPath.GetBufferSetLength(MAX_PATH));
// Cleanup time ...
m_strPath.ReleaseBuffer();
m_strDisplayName.ReleaseBuffer();
pMalloc->Release();
// Note: m_pItemIdList has *not* been freed! We keep around in// case the caller wants to retrieve it later. It will// ultimately be freed in the destructor.return IDOK;
}
void CYABFFW::EnableOk(BOOL bEnable)
{
ASSERT(NULL != m_hWnd);
SendMessage(BFFM_ENABLEOK, 0U, bEnable ? 1L : 0L);
}
void CYABFFW::SetSelection(LPCITEMIDLIST pItemIdList)
{
ASSERT(NULL != m_hWnd);
ASSERT_POINTER(pItemIdList, ITEMIDLIST);
SendMessage(BFFM_SETSELECTION, (WPARAM)FALSE,
(LPARAM)pItemIdList);
}
void CYABFFW::SetSelection(const CString &strPath)
{
ASSERT(NULL != m_hWnd);
SendMessage(BFFM_SETSELECTION, (WPARAM)TRUE,
(LPARAM)(LPCTSTR)strPath);
}
void CYABFFW::SetText(const CString &strNewText)
{
ASSERT(NULL != m_hWnd);
SendMessage(BFFM_SETSTATUSTEXT, 0U, reinterpret_cast<LPARAM>
(static_cast<LPCTSTR>(strNewText)));
}
void CYABFFW::OnInitBFFDialog()
{ /* No handling by default */ }
void CYABFFW::OnBFFSelChanged(LPITEMIDLIST /*pNewSel*/)
{ /* No handling by default */ }
/**
* \brief Called when the user types an invalid name
*
*
* \param strBadName Invalid item name provided by the user
*
* \return Return \c TRUE to indicate that the dialog may be
* dismissed, \c FALSE to force the dialog to remain displayed
*
*
*/
BOOL CYABFFW::OnBFFValidateFailed(const CString & /*strBadName*/)
{ return TRUE; /* No handling by default */ }
CYABFFW::~CYABFFW()
{
HRESULT hr;
IMalloc *pMalloc;
if (FAILED(hr = ::SHGetMalloc(&pMalloc)))
AfxThrowOleException(hr);
pMalloc->Free(m_pRoot);
FreeItemIdList(pMalloc);
}
BEGIN_MESSAGE_MAP(CYABFFW, CWnd)
//{{AFX_MSG_MAP(CYABFFW)// NOTE - the ClassWizard will add and remove mapping macros// here.//}}AFX_MSG_MAP
END_MESSAGE_MAP()
int CYABFFW::BrowseCallbackProc(HWND hWnd,
UINT nMsg,
LPARAM lParam,
LPARAM lpData)
{
CYABFFW *pWnd = reinterpret_cast<CYABFFW*>(lpData);
ASSERT_VALID(pWnd);
ASSERT(NULL == pWnd->m_hWnd || hWnd == pWnd->m_hWnd);
if (NULL == pWnd->m_hWnd && !pWnd->SubclassWindow(hWnd))
AfxThrowOleException(HRESULT_FROM_WIN32(::GetLastError()));
switch (nMsg)
{
case BFFM_INITIALIZED:
// Indicates the browse dialog box has finished// initializing. The lParam value is zero.
pWnd->OnInitBFFDialog();
return 0;
case BFFM_SELCHANGED:
// Indicates the selection has changed. The lParam parameter// points to the item identifier list for the newly selected// item.
{
LPITEMIDLIST p = reinterpret_cast<LPITEMIDLIST>(lParam);
ASSERT_POINTER(p, ITEMIDLIST);
pWnd->OnBFFSelChanged(p);
return 0;
}
case BFFM_VALIDATEFAILED:
// Indicates the user typed an invalid name into the edit box// of the browse dialog box. The lParam parameter is the// address of a character buffer that contains the invalid// name. An application can use this message to inform the// user that the name entered was not valid. Return zero to// allow the dialog to be dismissed or nonzero to keep the// dialog displayed.
{
LPTSTR p = reinterpret_cast<LPTSTR>(lParam);
ASSERT(!::IsBadStringPtr(p, UINT_MAX));
BOOL bDismissOk = pWnd->OnBFFValidateFailed(CString(p));
return bDismissOk ? 0 : 1;
}
default:
TRACE(_T("WARNING: Unknown message 0x%08x (0x%08x) ")
_T("passed to CYABFFW::BrowseCallbackProc!"),
nMsg, lParam);
return 0;
} // End switch on nMsg.
}
void CYABFFW::FreeItemIdList(IMalloc *pMalloc /*= NULL*/)
{
if (NULL == m_pItemIdList)
return;
bool bWeRelease = false;
if (NULL == pMalloc)
{
bWeRelease = true;
HRESULT hr;
IMalloc *pMalloc;
if (FAILED(hr = ::SHGetMalloc(&pMalloc)))
AfxThrowOleException(hr);
}
pMalloc->Free(m_pItemIdList);
if (bWeRelease)
pMalloc->Release();
m_pItemIdList = NULL;
}
LPITEMIDLIST CYABFFW::ResolveCsidl(int nCsidl) const
{
// Short-circuit special case ...if (CSIDL_DESKTOP == nCsidl)
returnNULL;
LPITEMIDLIST pidlRoot;
HRESULT hr = ::SHGetFolderLocation(NULL, nCsidl, NULL, 0U,
>pidlRoot);
if (FAILED(hr))
{
ASSERT(NULL == pidlRoot);
AfxThrowOleException(hr);
}
return pidlRoot; // Caller assumes responsibility
}
LPITEMIDLIST CYABFFW::ResolveFsPath(const CString &strPath) const
{
USES_CONVERSION;
# ifdef _DEBUG
DWORD dwFileAttrs = ::GetFileAttributes(strPath);
ASSERT(0xffffffff != dwFileAttrs &&
FILE_ATTRIBUTE_DIRECTORY & dwFileAttrs);
# endif // _DEBUG
HRESULT hr;
IShellFolder *pDesktop;
if ( FAILED(hr = ::SHGetDesktopFolder(&pDesktop)) )
AfxThrowOleException(hr);
// Unfortunately, T2OLE expects a non-const string, so ...
LPOLESTR p2 = T2OLE(const_cast<LPTSTR>(
static_cast<LPCTSTR>(strPath)));
LPITEMIDLIST pItemIdList;
if ( FAILED(hr = pDesktop->ParseDisplayName(NULL, NULL,
p2,
NULL,
&pItemIdList,
NULL)) )
{
pDesktop->Release();
AfxThrowOleException(hr);
}
pDesktop->Release();
return pItemIdList; // Caller assumes responsibility
}
Add www.codeguru.com to your favorites Add www.codeguru.com to your browser search box IE 7 | Firefox 2.0 | Firefox 1.5.xReceive news via our XML/RSS feed
RATE THIS ARTICLE:
Excellent Very Good Average Below Average Poor
(You must be signed in to rank an article. Not a member? Click here to register)