SpecialFolder.Preferences returns incorrect value

Hello

In RS2012r2.1 SpecialFolder.Preferences should return “C:\Users\\AppData\Roaming” on Windows Vista / 7 / 8.

However, I receive a crash report from the occasional user indicating that it returned “C:\Users\\Application Data”.

In these cases, attempting to create a subfolder “C:\Users\\Application Data\something” failed, presumably because of permission denied, or path not found, and thus the application crashed.

I imagine that this is likely to be a configuration problem specific to that machine.

Could anyone suggest some registry modification or otherwise which might fix this PC and make SpecialFolder.Preferences return a healthy writable folder?

In your shoes I would point to Specialfolder.Documents to be safe.

Does Specialfolder.ApplicationData have the same issue for you?

I can’t say for certain - the crash report came from a user’s PC. The report does indicate that SpecialFolder.SharedApplicationData returned with “C:\ProgramData” which seems to be correct.

@Tom Sanham
We return the value that is set in the registry for the computer. If that value has been changed by an administrator, its possible that they have also set the directory to be write protected. It seems that the prudent thing to do is to check to see that the parent folderitem is not nil and IsWritable before trying to use it.

Thanks for the info.

Could you let me know which registry key is accessed by SpecialFolder.Preferences?

Windows does “registry virtualization” and “file virtualization” which can give some of these symptoms - folders aren’t where you expect them to be:
http://support.microsoft.com/kb/927387

The cure usually is to make sure your App has a manifest that says it’s compatible with Vista (or 7, or…) so the virtualization doesn’t happen.

That’s bad advice. The Docs folder should only hold data that the user conciously puts in there, not data the app needs to put for its own workings.

More generally, the rule is:

Documents: User chooses to put data there (i.e. the may user nagivate to it).
ApplicationData: App puts data there that the user should never have to navigate to. This may be used for preference files as well we for global data file (e.g. database) that the app manages for the user without the user ever choosing to “open” or “save as” those files in there.
Preferences folder: This is very OS dependent. On OSX, no app should actually create or open files in it any more, but only use the OSX API functions to get or set individual preference values. On Win and Linux I am not sure, and Michael’s note is important in this regard, I think. For that reason, I would entirely avoid the Preferences folder and use the ApplicationData folder instead.

Note, though, that there may be differences between files + prefs shared by all users and those private to each individal users. I believe SpecialFolder offers “shared” variations for that purpose, even, or doesn’t it? Not having it ready to look at right now.

[quote=33144:@Tom Sanham]Thanks for the info.

Could you let me know which registry key is accessed by SpecialFolder.Preferences?[/quote]

I don’t know what registry key the OS may eventually read, I can say that we call SHGetSpecialFolderLocation and pass in CSIDL_APPDATA in the implementation for SpecialFolder.Preferences.

Thanks for the replies.

In the event that the application cannot both read and write to the folder returned by SpecialFolder.Preferences, I am experimenting with using a fallback routine which will call ExpandEnvironmentStringsW and use it to try to access the preferences folder (in the location it would usually be found in Windows):

[code]
declare function ExpandEnvironmentStringsW lib"kernel32.dll" (lpSrc as WString,lpDst as ptr,nSize as integer) as integer
dim ret_buf as new memoryBlock(10000)
dim ret_bytes as integer

if windows_xp then
ret_bytes=ExpandEnvironmentStringsW("%USERPROFILE%\Application Data",ret_buf,10000)
else
ret_bytes=ExpandEnvironmentStringsW("%USERPROFILE%\AppData\Roaming",ret_buf,10000)
end if

if ret_bytes=0 then
return nil
else
dim path as string = ret_buf.WString(0)
path=ConvertEncoding(path,Encodings.UTF8)
dim ret_f as new FolderItem(path,FolderItem.PathTypeShell)
return ret_f
end if[/code]