I have a C# application which runs and displays a tray icon. I have an installer for my tray application which launches the application after installation. The installer requires admin permissions whereas the tray icon must be run with normal permissions. My installer currently breaks this - when the installed tray application is launched it inherits admin permissions from the installer process.
As part of my installer I am launching a C# application to perform some custom work. This small application currently launches the tray application by calling:
Process.Start(@"path/to/my/tray/app.exe");
Is there any way to invoke the tray app with the current user's permissions rather than the elevated permissions given to the installer?
I have heard that the recommended way to do this is to have a wrapper EXE around the installer which launches the installer then launches the installed program. I would like to avoid this if possible.
I am using WiX to build an MSI installer so I would also accept solutions which work directly from WiX/MSI.
Very good question. The answers I found that ostensibly work are all a bit messy; the most elegant overall is the EXE wrapper.
Have a look at this article: http://www.codeproject.com/KB/vista-security/RunNonElevated.aspx. It describes a method by which you can obtain a native hook to one of the shell windows, which run non-elevated, and ask the shell to start your code for you. Native hooks in C# require very high CAS permissions; to get these permissions, your installer must be strongly named and signed, and the code must demand or assert SecurityPermission with SecurityPermissionFlag.UnmanagedCode.
The ProcessStartInfo class of the .NET Framework also contains a UseShellExecute Boolean property that, when set, tells Process.Start() to give this call to the shell rather than starting the process directly from the current application domain. I don't know if this will do that you need, but it's definitely much easier to try; you just use the ProcessStartInfo overload of Process.Start(), with a declared ProcessStartInfo having the flag set.
Remember that you cannot tell the shell to start an EXE as a user other than the currently logged-in user (UserName and Password must not be set on the ProcessStartInfo). You must also specify the path to the EXE using the WorkingDirectory property.
There are two approaches. The simplest is to use a shell exe that first launches the admin task elevated (lots of ways to do this; I prefer a manifest) and then the other task non-elevated. If you refuse to do that for whatever reason then take a look at the blog post and CodePlex project I link to from my blog post on this: http://www.gregcons.com/KateBlog/NonElevatedFromElevatedManagedThisTime.aspx
Alternatively, you could use the Windows API CreateProcessAsUser
, that seems to do the job.