When I'm working to resolve compatibility issues, there are always multiple options to mitigate. The solution we prefer to use is to update the code.
A common application code update is this: "my application used to write files to program files. It felt like as good a place to put it as any other. It had my application's name on it already, and because my users were admins, it worked fine. But now I see that this may not be as great a place to stick things as I once thought, because with UAC even Administrators run with standard user-like privileges most of the time. So, where should I put my files instead?"
The answer, as it turns out, is: it depends.
Let's look at the options, and when you might want to choose each.
First, you'll want to use the SHGetKnownFolderPath API function to pull the function if you are using native code. If you are using managed code, System.Environment.GetFolderPath will do the trick for you.
FOLDERID_ProgramData / System.Environment.SpecialFolder.CommonApplicationDataThe user would never want to browse here in Explorer, and settings changed here should affect every user on the machine. The default location is %systemdrive%\ProgramData, which is a hidden folder, on an installation of Windows Vista. You'll want to create your directory and set the ACLs you need at install time.
FOLDERID_Public / FOLDERID_PublicDocuments / System.Environment.GetEnvironmentVariable("public")The user would want to browse here in Explorer and double click to open the file. The default location is %public%, which has explicit links throughout Explorer, on an installation of Windows Vista. You'll want to create your directory and set the ACLs you need at install time.
FOLDERID_RoamingAppData / System.Environment.SpecialFolder.ApplicationDataThe user would never want to browse here in Explorer, and settings changed here should roam with the user. The default location is %appdata%, which is a hidden folder, on an installation of Windows Vista.
FOLDERID_LocalAppData / System.Environment.SpecialFolder.LocalApplicationDataThe user would never want to browse here in Explorer, and settings changed here should stay local to the computer. The default location is %localappdata%, which is a hidden folder, on an installation of Windows Vista.
FOLDERID_Documents / System.Environment.SpecialFolder.MyDocumentsThe user would want to browse here in Explorer and double click to open the file. The default location is %userprofile%\documents, which has explicit links throughout Explorer, on an installation of Windows Vista.
Now, you'll note that FOLDERID_Public is kind of the oddball here. System.Environment.GetFolderPath just calls SHGetFolderPath, which takes CSIDLs. There is no analogue for %public% here. However, we could have gone after CSIDL_COMMON_DOCUMENTS (FOLDERID_PublicDocuments) and dropped things there, but even though we just need to pass 0x2e (46) as the int argument, we don't offer that. Because we have this subset going, I'd probably start thinking about using p/invoke if I needed to support public documents (http://www.pinvoke.net/default.aspx/shell32.SHGetKnownFolderPath).
If you're using Visual Basic, you can also use My.Computer.FileSystem.SpecialDirectories, but it also doesn't seem to get any public documents love...
PingBack from http://msdnrss.thecoderblogs.com/2008/02/05/where-should-i-write-program-data-instead-of-program-files/
Did you see the post at blogs.msdn.com
"...I'd probably start thinking about using p/invoke if I needed to support public documents..."
Dear Microsoft .NET Framework Developers: Thanks so much for once again omitting something useful from the .NET framework. I take a great deal of pleasure in wasting part of the day researching my way around the potholes in your framework.
P/Invoke this.
Angry Ted:
Try this
IDictionary iDict =
Environment.GetEnvironmentVariables();
Enumerate & get the needed physical location
This whole situation is really annoying.
Yes, of course, data -- especially read/write data -- don't belong in the programs directory and should be installed elsewhere ... but there isn't any suitable place.
It's blindingly obvious that there will be apps that require a single set of data files to be usable by multiple users, and that there should therefore be 'standard' location for those shared data to be installed.
On the face of it the CSIDL_COMMON_APPDATA directory looks tailor-made for this use, but as it's hidden by default it's not ideal. In Vista the %public% directory is a strong contender ... but it's not available in XP so application developers whose users are still using XP or even 2k (and that's most of us) aren't going to touch it.
My advice would be to use the CSIDL_COMMON_APPDATA directory ... and to un-hide it if users are going to need to browse to it.
Hi Daniel,
For XP and Vista compat, why not use CSIDL_COMMON_DOCUMENTS for documents you want the user to browse using Explorer? The idea is to have a hidden place for data and an explosed place for documents.
Thanks,
Chris
Please STOP putting application folders into the <User>/Document folder!
that should only EVER have things the user has put into it.
do you think i want to see "SQL Server Management Studio express" or "BlueTooth Exchange Folder" or ANY of that crap? not a chance.
Work it out people.
Had an interesting situation the other day. I was using FolderShare and decided to share a utilities folder I have on my work PC so that I don't need to continually maintain it between the various machines I use.
Now at work I use XP/2003 but at home I have a Vista Laptop and PC; so you can probably guess what happened. Foldershare happily shared the folder on the 5.x Windows but barfed on trying to create a program files folder in Program Files; even when creating that folder manually for it - since the main point of this excercise was to make it easy to get a new machine up to speed and in sync I didn't want to fiddle too much - but then the dilemma of having actual program files that couldn't exist in program files. In the end I went with App Data under Users\<user> but it still felt a little unclean.
Hi Lex,
That's not an issue where 5.x works and 5.0 doesn't. Rather, your 5.x is clearly running as admin, where we limit when you run as admin on Windows Vista. Run XP as a standard user, and it won't work there either.
I actually keep utilities that I use in users\<me>\Documents\Program Files. users\<me>\appdata is hidden by default, and I like to get to them in Explorer. (I don't take the time to create shortcuts.)
Hi,
Its strange that SUA catches file writes to
ProgramData location?
'You'll want to create your directory and set the ACLs you need at install time' <-Can you elaborate on this perhaps this is where I went wrong.
Thanks.
Hi Ganesh,
Not strange at all. ProgramData is per-machine, so it's shared. The first standard user to create a file will be OK because of the CREATOR OWNER ACE, but the second won't be as lucky - it's an app bug that we can only fix for the single user scenario without some action on the part of the developer / installer.
At install time, you are running elevated, so you can modify ACLs within ProgramData to grant whatever permissions you need (and no more if you are following LUA principles).
Andrew said "Please STOP putting application folders into the <User>/Document folder!" and I agree, though for slightly different reasons.
I want the "documents" location to be safe to backup, full-share, and restore after a melt-down of unknown cause. To me, that means keeping incoming material and infectable code out of that location.
However low the user account rights, they will always allow writes to the user's data location - suggesting that any code files in this location could be infected by generic code infecting viruses. Then if the system is "just" wiped and rebuilt, the virus gets restored with the backed-up "data".
PS: I'd love to "shim" The Sims 2 so that it doesn't dump its 500M of game state into what would otherwise be an easy-backup 20M data set :-/
After quite some time figuring out how to get VB.Net 2005 and a Setup Project to be able to install an SQL Express .mdf/.ldf file to C:\ProgramData, use an Installer class in the application to set the ACL for the directory, modify the install to use the custom action and then change install to set the NoImpersonate bit, I am now stuck on this very topic. How can I get the Setup Project to know about the folder "C:\ProgramData" (I have hard coded it for now)? The special folder list does not have a reference that will resolve to ProgramData. In the VB.NET code I can reference CSIDL_COMMON_APPDATA, my problem is with the install package.
%commonapplicationdata%
See: http://msdn.microsoft.com/en-us/library/aa372057.aspx
Thank you very much Chris. I changed the DefaultLocation to [CommonAppDataFolder] for the Custom Folder I made in the setup project and it worked. That was the last of the hurdles for me on this project. I am happier than a tornado in a trailer park right now. Thanks again.