我有一个 C# 应用程序,它运行并显示一个托盘图标。我的托盘应用程序有一个安装程序,它在安装后启动该应用程序。安装程序需要管理员权限,而托盘图标必须以普通权限运行。我的安装程序目前打破了这一点 - 当安装的托盘应用程序启动时,它会从安装程序进程继承管理员权限。
作为安装程序的一部分,我将启动一个 C# 应用程序来执行一些自定义工作。这个小应用程序当前通过调用启动托盘应用程序:
Process.Start(@"path/to/my/tray/app.exe");
是否有任何方法可以使用当前用户的权限而不是授予安装程序的提升权限来调用托盘应用程序?
我听说推荐的方法是在安装程序周围有一个包装器 EXE,它启动安装程序,然后启动已安装的程序。如果可能的话我想避免这种情况。
我正在使用 WiX 构建 MSI 安装程序,因此我也接受直接从 WiX/MSI 运行的解决方案。
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.
发布评论
评论(3)
有两种方法。最简单的方法是使用 shell exe,首先启动提升的管理任务(有很多方法可以执行此操作;我更喜欢清单),然后启动其他未提升的任务。如果您出于某种原因拒绝这样做,请查看我的博客文章中链接到的博客文章和 CodePlex 项目:http://www.gregcons.com/KateBlog/NonElevatedFromElevatedManagedThisTime.aspx
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
或者,您可以使用 Windows API
CreateProcessAsUser
,似乎可以完成这项工作。Alternatively, you could use the Windows API
CreateProcessAsUser
, that seems to do the job.非常好的问题。我发现表面上工作的答案都有点混乱;最优雅的整体是 EXE 包装器。
看看这篇文章: http://www.codeproject.com/KB /vista-security/RunNonElevated.aspx。它描述了一种方法,您可以通过该方法获得一个 shell 窗口的本机挂钩,该窗口以非提升方式运行,并要求 shell 为您启动代码。 C# 中的原生 hooks 需要非常高的 CAS 权限;要获得这些权限,您的安装程序必须具有强命名和签名,并且代码必须使用 SecurityPermissionFlag.UnmanagedCode 请求或断言 SecurityPermission。
.NET Framework 的 ProcessStartInfo 类还包含一个 UseShellExecute Boolean 属性,设置该属性后,会告诉 Process.Start() 向 shell 发出此调用,而不是直接从当前应用程序域启动进程。我不知道这是否能满足您的需要,但尝试起来肯定容易得多;您只需使用 Process.Start() 的 ProcessStartInfo 重载,并声明设置了标志的 ProcessStartInfo 。
请记住,您不能告诉 shell 以当前登录用户以外的用户身份启动 EXE(不得在 ProcessStartInfo 上设置用户名和密码)。您还必须使用WorkingDirectory 属性指定EXE 的路径。
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.