TSaveDialog 失败,客户端视觉样式被禁用

TSaveDialog fails with client visual styles disabled

我正在尝试在 Delphi XE6:

中使用 TSaveDialog
if not SaveDialog1.Execute(0) then
   Exit;

立即调用returnsfalse,不显示任何对话框。我将其追溯到创建 shell Save Dialog COM 对象的行为:

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

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

CoCreateInstance 的调用失败。我创建了最少的代码来重现该问题:

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

它抛出 EOleSysError 异常:

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

我的应用程序 使用公共控件库的版本 6 (6.0.7601.18837),但我意识到它只有在用户禁用了我的应用程序的视觉样式时才会发生:

我们仍在使用通用控件库的第 6 版,只是 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

暴力解决方案是将全局 UseLatestCommonDialogs 设置为 false。

但这很糟糕,因为它只适用于在应用程序中禁用视觉样式的人:

这意味着我不能简单地使用 IsAppThemed,因为如果 IsThemeActive 为假,那么 returns 也为假。

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

我想我想问的是如何检查 Disble Visual Styles 兼容标志的状态。

我真正想问的是如何使 TSaveDialog 在 Delphi 中正常工作(并不意味着读取兼容标志是解决方案的一部分)。

您肯定不想测试兼容标志。如果你要测试,你想测试那个标志控制的是什么。在这种情况下,主题是否正在使用中。如果您打算那样进行测试,那么当满足以下条件时,您应该使用 Vista 风格的对话框:

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

否则您需要使用旧的 XP 样式对话框。您可以使用以下代码实现此目的:

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

问题在于,当用户使用 运行 Windows 经典主题时,您将禁用新样式对话框。我相信你不想要的。

因此您可以采用基于功能的方法。那就是尝试使用新样式对话框,如果新样式对话框失败,则回退到旧样式对话框。因此,尝试创建一个 IFileSaveDialog。根据是否成功分配UseLatestCommonDialogs


另一方面,此兼容设置旨在用于在启用主题时无法正常工作的应用程序。您的应用程序在主题下确实可以正常工作,我认为您可以完全有理由说您的应用程序不支持该特定的兼容模式。

您不应支持兼容模式。例如,如果您停止支持 XP,那么就不会期望您支持 XP 兼容垫片。

经过反思,这是我给你的建议。什么都不做。如果您的用户询问您的应用程序以这种方式失败,请告诉他们您不支持该兼容模式。让您的应用程序支持兼容模式不是您的工作。