Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TSaveDialog fails with client visual styles disabled

I am trying to use a TSaveDialog in Delphi XE6:

if not SaveDialog1.Execute(0) then
   Exit;

The call immediately returns false, without displaying any dialog. I traced it down to the act of creating the shell Save Dialog COM object:

function TCustomFileSaveDialog.CreateFileDialog: IFileDialog;
var
       LGuid: TGUID;
begin
  LGuid := CLSID_FileSaveDialog;

  CoCreateInstance(LGuid, nil, CLSCTX_INPROC_SERVER,
    StringToGUID(SID_IFileSaveDialog), Result);
end;

The call to CoCreateInstance is failing. I created minimal code to reproduce the issue:

procedure TForm1.Button1Click(Sender: TObject);
const
   CLSID_FileSaveDialog: TGUID = '{C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}';
begin
   CreateComObject(CLSID_FileSaveDialog);
end;

It throws the EOleSysError exception:

0x80040111: ClassFactory cannot supply requested class, ClassID: {C0B4E2F3-BA21-4773-8DBA-335EC946EB8B}

My application is using Version 6 of the Common Controls library (6.0.7601.18837), but i realized it only happens if the user has disabled visual styles for my application:

enter image description here

We're still using version 6 of the common controls library, just that IsAppThemed returns false.

Note: I know a lot of people mistakenly believe that:

  • Visual Styles API only works if we have version 6 of Comctrl32.dll loaded
  • If version 6 of Comctrl32.dll is loaded, then Visual Styles API will work
  • If we're not using ComCtrl v6 then that means Visual Styles are disabled
  • Visual Styles are disabled if we're using the old common controls library

The brute-force solution is to set the global UseLatestCommonDialogs to false.

But that's pretty bad, as it only applies to people who have disabled visual styles in their application:

  • the dialog continues to work on OS without visual styles (e.g. Windows Server 2008 R2)
  • the dialog continues to work with visual styles turned off (e.g. Windows 7 with visual styles turned off)

This means i cannot simply use IsAppThemed, as that also returns false if IsThemeActive is false.

| IsThemeActive | IsAppThemed | Disable visual styles | Result    |
|---------------|-------------|-----------------------|-----------|
| True          | True        | Unchecked             | Works     |
| True          | False       | Checked               | Fails     |
| False         | False       | Unchecked             | Works     |
| False         | False       | Checked               | Fails     |

What i guess i'm asking is how to check the status of the Disble Visual Styles compat flag.

What i'm really asking is how to make the TSaveDialog work correctly in Delphi (without implying that reading the compat flag is part of the solution).

like image 450
Ian Boyd Avatar asked Dec 17 '15 20:12

Ian Boyd


1 Answers

You surely don't want to test for the compat flag. If you are going to test, you want to test what that flag controls. In this case whether or not themes are in use. If you are going to test like that then you should use the Vista style dialogs when the following condition holds:

IsWindowsVistaOrGreater and Winapi.UxTheme.InitThemeLibrary and Winapi.UxTheme.UseThemes

Otherwise you need to use the old XP style dialogs. You can make this happen with the following code:

UseLatestCommonDialogs := IsWindowsVistaOrGreater and Winapi.UxTheme.InitThemeLibrary 
  and Winapi.UxTheme.UseThemes;

The problem with this though is that you will disable new style dialogs when the user is running with the Windows Classic theme. Which I am sure that you do not want.

So you could take a functionality based approach. That is attempt to use the new style dialogs and fallback on the old style dialog if the new fails. So, attempt to create an IFileSaveDialog. Assign UseLatestCommonDialogs based on whether or not that succeeds.


On the other hand, this compat setting is intended to be used on applications that don't work correctly when themes are enabled. Your application does work correctly under themes and I think it is entirely justifiable for you to say that your application does not support that particular compat mode.

You are not expected to support compatibility modes. For instance, if you stop supporting XP, then you would not be expected to support the XP compat shims.

On reflection that's my advice to you. Simply do nothing. If your users ask about your app failing in that way, tell them that you don't support that compat mode. It is not your job to make your application support compatibility modes.

like image 194
David Heffernan Avatar answered Sep 28 '22 22:09

David Heffernan