01 December, 2006

Using CACLS in an MSI install

What I have discovered is that if you use the Setup and Deployment project from the Visual Studio 2003 IDE, it doesn't always work the way one might expect it to work.

For instance, when the user installs the program, they are presented with the option to select to install the program for them only, or for anyone on that machine. If the user selects for them only, it would be expected that the files and components of that program's permissions are set so as to only allow that person to run the program.

However, if the user selects to allow all users to run the program, my prevailing expectation was that it would set permissions on the files and components to allow any user to run the program. WRONG. As I should have expected, with Microsoft making so many assumptions and decisions for us (and giving us very little means to adjust that easily), the files and components are set to read only for all but admin account holders.

In our programs, there are many files that are read and written to. For this, since we have dropped InstallShield as our primary install creating agent, I had to create yet another small program that runs on install (that makes two now). I do have these installed as "hidden" files so hopefully it won't clutter up the install directory too badly.

At any rate, I use CACLS.EXE to set the directory permissions. It's a fairly simple command line program. As I didn't want to pop open an command line window, I created a form that is 1px by 1px, located at 0,0 and has an opacity of 0. This should keep it from being seen at all. What we needed was for the local "Users" group to have read/change permissions to all files that get installed. For that I used the command line switch "/G BUILTIN\Users:C(hange)". This table lists the permission codes:




















NNone
RRead
WWrite
CChange
FFull

The codes really are (surprisingly) intuitive. There are other switches involved. These are




























/TRecursive through current and all sub-directories
/EEdits permissions (doesn't replace them)
/CIgnores "Access Denied" errors
/GGrant access rights
/RRevoke access rights
/PReplace access rights
/DDeny access rights


So I end up with the following example:

string response;
string userPerm = @"BUILTIN\Users:C";
string fileDirectory = @"C:\Program Files\Program";
string caclsSwitches = @" /T /E /C /G ";
string caclsLoc = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "CACLS.EXE");
Process p;
if (Directory.Exists(fileDirectory)
{
p = new Process();
p.StartInfo.FileName = calsLoc;
p.StartInfo.Arguments = fileDirectory + caclsSwitches + userPerm;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
p.StartInfo.CreateNoWindow = true;
p.Start();
response = p.StandardOutput.ReadToEnd();
if (response == null || response.Lenghth == 0)
{
// handle errors here
}
if (p != null) p.Dispose();
response = null;
}



Resources:
Undocumented CACLS: Group Permissions Capabilities
How to Use CACLS.EXE in a Batch File
Information about the cacls command.

2 comments:

Anonymous said...

I cant seem to get this to work? which event in the installer do you call this code in?

Cheers,
Andrew.

Unknown said...

It's been a while (and I no longer work for that company, so I don't have my code I can go look at and refresh my memory), but I called to execute the program in either the "Install" action or the "Commit" action - I just can't remember which one.

Hope this helps!