ClickOnce 部署 vs. requestedExecutionLevel = requireAdministrator
如何绕过 ClickOnce 的一个限制
引言
我制作了一个小型工具应用程序,我想在每次启动计算机时都启动它。把它做成 Windows 服务并使用 Windows 安装程序安装它太复杂了。我也想尽可能简单地将其分发给我的同事(他们是程序员,但并非个个都那么聪明 ;-))。所以我认为 ClickOnce 部署绝对是该程序的理想选择。
所以我把它做成一个简单的 WinForms 程序,并且让程序将自己添加到 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run 注册表项中。
为了做到这一点,程序必须以管理员权限运行(非常令人恼火,但不幸的是对此无能为力)。
我向我的项目添加了一个应用程序清单并更改了
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
to
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
一切在我的开发机器上运行完美。
然后我尝试创建一个 ClickOnce 部署。但这被证明是不可能的。 ClickOnce 应用程序在设计上无法以管理员权限运行。因此,当您尝试创建部署时,它已经抱怨 requireAdministrator
不是 requestedExecutionLevel
的有效设置。
然后我感觉就像无桨的小溪一样。我该怎么办?我想使用 ClickOnce,并且我想以管理员身份运行,因为启动项是程序的重要组成部分。但这两件事是相互排斥的。
我想,一定有办法解决这个问题,我在谷歌上搜索了几个小时。我看到的一切都是:ClickOnce 应用程序无法以管理员权限运行。
我几乎要放弃了,直到我终于找到了解决方案。我发现了一篇 AntsCode 上的文章,作者是一个名叫 Anthony 的人,他有一个可行的解决方案。您运行程序,首先要做的就是检查您是否拥有管理员权限。如果您没有,您就以管理员权限启动自己并关闭非管理员实例(当然,您的应用程序不能是单例应用程序,但幸运的是,我的情况也不是这样)。
它对我来说非常有效,所以我想在这里与大家分享这个技巧。
致谢和免责声明
我没有声称拥有该代码的任何所有权,也没有为该代码或该想法做出任何贡献。所有功劳都应该归功于 AntsCode 上的 Anthony。
但在我看来,这才是 CodeProject 技巧的全部意义所在。一篇文章是关于您自己一直在努力并且想要与社区分享的事情。但一个技巧是关于您发现并想要提醒他人的事情,因为您认为他们也会觉得它很有趣。它不必是您自己做的。至少我是这样认为的。如果 CodeProject 管理员有不同的看法,我希望他们能启发我。
如果您想称之为抄袭,我的观点是,只有当您为别人的工作自己邀功时,才是抄袭,而且如前所述:我没有这样做!
Using the Code
所以这里是您如何操作的!您保持您的清单不变
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
然后,您可以像这样编辑您的 Program.cs 文件
using System;
using System.Diagnostics;
using System.Reflection;
using System.Security.Principal;
using System.Windows.Forms;
namespace MyProgramNamespace
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
var wi = WindowsIdentity.GetCurrent();
var wp = new WindowsPrincipal(wi);
bool runAsAdmin = wp.IsInRole(WindowsBuiltInRole.Administrator);
if (!runAsAdmin)
{
// It is not possible to launch a ClickOnce app as administrator directly,
// so instead we launch the app as administrator in a new process.
var processInfo =
new ProcessStartInfo(Assembly.GetExecutingAssembly().CodeBase);
// The following properties run the new process as administrator
processInfo.UseShellExecute = true;
processInfo.Verb = "runas";
// Start the new process
try
{
Process.Start(processInfo);
}
catch (Exception)
{
// The user did not allow the application to run as administrator
MessageBox.Show("Sorry, but I don't seem to be able to start " +
"this program with administrator rights!");
}
// Shut down the current process
Application.Exit();
}
else
{
// We are running as administrator
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
}
瞧!效果很好(至少在我的机器上是这样)!
关注点
完全无关的话题,但仍然是:我注意到了一件奇怪的事情。我将注册表值添加到 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run,如前所述,并且代码一路运行,没有任何错误。但无论我做什么,我仍然无法在 RegEdit 中看到添加的值。
事实证明,如果您的操作系统是 64 位,那么就有一个专门用于此的特殊 hive。该程序声称它正在将该值添加到上面的键中,并且所有调试都支持该声明。但实际上,它将它添加到了这个键中
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run
如果您问我,这非常狡猾!
历史
- 版本 1.00 - 初始发布