I found out an application I wrote does not work properly under Windows Vista/7 if UAC is enabled at any level, because it writes files to the install directory of the program, defaults to "C:\Program Files\MyProgram." If UAC is disabled (or on any other version of Windows) it works properly - I read that UAC denies applications write access to the Program Files directory by default.
My question is, well, how should I write my application so that it can be used without any "rights" needed at all. I don't want users to have to run it with elevated privileges or as administrator. I just want it to work. Are there certain directories that any app has write access to under UAC where it might be better to write my files? They are mostly config files that are dynamically created/destroyed/updated.
Thanks for you help!
Per-user application specific data should be written in the AppData folder.
You should use SHGetKnownFolderPath with FOLDERID_LocalAppData.
In managed code, you should use System.Environment.GetFolderPath with System.Environment.SpecialFolder.LocalApplicationData.
Yes, there are specific locations. Consider this msdn article as a first reference. It mentions the locations:
CSIDL_APPDATA
CSIDL_LOCAL_APPDATA
CSIDL_COMMON_APPDATA
In native code, the method SHGetKnownFolderPath should prove useful.
In managed code you can use Environment.GetFolderPath(). If you're in a specific application framework, such as windows forms, you can get even easier access via direct properties, such as Application.LocalUserAppDataPath (which is my personal favorite technique). The framework path will include app-specific qualifiers on the path it returns to distinguish between (e.g.) different versions of your app.
Related
I'm writing a desktop app and need a folder which...
Exists in Windows 7 and Vista
Is common to all users (for store config data).
Application can save data on it, without Admin privileges (not like "ProgramData").
is standard (I don't want to create another app specific folder in "C:", the Desktop or other place alike.)
"Program Files" is not an option, of course.
Can you suggest an appropriate folder, or better use the Registry?
The recommended way to do this is to create a folder at install time, dedicated to your application, underneath "ProgramData" (i.e. CSIDL_COMMON_APPDATA/FOLDERID_ProgramData).
As you already know, the CSIDL_COMMON_APPDATA folder is read only for standard users. So your install program needs to give the folder that it creates an ACL that permits the access that you require.
This is the solution that meets all the criteria laid out in your bullet points.
You mention the registry. There is no area of the registry that is shared between all users and yet writeable by standard users. Whilst you can use ACLs to grant more permissive access rights to the registry, it is really not the done thing. Please forget that I even mentioned this possibility!
IF your app is .NET then use CommonApplicationData - you can get the real location by calling GetFolderPath.
EDIT - as per comments:
You need to setup ACL correctly - for sample source code on how to do this see http://www.codeproject.com/Tips/61987/Allow-write-modify-access-to-CommonApplicationData
What about Public User directories? For Example: C:\Users\Public\Libraries or C:\Users\Public\Documents
I've noticed these folder on several of my Windows 7 machines. I'm not sure if it is always there, but might be an option. I was hoping for something like an %appdata% for the Public User, but the closest thing I found was Public\Libraries.
(As a side note, it appears C:\Users\Public\Desktop does require admin to write to.)
what about using %APPDATA%/Company/Product for the directory?
My application is a rather well behaved Windows citizen, so when I ported it to Windows Vista/7 I replaced my custom file format association code with support for the Default Programs API. However I ran into a problem when trying to make uninstaller for my application - there seems to be no way to remove file format associations via Default Programs API.
I tried to call IApplicationAssociationRegistration::ClearUserAssociations but it actually removes all associations, including the ones for other applications - completely restoring default state of the OS (which is of course unacceptable).
I tried to call IApplicationAssociationRegistration::SetAppAsDefault to return file format associations to the previous "owner" - but it does not help, because my application handles many unique file formats which the OS does not support and there is no previous "owners". And Windows does not allow to pass empty strings to SetAppAsDefault...
So what do I do? Any good solutions?
I think that you are using the Default Programs API in the wrong way. If I understand correctly the default programs functionality was added by Microsoft due to legal requirements to replace Internet Explorer as the default browser. It offers another set of functionality than the normal file associations used by aplications. If you just have a simple file association to register, I'd suggest you stick to the old behaviour.
From MSDN:Default Programs (Windows):
Default Programs is primarily designed for applications that use standard file types such as .mp3 or .jpg files or standard protocols, such as HTTP or mailto. Applications that use their own proprietary protocols and file associations do not typically use the Default Programs functionality.
Side note: All the considerations below apply even if you modify directly the file associations in the registry instead of using the Default Programs API.
On first run, your application should collect the previous owners of all the file types for which one exists, through IApplicationAssociationRegistration::QueryCurrentDefault and store them in storage your app owns.
On uninstall, your application should use IApplicationAssociationRegistration::SetAppAsDefault to attempt to restore any file association it still owns to the previous owner it has. For associations your app still owns, but don't know previous owners, go to the HKCR registry and delete the corresponding extension, protocol or MIME type entry. Don't touch any associations your app is not the current owner - you'll be overwriting the user's choice.
I certainly wish that the batched backup on first run and cleanup on uninstall were provided as a single API call by the Default Programs API, but until they decide to generalize that behavior for all apps, you're on your own.
Note that the cleanup your application executes on uninstall will be specific for the uninstalling user. Any other users that might have used the application and changed their defaults will not be cleaned up.
You can automate the cleanup for each user by adding a simple per-user task that executes the steps above in the Task Scheduler. The task will be scheduled to execute once and then removes itself from the user task scheduler. The only potential problem with that approach is that since you don't know how many users there will be, it's impossible for you to know when to remove the dll for that task from the machine. Then again, if you leave that dll in the ProgramData folder, it's not a big deal.
The correct code for associating your file extension with an application, is using the Windows Registry settings. Typically this is done to the entire machine (regardless of user) using the HKEY_LOCAL_MACHINE\SOFTWARE\Classes registry hive, which is also accessible more conveniently through the HKEY_CLASSES_ROOT alias (registry shortcut).
Your process involves three steps:
Saving the "old" settings, before installing your application (BTW, it's nice that you are doing that. Many applications simply remove the mapping altogether!)
Creating your own associations. A good example on how to perform this is at Modifying File Associations With Registry Editor. A thorough explanation is on MSDN:
When uninstalling, recover the saved "old" settings from step (1), and rewrite the installed values with these original ones. In case there were no "old" settings, a good citizenship would mean simply deleting the class key altogether from the registry.
One tool to assist in debugging and seeing how class (extension) mappings change is at FileAsoc Windows File Association Editor. It lets you recover file extensions while debugging your application. The webpage also gives a short explanation of how exactly the values are stored.
Hope this helps!
Instead of making the file associations in your app, make them in the installer. Using WiX you can create an installer that sets file associations on install and will remove them on uninstall
I have an application written in Delphi 2006 that was working fine in Windows XP. I packed the application using Inno Setup, using Program Files as the default folder. A few users migrated to Windows Vista and Windows 7. The issue here is that the application creates some files inside its installation folder by its own. This was working in XP but in Windows Vista the users were having problems with the created files (they don't appear and so on). After investigating the users' reports I discovered KB 927387: "Common file and registry virtualization issues in Windows Vista or in Windows 7."
Running the application with administrator rights just solves the problem, but that is (I think) an awful workaround. I would like to know if there are any directives or tips for making the application compatible with Vista and 7, because more users will migrate to these OS soon.
You need to re-write your application to store its files in the proper locations, even in XP, but especially in Vista onwards, particularly if UAC is enabled. This is becoming more and more important to get right as Microsoft keeps locking down and enforcing its security models with each new OS version. The rules for how to properly manage application- and user-related files is documented on MSDN, for example: "Application Specification for Microsoft Windows 2000 for Desktop Applications, Chapter 4: Data and Settings Management" and "Application Specification for Microsoft Windows 2000 for Desktop Applications Appendix A: Best Practices" (yes, they are old, but are still quite relevant). Look at SHGetSpecialFolderLocation(), SHGetFolderPath(), SHGetKnownFolderPath() and other related functions to help you.
For Vista/Win7, your app can't put the files in a subfolder of Program Files / Programs unless UAC is turned off or the app is running as elevated. Note that "elevated" does not necessarily mean "logged in as Administrator." Non-administrator users can elevate, and Administrator isn't necessarily elevated.
If the app does attempt to write to Program Files but is not elevated, the OS will either block the app or "virtualize" the write (put the files somewhere else), depending upon how UAC is configured. Neither one helps the app succeed at what it was trying to od.
So it needs to put them somewhere else. Where depends on why the files are being created, and you haven't told us that. You can read this article to learn about the options. Note that in addition to the user's AppData and Roaming folders, there is also an "All Users" (shared) profile.
You should probably look at this article and screencast, which discusses UAC in depth from a Delphi point of view.
Files you create for use by your application other than at installation time should go into the ProgramData directory if its global to the workstation, or into the users ApplicationData directory if its specific to the user.
For cases where you absolutely must place a file in the program files directory, you can use com to request elevation. This is discussed in great detail, and delphi specific bits are also available. One example that I have used this is in patching my users installation base. They are warned by UAC that the system needs to make changes, so if your doing this as an automated task, you might need to rethink the logic to be more user driven.
Here is another article, by Zarko Gajic, which shows how to get different system directories. Also have a look at this related question.
I had a similar enquiry here (Stack Overflow).
In the end I realised that I needed to put my application into Program Files at install time (requiring UAC/elevation) and then store my app's data in the user's App Data folder. I had to change the way my program generated 'default' configuration settings and also where I was saving this stuff, but it was worth the effort in the end - we ended up with something that installs and runs fine on XP, Vista and Windows 7.
The only UAC hit we get is at installation time, which makes sense to me (and you get a similar hit at install-time on the Mac too). We didn't have any data that would be common to all users in this particular case but I would have looked at the Program Data special folder if that had been the case.
The installer software we use (Setup Factory) made this fairly straightforward (we just wrote a small bit of code to detect XP versus Vista/Win7 and choose the right special folder accordingly). It would be easy to do this in Inno Setup too, from what limited experience I have of it.
I work on a vb6 application which is having problems with Vista, for the obvious reasons (writing to program files, and other things that are no longer allowed by default).
Where should I store application data or user's saved files?
Do I need priviledges to create folders and files, there, too?
What other common actions will cause problems?
The program has an updater which must download and register files, how do I elevate priviledges when this occurs?
Some of these questions have obvious answers, but I want to get the obvious stuff right.
Depending upon what you are doing, you might be in for a world of pain. There are no hard and fast answers to any of those questions, but from someone who is going through the same issues right now, here's what I know.
1) Where should I store application data or user's saved files?
This depends on what you are wanting to do. If you want them per user, store them in Users/AppData, if you want them for all users, store them in Common/AppData
If SHGetFolderPath(0, CSIDL_COMMON_APPDATA, -1, SHGFP_TYPE_CURRENT, sTempPath) = 0 Then
sCommonAppdata = Left$(sTempPath, InStr(1, sTempPath, Chr(0)) - 1) & "CompanyName\AppName"
End If
Change that to CSIDL_APPDATA for the Users AppData directory. Note: These map to totally different places on the filesystem for XP and Vista so when you are debugging prepare to look in different places.
2) Do I need priviledges to create folders and files, there, too?
You need Adminsitrator access to write anything in Program Files, if at all possible don't do it! We are currently running into issues that the API's for VB and the standard API's behave differently on files in Program Files.
3) What other common actions will cause problems?
There are lots of hidden gotchas. Just to name a few, you cannot communicate through IPC or Named Pipes to other applications (we have a Service that we were talking to through a Tray Notification Icon and that had to be completely re-written). Anything were you see a UAC notification is very difficult. Also you cannot write to anything in the Registry in LOCAL_MACHINE without Administrator, so you either have to stick to LOCAL_USER or raise credentials (see below).
4) The program has an updater which must download and register files, how do I elevate priviledges (sic) when this occurs?
Good luck with this, I highly recommend that you don't write it in VB6, like I said, the VB6 file api's appear to access files differently from the standard API's. If you need to elevate privileges see this post that someone kindly helped me with.
In the sort term turning off UAC and installing the ActiveX installer server will help. Long term you need to put data and configuration information in the users directory under \users or in \programdata.
In the short run it might not be necessary at all to modify your application, because
Vista comes with a set of compatibility options to allow legacy applications to run. This includes file and registry virtualization, a feature which basically redirects write operations to protected folders such as C:\Program Files to a virtual location only visible for the specific application running in a compatibility mode.
Some more details are mentioned in this article: How To Manage Windows Vista Application Compatibility in Dr. Dobb's.
Karl Peterson wrote a nice article on where to store user data & app data, with a VB6 class that retrieves the location of the special paths for you.
I have a vb6 application. I am unable to carry out the following on a Windows Vista machine:
Register a .DLL from the Users directory
Start another .EXE
Write to a file in the Users directory.
How can I overcome these issues?
This is by design. Software is supposed to require admin rights for installation (including update/upgrades). You got away with it in XP because so many people ran as administrator. If you're registering a dll after installation, you're doing something wrong.
You should be able to do this without issue, but only with the same rights as the user that the current app is running under. What is this exe trying to do, and would a standard user normally have those permissions?
What specific folder in the user's directory? Again: a user should have write access to most of the their own profile, but not necessarily as much outside of that as you think.
You have access to write to the c:\users\ directory. Going to another users directory is a bit of a security problem that you're not going to work around.
As far as registering an assembly from a users directory... There is a directory under c:\users\\AppData\Local\assembly which you should have access to for temporary assemblies
Starting another exe is trivial... As long as it's installed in the normal \program files.. path.
It really sounds like maybe you need to reevaluate how your application works.
Some suggestions:
Move to Reg-Free COM where it can handle things (i.e. not for ActiveX EXEs, DCOM).
We'd need more information. This should be no problem unless there are permissions issues.
Users are not supposed to be creating files outside of their own profiles, CommonAppData, and application-defined locations (often on non-system drives). More information is required to give any really useful answer.