Cancelled TSaveDialog accepts filename after OnCanClose is cancelled.
- Lazarus/FPC Version: Lazarus 2.3.0 (rev main-2_3-3501-g56198866) FPC 3.2.2 i386-win32-win32/win64
- Operating System: Windows 11
- CPU / Bitness: Intel, 32bit
What happens
When a user wants a SaveDialog to accept only non-existing files he can set the dialog's option odOverwritePrompt
. But if he wants to display a different error message he can implement a handler for the dialog's OnCanClose
event. However, when this handler returns the parameter CanClose
as being false
and the user closes the save dialog by means of the Cancel button, the dialog's Execute
method still returns true
. So, when the user saves a file in case of Execute=true
the file is overwritten although he did not want this, and although he cancelled the file dialog.
What did you expect
When Cancel is pressed in the SaveDialog the Execute
method should return false
, like it happens in Delphi.
Steps to reproduce
The attached project contains a button which opens a SaveDialog and shows a message depending on whether the Execute
method returns true
("Saved") or false
("Operation cancelled"). It also implements a handler for its OnCanClose
event asking for confirmation if the currently selected file already exists.
- Run the project and click the button to open the SaveDialog.
- Double-click on any existing file.
- The message of the
OnCanClose
handler appears, asking for confirmation to overwrite the file. Click on "No" ("do not overwrite"). - Focus returns to the SaveDialog. Click "Cancel".
- The message "File saved" appears rather than the expected "Operation cancelled". If a real writing operation would have been executed the original file would have been overwritten although the user had not allowed this.
In Delphi (a Delphi-compatibile version of the project is contained as well), the "Operation cancelled" message appears, as expected.
Further information
The issue occurs only on Windows. On Linux, the bug does not exist (see also forum post https://forum.lazarus.freepascal.org/index.php/topic,63566.msg481676.html#msg481676).
Fix
The debugger leads into the method TFileDialogEvents.OnFileOk
of the win32 widgetset (this explains why the bug does not exist on Linux). This method sets the variable FDialog.UserChoice
to mrOK
which is needed by the dialog`s DoCanClose
method. However, this variable is not changed anywhere even if ``DoCanClose`` returned a ``CanClose=false``.
Setting FDialog.UserChoise
to mrNone
in the else
branch of TFileDialogEvent.OnFileOK
solves the issue.
function TFileDialogEvents.OnFileOk(pfd: IFileDialog): HResult; stdcall;
var
CanClose: Boolean;
begin
Result := TWin32WSOpenDialog.ProcessVistaDialogResult(pfd, FDialog);
if Succeeded(Result) then
begin
FDialog.UserChoice := mrOK; //DoCanClose needs this
CanClose := True;
FDialog.DoCanClose(CanClose);
if CanClose then
begin
Result := S_OK;
end
else
begin
FDialog.UserChoice := mrNone; // <--- added
Result := S_FALSE;
end;
end;
end;
(removed the {ifdef directives for better readability)