In this tip, you will see how to workaround a ClickOnce limitation.
Introduction
I made a small tool application that I wanted to start up every time I start up my computer. It was way too simple to make it as a Windows Service and have to install it using a windows installer. I also wanted to be able to distribute it as simple as possible to my colleagues (they're programmers, but not all that bright ;-) ). So I thought that ClickOnce deployment would be absolutely perfect for the program.
So I did it as a simple WinForms program, and I had the program add itself to the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run registry key.
In order to do that, the program has to be run with administrator rights (elephanting irritating, but there's nothing to do about that unfortunately).
I added an application manifest to my project and changed:
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
to:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
and everything worked perfectly on my development machine.
Then I tried creating a ClickOnce deployment. But that proved impossible. A ClickOnce application by design cannot be run with administrator rights. So it complains already when you try to create the deployment that requireAdministrator
is not a valid setting for requestedExecutionLevel
.
And then it felt like I was up the creek without a paddle. What should I do? I wanted to use ClickOnce and I wanted to run as an administrator, because the startup thing was an important part of the program. But the two things were mutually exclusive.
There had got to be a way around it, I thought, and I Googled for several hours. All I saw was: A ClickOnce application cannot be run with administrator rights.
I was about to give up when I finally found the solution. I found an article on AntsCode written by a guy called Anthony that had a working solution. You run the program, and as the first thing you do, you check if you have administrator rights. If you haven't got that, you shell yourself WITH administrator rights and close down the non-admin instance (your application cannot be a singleton application of course, but in my case it luckily wasn't that either).
It worked like a charm for me, so I thought that I'd share the tip with all y'all here.
Credits and Disclaimer
I don't claim any ownership to the code, and I don't take any credit for either the code or the idea. All credit should go to Anthony on AntsCode.
But in my opinion, that is the whole deal with a CodeProject tip. An article is about something you yourself have worked intensely on and want to share with the community. But a tip is about something you have found and want to tip others about because you think that they will find it interesting as well. It doesn't have to be of your own doing. That is at least how I see it. If the CodeProject admins see it differently, I hope they will enlighten me.
If you want to call plagiarism, my opinion is that it is only plagiarism if you take credit for other peoples work yourself, and as mentioned: I don't do that!
Using the Code
So here's how you do it! You leave your manifest as is:
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
And then you edit your Program.cs file like this:
using System;
using System.Diagnostics;
using System.Reflection;
using System.Security.Principal;
using System.Windows.Forms;
namespace MyProgramNamespace
{
static class Program
{
[STAThread]
static void Main()
{
var wi = WindowsIdentity.GetCurrent();
var wp = new WindowsPrincipal(wi);
bool runAsAdmin = wp.IsInRole(WindowsBuiltInRole.Administrator);
if (!runAsAdmin)
{
var processInfo =
new ProcessStartInfo(Assembly.GetExecutingAssembly().CodeBase);
processInfo.UseShellExecute = true;
processInfo.Verb = "runas";
try
{
Process.Start(processInfo);
}
catch (Exception)
{
MessageBox.Show("Sorry, but I don't seem to be able to start " +
"this program with administrator rights!");
}
Application.Exit();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
}
Voila! Works like a charm (on my machine anyway)!
Points of Interest
Completely off topic, but still: I noticed a strange thing. I added the registry value to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run as mentioned, and the code went through all the way without any errors. But no matter what I did, I still couldn't see the added value in RegEdit.
It turns out that if your OS is 64 bit, then there is a special hive for that. The program CLAIMS that it's adding the value to the key above, and all debugging supports that claim. But it IS in fact adding it to THIS key instead:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run
Very sneaky if you ask me!
History
- Version 1.00 - Initial release