在 Inno Setup 中避免 "Failed to expand shell folder constant userdocs" 错误

Avoiding "Failed to expand shell folder constant userdocs" errors in Inno Setup

我将一些示例文档安装到 Windows 上标准 'My documents' 文件夹的 'PerfectTablePlan' 子文件夹中。这适用于 99% 以上的用户。但是,如果用户没有 'My documents' 文件夹,我会收到许多格式如下的丑陋错误消息:

Internal error:Failed to expand shell folder constant “userdocs”

这对用户来说不是很鼓舞人心!

可以不为这些用户安装示例(或将它们安装在其他地方)。但不要显示丑陋的错误消息。

问题似乎来自{userdocs} 的ExpandConstant 宏扩展。

有没有什么方法可以在不使用宏的情况下获取 'My documents' 的路径?

或者有什么方法可以抑制错误信息? ExpandConstant 抛出异常: http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_expandconstant

我的 .iss 文件的相关部分如下所示:

#define MySampleDir "{code:SampleDirRoot}\PerfectTablePlan"
...
[Files]
Source: ..\binaries\windows\program\plans\*_v14.tp; DestDir: {#MySampleDir}\; Flags: ignoreversion onlyifdoesntexist createallsubdirs recursesubdirs uninsneveruninstall;
Source: ..\binaries\windows\program\plans\*_v3.tps; DestDir: {#MySampleDir}\; Flags: ignoreversion onlyifdoesntexist createallsubdirs recursesubdirs uninsneveruninstall;
...
[Code]
function SampleDirRoot(Param: String): String;
begin
if DirExists( ExpandConstant('{userdocs}') ) then
Result := ExpandConstant('{userdocs}')
else
Result := ExpandConstant('{allusersprofile}')
end;

异常:

Failed to expand shell folder constant 'constant name'

在内部调用SHGetFolderPath function (called from inside ExpandConstant when expanding a shell folder constant) returns an empty path string for the given folder CSIDL, in this case for the CSIDL_PERSONAL标识符时引发。

这意味着用户没有 CSIDL_PERSONAL 文件夹。这让我想知道如何将 Windows' 用户帐户配置为没有该文件夹。那么,您可以通过在 try..except 块中捕获引发的内部异常来解决此问题(或 Windows 配置错误?):

[Code]
function SampleDirRoot(Param: string): string;
var
  Folder: string;
begin
  try
    // first try to expand the {userdocs} folder; if this raises that
    // internal exception, you'll fall down to the except block where
    // you expand the {allusersprofile}
    Folder := ExpandConstant('{userdocs}');
    // the {userdocs} folder expanding succeded, so let's test if the
    // folder exists and if not, expand {allusersprofile}
    if not DirExists(Folder) then
      Folder := ExpandConstant('{allusersprofile}');
  except
    Folder := ExpandConstant('{allusersprofile}');
  end;
  // return the result
  Result := Folder;
end;

但我从未听说过没有 CSIDL_PERSONAL 文件夹的可能性。请注意,上面的代码仅保护 {userdocs} 常量。

我们在这里使用文件夹重定向,并且在其他应用程序(GnuCash、UFile)中也遇到过类似的问题。我们案例中的问题是一个称为重定向文件夹迁移*的功能的结果,在某些情况下,迁移完成后,该功能不会更新用户的注册表设置以指向新位置(请注意,此迁移可能在 GPO 完成后几周发生设置).

无论如何,并不是条目是空白的,而是它指向一个 SERVER\SHARE 几周前已经下线的条目。

运行 每台机器上每个活动用户的 gpupdate /force 解决了我们的问题,因为它更新了注册表,然后告诉用户他必须注销/登录。


*重定向文件夹迁移允许管理员指定每个用户的(例如)我的文档文件夹应从一个网络位置移动到另一个网络位置。它以缓慢的、有节制的方式执行此操作,下次用户登录到域(上的任何工作站)时(因此,如果您的最后一个用户从现在起一个月后才登录,则该过程需要一个月才能完成)。这在理论上是一个可爱的想法,但在实践中是一个很大的 PITA。

顺便说一下,'共享不存在,因为它正在冒充另一个用户' 对于正确重定向的文件夹来说应该不是问题,因为重定向应该使用 URL,而不是映射的驱动器号(例如 \\server\RedirectedUserFolders\SOME-USER\My 文档,但如果删除某些默认权限,权限可能会出现问题)。