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:
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:
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).
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.