TSaveDialog Options ofAllowMultiSelect doesn't work properly on a W10 system

337 Views Asked by At

I have an old application, untouched for a long time, built with C++ Builder 2009, that still works fine. That is to say .. Today I noticed some of the TSaveDialog->Options don't work as intended on my Windows 10 system. To make sure I'm not dreaming I tested the same application on an older Windows version (I tried XP) and there it worked perfectly fine as intended.

The TSaveDialog instance is setup at design time with Options: [ofHideReadOnly,ofAllowMultiSelect,ofEnableSizing]

I noticed today (on Windows 10) that ofAllowMultiSelect doesn't work anymore ? Instead ofOverwritePrompt is (incorrectly) used ! In other words I cannot select two ore more files anymore and when I select a file that already exists I first get a 'Confirm Save As' dialog.

When I compile again on my Windows 10 system, using C++ Builder 2009, in debug mode and inspect Options, the debugger seems to (still) properly see ofHideReadOnly, ofAllowMultiSelect, ofEnableSizing, yet the problem persists. So it's not as if the values changed somehow ?

When I try at runtime:

SaveDialog->Options.Clear() ;
SaveDialog->Options << ofHideReadOnly << ofEnableSizing << ofAllowMultiSelect ;

the problem also persists !

When I remove ofAllowMultiSelect (at run time or at design time) 'Confirm Save As' is not shown anymore on an existing file (but I obviously also still can't select multiple files).

I'm flabbergasted by this to be honest ? Not sure what to do next ? I have no option to test a more recent c++ version but I'm also having difficulties comprehending how the compiler could be responsible here.

Any guidance appreciated.
Delphi tag added because of VCL overlap between c++ Builder and Delphi

1

There are 1 best solutions below

3
On BEST ANSWER

On Windows Vista and later, IF AND ONLY IF all of these conditions are met:

  • the global Dialogs::UseLatestCommonDialogs variable is true
  • and the TSaveDialog::Template property is NULL
  • and the TSaveDialog::OnIncludeItem, TSaveDialog::OnClose, and TSaveDialog::OnShow events have no handlers assigned

Then TSaveDialog will internally use the Win32 IFileSaveDialog interface, where the ofAllowMultiSelect option will be mapped to that dialog's FOS_ALLOWMULTISELECT option, which is NOT SUPPORTED by IFileSaveDialog, only by IFileOpenDialog, per the documentation:

FOS_ALLOWMULTISELECT
Enables the user to select multiple items in the open dialog. Note that when this flag is set, the IFileOpenDialog interface must be used to retrieve those items.

If the above 3 conditions are not satisfied, then TSaveDialog will internally use the Win32 GetSaveFileName() function instead, where the ofAllowMultiSelect option will be mapped to that dialog's OFN_ALLOWMULTISELECT option, which IS SUPPORTED by GetSaveFileName() 1.

That is why you are seeing behavioral differences when running your app on Windows XP vs Windows 10.

So, if you want the old TSaveDialog behavior on newer Windows versions, you need to make sure at least 1 of those 3 conditions is not satisfied. For instance, by setting UseLatestCommonDialogs=false before calling SaveDialog->Execute(), or by assigning an (empty) event handler to one of the OnIncludeItem/OnClose/OnShow events.

Or, you could simply call GetSaveFileName() directly, instead of using TSaveDialog at all.

1: However, just note that on Vista+, GetSaveFileName() is just a wrapper for IFileSaveDialog, and is provided only for backwards compatibility. So, you still might not get the exact behavior you want even if you did use GetSaveFileName() on Windows 10.


On a side note: this code does not work the way you think it does:

SaveDialog->Options.Clear();
SaveDialog->Options << ofHideReadOnly << ofEnableSizing << ofAllowMultiSelect;

The Options property is not actually updated! In both statements, the Options property is read from, returning a temporary TOpenOptions, which you are then modifying, but not assigning back to the Options property. IOW, the code is effectively doing the following:

TOpenOptions temp1 = SaveDialog->Options;
temp1.Clear();

TOpenOptions temp2 = SaveDialog->Options;
temp2 << ofHideReadOnly << ofEnableSizing << ofAllowMultiSelect;

So, to update the Options property correctly, use this instead:

SaveDialog->Options = TOpenOptions() << ofHideReadOnly << ofEnableSizing << ofAllowMultiSelect;