Selecting Multiple Files using CFileDialog, Properly

Posted by Matt | Filed under , , , ,

I ran into an issue today.  I had a CFileDialog object set to select multiple files (using OFN_ALLOWMULTISELECT).  When I launched the dialog, I selected 24 files.  However, after looping through the files using CFileDialog::GetNextPathName(), there were only 10 being returned.

The problem is that there wasn’t an error being set anywhere (that I could find).  CFileDialog was being quite sneaky in that it wasn’t giving me any signal that 14 of the selected files were basically being ignored.  At first, I thought it was just a bug in Windows.  After some thinking, it turns out that there’s an internal buffer used to store these files.  This may seem very obvious to some people, but it wasn’t obvious to me.

Part of the OPENFILENAME structure is a buffer for holding (a) the default file when the dialog is opened, and (b) the selected files when the dialog is closed.  This member is called lpstrFile.  nMaxFile is also used in conjunction to specify the length of the buffer pointed to by lpstrFile.

By default, CFileDialog uses a buffer 260 characters long.  In my problem above, 260 characters is basically able to only hold 10 of the selected files.  The rest were just dropped quietly.

The workaround is to use a larger buffer.  It’s quite easy to do this:

// Create our dialog object
CFileDialog dialog(TRUE, NULL, NULL, OFN_HIDEREADONLY | 
  OFN_ALLOWMULTISELECT);

// This is the trick
const int nBufferSize = 128*1024; // May be excessive?
TCHAR *szBuffer = new TCHAR[nBufferSize];
memset(szBuffer, 0, sizeof(TCHAR) * nBufferSize);
dialog.m_ofn.lpstrFile = szBuffer;
dialog.m_ofn.nMaxFile = nBufferSize - 1;

// Show the dialog
if (dialog.DoModal() == IDOK)
{
  POSITION pos = dialog.GetStartPosition();
  while (pos != NULL)
  {
    CString sFile = dialog.GetNextPathName(pos);
    // Process the file
  }
}

delete[] szBuffer; // No leaks

The trick is to set lpstrFile and nMaxFile to a larger buffer.  128k may be excessive, but at least it will work.  Feel free to use whatever size buffer you want to.

Note: Above, I created the buffer on the heap using ‘new’.  If you don’t want to deal with ‘new’ and ‘delete’, you could create it on the stack like:

TCHAR szBuffer[nBufferSize];

and skip the ‘delete’ call.  However, this will eat up a good chunk of your stack space.  The default stack is 1 MB.  So 128k is 10% of your stack.  For this reason, I created it on the heap.

Comments

May 30, 2009 21:45

pingback

Pingback from aramail.net

Aramail Phone Cards » Selecting Multiple Files using CFileDialog, Properly

aramail.net

July 11, 2009 05:46

Thanseer

The content was very very helpful, i was wandering around the CFileDialog with the saME issue, and at the shore of conclusion same as you, that the bug of Microsoft themselves,, but i just opened my browser and search,
Any way thanx

Thanseer India

July 29, 2009 17:24

GregM

Here's how you get notified:

    if(IDOK != dlg.DoModal())
        {
        // see if the problem was a buffer that is too small.
        if(FNERR_BUFFERTOOSMALL == CommDlgExtendedError())

and here's how you fix it without having to allocate a huge buffer ahead of time

www.codeproject.com/KB/dialog/pja_multiselect.aspx

GregM United States

Comments are closed