一个MFC风格的BrowseForFolder 封装类

本文介绍了一个MFC风格的ShellAPI函数SHBrowseForFolder()封装类CYABFFW,该类简化了文件夹浏览对话框的使用。通过多种构造函数提供了灵活的初始化选项,并实现了回调函数处理及资源释放等功能。

 原文链接:CYABFFW: Yet Another BrowseForFolder Wrapper

      以良好的MFC风格将Shell API函数SHBrowseForFolder()封装为一个CWnd子类。

使用示例1

CYABFFW dlg();
if  (IDOK  ==  dlg.DoModal())
{
    CString s 
= dlg.GetPath();
    
// Do something with `s' 

使用示例 2
CYABFFW dlg(_T( " Please select a directory " ),  //  Hint to user

            BIF_USE_NEWUI,                   
//  Flags for the dlg

            
this ,                             //  Parent window

            CSIDL_DRIVES);                   
//  Root of search

if  (IDOK  ==  dlg.DoModal())
{
    CString s 
= dlg.GetPath();
    
// Do something with `s' 


YABFFW.h
class CYABFFW : public CWnd
{
  
// Construction
public:
  
/**//// Construct with a textual hint, BFF flags, parent window, a
  
/// root folder expressed as a CSIDL value, and a default
  
/// selection expressed as a textual path

  CYABFFW(const CString &strHint = CString(), UINT nFlags = 0U,
          CWnd 
*pParentWnd = NULL, int nRootFolder = CSIDL_DESKTOP,
          
const CString &strDefaultSel = CString());
  
/**//// Construct with a default selection expressed as a CSIDL
  
/// value, a textual hint, BFF flags, parent window, & a root
  
/// folder expressed as a CSIDL value

  CYABFFW(int nDefaultSel, const CString &strHint = CString(),
          UINT nFlags 
= 0U, CWnd *pParentWnd = NULL,
          
int nRootFolder = CSIDL_DESKTOP);
  
/**//// Construct with a hint from Resource, BFF flags, parent
  
/// window, a root folder expressed as a CSIDL value, and a
  
/// default selection expressed as a textual path

  CYABFFW(int nHint, UINT nFlags = 0U, CWnd *pParentWnd = NULL,
          
int nRootFolder = CSIDL_DESKTOP,
          
const CString &strDefaultSel = CString());
  
/**//// Construct with a hint from Resource, a default selection
  
/// expressed as a CSIDL value, BFF flags, parent window, & a
  
/// root folder expressed as a CSIDL value

  CYABFFW(int nDefaultSel, int nHint, UINT nFlags = 0U,
          CWnd 
*pParentWnd = NULL, int nRootFolder = CSIDL_DESKTOP);
  
/**//// Construct with a textual hint, an arbitrary root folder,
  
/// BFF flags, a parent window, and a default selection
  
/// expressed as a textual path

  CYABFFW(const CString &strHint, const CString &strRoot,
          UINT nFlags 
= 0U, CWnd *pParentWnd = NULL,
          
const CString &strDefaultSel = CString());
  
/**//// Construct with a textual hint, a default selection
  
/// expressed as a CSIDL value, an arbitrary root folder, BFF
  
/// flags, & a parent window

  CYABFFW(int nDefaultSel, const CString &strHint, const CString &strRoot,
          UINT nFlags 
= 0U, CWnd *pParentWnd = NULL);
  
/**//// Construct with a hint from Resource, an arbitrary root
  
/// folder, BFF flags, a parent widnow, and a textual default
  
/// selection

  CYABFFW(int nHint, const CString &strRoot, UINT nFlags = 0U,
          CWnd 
*pParentWnd = NULL,
          
const CString &strDefaultSel = CString());
  
/**//// Construct with a hint from Resource, a default selection
  
/// expressed as a CSIDL value, an arbitrary root folder, BFF
  
/// flags, & a parent widnow

  CYABFFW(int nDefaultSel, int nHint, const CString &strRoot,
          UINT nFlags 
= 0U, CWnd *pParentWnd = NULL);

  
// Attributes
public:
  
/**//// 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;

  
// Operations
public:
  
/**//// Display the Dialog - returns IDOK or IDCANCEL
  int DoModal();
  
// Overrides
public:
  
/**//// Called when the BFF Dialog has initialized
  virtual void OnInitBFFDialog();
  
/**//// Called when the selection has changed in the tree control
  virtual void OnBFFSelChanged(LPITEMIDLIST pNewSel);
  
/**//// Called when the user types an invalid name
  virtual BOOL OnBFFValidateFailed(const CString &strBadName);

  
// ClassWizard generated virtual function overrides
  
//{{AFX_VIRTUAL(CYABFFW)
  
//}}AFX_VIRTUAL

  
// Implementation
public:
  
virtual ~CYABFFW();

  
// Generated message map functions
protected:
  
//{{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 callback
  static int CALLBACK BrowseCallbackProc(HWND hWnd, UINT nMsg,
                                         LPARAM lParam,
                                         LPARAM lpData);

private:
  
/**//// Free the memory referenced by m_pItemIdList
  void 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;
  
/**//// Default selection, if given as text (empty if none)
  CString m_strDefaultSel;
  
/**//// Default selection, if given as a CSIDL value (NULL if none)
  LPITEMIDLIST m_pDefaultSel;

}
;

YABFFW.cpp
CYABFFW::CYABFFW( const CString &strHint    /**//*= CString()*/,
                  UINT nFlags               
/**//*= 0U*/,
                  CWnd 
*pParentWnd          /**//*= NULL*/,
                  
int nRootFolder           /**//*= CSIDL_DESKTOP*/,
                  
const CString &strDefault /**//*= CString()*/     ) :
  m_nFlags(nFlags),
  m_strHint(strHint),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL),
  m_strDefaultSel(strDefault),
  m_pDefaultSel(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd); 
// Paranoia

  m_pRoot 
= ResolveCsidl(nRootFolder);
}



