从一个提升权限/以管理员身份运行的进程中以非提升权限运行子进程

4
我的应用程序有一个内置的自动更新系统,通过另一个名为“updater.exe”的应用程序来实现更新。该程序与主应用程序在同一文件夹中。它会下载最新版本,终止旧版本(如果正在运行),然后进行覆盖。
问题是,为了这样做,必须以管理员权限运行updater.exe才能访问C:\Program Files\MyApp。
到目前为止,主应用程序已使用UAC以管理员权限运行updater.exe,但接下来出现了问题:
更新完成后,我希望新安装的版本能够自动启动。然而,主应用程序也会以管理员权限运行。情景很简单:
Main app[running as user] --> Updater App[run as admin] --> Main app[ADMIN again]

只是因为我的应用程序使用了My.Settings对象,所以当它以管理员身份运行时,它会失去所有存储的设置,因为通常它总是以普通用户身份启动,而你可能知道,My.Settings是一个用户敏感的对象。
我该如何解决这样的问题?我已经搜索过了,但没有找到与“以普通用户身份运行”有关的任何内容,但总是以管理员身份运行,这很容易。
说实话,在另一方面,我不认为这样的事情可能发生,因为更新程序不能知道哪个特定的用户启动了它。或者可以吗?我有什么遗漏的地方吗?
如果我没错的话,唯一的选择就是不使用My.Settings,而是使用Windows注册表来存储用户首选项?
谢谢。

1
如果你想这样做,那并不容易。这个这个应该可以通过p/invoke为你提供帮助,如果你真的想要的话。它还建议另一种解决方案,即运行一个非提升进程来启动你的更新应用程序。 - SomeNickName
谢谢提供的链接!我想最简单的方法应该是你给出的第一个链接中的第二个答案。在运行更新器之前,从主应用程序中运行另一个exe(比如说“autorestarter.exe”)是一个非常好的想法。所有“autorestarter”需要做的就是等待“updater”进程完成(每秒检查一次),当它结束时再次启动主应用程序。 最简单的解决方案。谢谢。 - Roni Tovi
你的主应用程序中是否已经尝试过 <requestedExecutionLevel level="highestAvailable" uiAccess="false" /> - Top Systems
@RoniTovi 在这种情况下,你可以使用 Process.WaitForExit()。 - SomeNickName
1个回答

4
一种“取消提升”子进程的方法是通过资源管理器启动它。这通常适用于普通Windows用户。但是,如果资源管理器本身正在运行提升,则您尝试启动的应用程序也将运行提升。资源管理器可能会运行提升,因为:
  • 活动用户是管理员(不仅仅是具有管理员权限的用户)
  • Explorer.exe 是从运行提升的命令窗口中重新启动的
还有其他可能原因。
最好将其视为以默认权限启动应用程序。如果运行提升的资源管理器将启动新实例,则原始第一个“主应用程序”实例也将运行提升。
使用复选框选择是否提升的测试代码来启动相同的应用程序:
Dim proc = New Process
proc.StartInfo.UseShellExecute = True
proc.StartInfo.WindowStyle = ProcessWindowStyle.Normal
proc.StartInfo.WorkingDirectory = mypath

If chkAdmin.Checked Then                    ' run this app as admin
    proc.StartInfo.FileName = myApp
    proc.StartInfo.WorkingDirectory = mypath
    proc.StartInfo.Verb = "runas"           ' run "me" as admin
    proc.StartInfo.Arguments = ""
Else                                        ' run explorer w/app as arg
    ' de-elevate new app instance to run de-elevated
    proc.StartInfo.FileName = Path.Combine(windir, "explorer.exe")  
    proc.StartInfo.Verb = ""                ' important!
    proc.StartInfo.Arguments = myApp        ' send the child app name as arg
End If

proc.Start()

这张图片展示了结果:
顶部标签表示该应用是否以提升的权限运行,每个应用实例都是由前一个实例启动的。
第二个窗口正在以提升的权限运行。当它启动下一个实例时,未勾选“作为管理员”复选框,因此第三个实例通过资源管理器启动,没有以提升的权限运行。第四个启动第五个也是同样的情况。

1
你应该检查完整性级别,而不仅仅是“是否为管理员”。可能会创建一个没有管理员访问权限但仍以高完整性级别运行的子进程。真正的非提升进程具有中等完整性级别。Process Hacker可以显示给定进程的完整性级别。无法确定此方法是否执行此操作。有一种(真正丑陋的P/Invoke C#)启动非提升子进程的方法,不依赖于Explorer未被提升 - 请参见这里 - Roman Starkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接