Avoiding “Failed to expand shell folder constant u

2019-05-03 16:45发布

I install some sample documents into a 'PerfectTablePlan' sub-folder of the standard 'My documents' folder on Windows. This works fine for 99%+ of users. But if a user doesn't have a 'My documents' folder I get a number of ugly error messages of the form:

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

This is not very confidence inspiring for the user!

It is acceptable to not install the samples for these users (or install them somewhere else). But not to show the ugly error messages.

The problem seems to come from the ExpandConstant macro expansion of {userdocs}.

Is there some way I can get the path of 'My documents' without using a macro?

Or some way to suppress the error message? ExpandConstant throws an exception: http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_expandconstant

The relevant parts of my .iss file looks like this:

#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;

2条回答
祖国的老花朵
2楼-- · 2019-05-03 16:58

We use folder redirection here and have had issues similar to this with other apps (GnuCash, UFile). The problem in our case was a result of a feature called Redirected Folder Migration*, which in some cases did not update the user's registry settings to point to the new location after the migration was completed (note this migration might occur weeks after the GPO was set).

Anyway, it's not that the entry was blank, but that it pointed to a SERVER\SHARE that had been brought offline weeks earlier.

Running a gpupdate /force for every active user on every machine fixed our problem, as it updated the registry and then told the user he had to logoff / logon.


*Redirected Folder Migration allows an admin to specify that each user's (e.g.) My Documents folder should be moved from one network location to another. It does this in a slow, measured fashion, the next time the user logs on to (any workstation on) the domain (so if your last user only logs on a month from now, the process takes a month to complete). It's a lovely idea in theory, but in practice is a big PITA.

Incidentally, the 'share doesn't exist because it's impersonating another user' thing shouldn't be an issue with properly redirected folders because the redirect should use a URL, not a mapped drive letter (e.g. \\server\RedirectedUserFolders\SOME-USER\My Documents, although permissions might be an issue if you remove some default rights).

查看更多
Fickle 薄情
3楼-- · 2019-05-03 17:11

The exception:

Failed to expand shell folder constant 'constant name'

is raised when the internally called 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 identifier.

That means the user doesn't have the CSIDL_PERSONAL folder. It makes me wonder how can one configure Windows' user account to not have that folder. Well, you can workaround this issue (or Windows misconfiguration ?) by catching the raised internal exception in the try..except block:

[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;

But I've never heard about possibility of not having the CSIDL_PERSONAL folder. Please note, that the above code protects only the {userdocs} constant.

查看更多
登录 后发表回答