CYABFFW::CYABFFW(
int            nDefaultSel,
                 
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),
  m_pDefaultSel(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd); 
// Paranoia

  m_pRoot 
= ResolveCsidl(nRootFolder);
  m_pDefaultSel 
= ResolveCsidl(nDefaultSel);
}



CYABFFW::CYABFFW(
int            nHint,
                 UINT           nFlags        
/**//*= 0U*/,
                 CWnd          
*pParentWnd    /**//*= NULL*/,
                 
int            nRootFolder   /**//*= CSIDL_DESKTOP*/,
                 
const CString &strDefaultSel /**//*= CString()*/) :
  m_nFlags(nFlags),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL),
  m_strDefaultSel(strDefaultSel),
  m_pDefaultSel(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd); 
// Paranoia

  
if (!m_strHint.LoadString(nHint))
    AfxThrowResourceException();

  m_pRoot 
= ResolveCsidl(nRootFolder);
}


CYABFFW::CYABFFW(
int nDefaultSel,
                 
int nHint,
                 UINT nFlags      
/**//*= 0U*/,
                 CWnd 
*pParentWnd /**//*= NULL*/,
                 
int nRootFolder  /**//*= CSIDL_DESKTOP*/) :
  m_nFlags(nFlags),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL),
  m_pDefaultSel(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd); 
// Paranoia

  
if (!m_strHint.LoadString(nHint))
    AfxThrowResourceException();

  m_pRoot 
= ResolveCsidl(nRootFolder);
  m_pDefaultSel 
= ResolveCsidl(nDefaultSel);
}



 CYABFFW::CYABFFW(
const CString &strHint,
                  
const CString &strRoot,
                  UINT nFlags      
/**//*= 0U*/,
                  CWnd 
*pParentWnd /**//*= NULL*/,
                  
const CString &strDefaultSel /**//*=CString()*/) :
  m_nFlags(nFlags),
  m_strHint(strHint),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL),
  m_strDefaultSel(strDefaultSel),
  m_pDefaultSel(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd); 
// Paranoia

  m_pRoot 
= ResolveFsPath(strRoot);
}



CYABFFW::CYABFFW(
int            nDefaultSel,
                 
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),
  m_pDefaultSel(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd); 
// Paranoia

  m_pRoot 
= ResolveFsPath(strRoot);
  m_pDefaultSel 
= ResolveCsidl( nDefaultSel );
}



CYABFFW::CYABFFW(
int nHint,
                 
const CString &strRoot,
                 UINT nFlags 
/**//*= 0U*/,
                 CWnd 
*pParentWnd /**//*= NULL*/,
                 
const CString &strDefaultSel /**//*= CString()*/) :
  m_nFlags(nFlags),
  m_pItemIdList(NULL),
  m_pParentWnd(pParentWnd),
  m_pRoot(NULL),
  m_strDefaultSel(strDefaultSel),
  m_pDefaultSel(NULL)
{
  ASSERT_NULL_OR_VALID(pParentWnd); 
// Paranoia

  
if (!m_strHint.LoadString(nHint))
    AfxThrowResourceException();

  m_pRoot 
= ResolveFsPath(strRoot);
}



CYABFFW::CYABFFW(
int nDefaultSel,
                 
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); 
// Paranoia

  
if (!m_strHint.LoadString(nHint))
    AfxThrowResourceException();

  m_pRoot 
= ResolveFsPath(strRoot);
  m_pDefaultSel 
= ResolveCsidl(nDefaultSel);
}


#ifdef _DEBUG
CString CYABFFW::GetDisplayName() 
const
{
  
return m_strDisplayName;
}



LPCITEMIDLIST CYABFFW::GetItemIdList() 
const
{
  
return m_pItemIdList;
}


CString CYABFFW::GetPath() 
const
{
  
return m_strPath;
}

#endif  // _DEBUG

int 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 free-d in the destructor.

  
return IDOK;
}


void CYABFFW::OnInitBFFDialog()
{
  
if ( ! m_strDefaultSel.IsEmpty( ) )
  
{
    TRACE( _T( 
"Setting the default selection to %s.\n" ),
           ( LPCTSTR ) m_strDefaultSel );
    SendMessage( BFFM_SETSELECTION, TRUE, ( LPARAM ) ( LPCTSTR ) m_strDefaultSel );
  }

  
else if ( m_pDefaultSel )
  
{
    SendMessage( BFFM_SETSELECTION, FALSE, ( LPARAM ) m_pDefaultSel );
  }

}


void CYABFFW::OnBFFSelChanged(LPITEMIDLIST /**//*pNewSel*/)
/**//* No handling by default */ }


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!\n"),
          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)
    
return NULL;

  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
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值