Vista Style CFileDialog and the compiling OS

Posted by Matt | Filed under , , , ,

A reader asks:

“To set bVistaStyle to FALSE, under which OS, we need to compile my project.
[…]
in the MSDN I found that the “This parameter is applicable only if you are compiling in Windows Vista.
Please see the following MSDN link: http://msdn.microsoft.com/en-us/library/wh5hz49d.aspx
Kindly let us know your feedback on this.”

I took a look at the documentation and it does indeed say that.  However, the documentation is incorrect.

There are no #defines or #pragmas around the constructor for CFileDialog that would restrict the OS at compile time.  What they may be intending to say is that the parameter is applicable only if you are running in Windows Vista.

I wrote a test program that shows a CFileDialog and I explicity set the bVistaStyle flag to TRUE.  I Compiled it on XP and I got the XP-style file open dialog (which is to be expected).  I then copied the EXE to my Vista computer (I did not recompile).  There, I got the Vista style dialog.

So you can safely compile on XP and it will still show the Vista dialog on Vista.

Note that this is restricted to Visual Studio 2008.  The parameter is not present in Visual Studio 2005 (out of the box).

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.