One common beginner’s mistake
Someone may use the following code to show a File Open dialog:
Example 1
CFileDialog dlgOpenFile(TRUE); dlgOpenFile.GetOFN().Flags |= OFN_ALLOWMULTISELECT; if(IDOK == dlgOpenFile.DoModal()) { // get selected files POSITION pos = dlgOpenFile.GetStartPosition(); while(NULL != pos) { CString strFilePath = dlgOpenFile.GetNextPathName(pos); // ... do something with strFilePath. } }
Setting OFN_ALLOWMULTISELECT flag tells to Open File dialog to allow multiple file selection. That works fine as log as the user selects a pretty small number of files. Otherwise, it fails with no warning and no file name (or even worse, some garbage) is retrieved.
What is the cause?
If the first parameter of constructor is TRUE, CFileDialog uses GetOpenFileName Windows API function that takes an OPENFILENAME structure. No matter if multiple selection is set, it retrieves all selected (full path and) file names in one single bufffer, which is poited by lpstrFile member of OPENFILENAME. By default, the size of that buffer is set to MAX_PATH (about 260) characters which is pretty small.
Setting a large enough buffer before DoModal
One solution is to make pstrFile member of OPENFILENAME to point to a large enough buffer, able to keep much more file names. Also, the nMaxFile structure member must be set to actual extended buffer size (in characters).
Example 2
CFileDialog dlgOpenFile(TRUE); // add OFN_ALLOWMULTISELECT flag dlgOpenFile.GetOFN().Flags |= OFN_ALLOWMULTISELECT; try { // set a buffer to keep at least 100 full path and file names const int nBufferSize = 100 * (MAX_PATH + 1) + 1; CString strBuffer; LPTSTR pBuffer = strBuffer.GetBufferSetLength(nBufferSize); dlgOpenFile.GetOFN().lpstrFile = pBuffer; dlgOpenFile.GetOFN().nMaxFile = nBufferSize; // show modal Open File dialog if(IDOK == dlgOpenFile.DoModal()) { // get selected files POSITION pos = dlgOpenFile.GetStartPosition(); while(NULL != pos) { CString strFilePath = dlgOpenFile.GetNextPathName(pos); // ... do something with strFilePath. } } // release buffer strBuffer.ReleaseBuffer(); } catch(CException* e) { e->ReportError(); e->Delete(); }
It works but is not very ellegant because uses a fixed buffer. Next, well show a method which dynamically estimates and allocate the necessary buffer.
Overriding CFileDialog::OnFileNameChange
CFileDialog::OnFileNameChange is a virtual function that is called as a response to CDN_SELCHANGE notification, sent when the user changes the selection. We can override that function in our own CFileDialog-derived class, then send CDM_GETFOLDERPATH and CDM_GETSPEC mesages in order to estimate the required buffer size.
Example 3
class CMyFileDialog : public CFileDialog { // Attributes private: LPTSTR m_pFileBuff; // ... // Overrides protected: virtual void OnFileNameChange(); // ... };
void CMyFileDialog::OnFileNameChange() { ASSERT(GetOFN().Flags & OFN_EXPLORER); CWnd* pParent = GetParent(); ASSERT(NULL != pParent); // get required sizes for path and file names buffers const UINT nPathSize = (UINT)pParent->SendMessage(CDM_GETFOLDERPATH); const UINT nFileSize = (UINT)pParent->SendMessage(CDM_GETSPEC); const UINT nMaxFile = GetOFN().nMaxFile; if(nPathSize + nFileSize > nMaxFile) { delete []m_pFileBuff; m_pFileBuff = new TCHAR[nPathSize + nFileSize + 1]; // set new buffer GetOFN().lpstrFile = m_pFileBuff; GetOFN().nMaxFile = nPathSize + nFileSize + 1; } CFileDialog::OnFileNameChange(); }
Pretty nice but it works only if CFileDialog has not Vista style. I’ll show how to resolve this, in the next article.
Resources
- MSDN: CFileDialog Class
- MSDN: CFileDialog::OnFileNameChange
- Windows Dev Center: Common Dialog Box Messages
See also
- Codexpert blog: File Open Dialog with Multiple Selection – Part 2: Vista Style