【问题标题】:C# get external shell command result as administratorC#以管理员身份获取外部shell命令结果
【发布时间】:2013-12-28 14:22:00
【问题描述】:

我正在编写一个控制服务的 C# winform 应用程序。我尝试使用System.Diagnostics.ProcessSystem.ServiceProcess.ServiceController 控制此服务。

由于需要 AdminRights 才能在服务中进行更改,并且我阅读了几篇关于使用“runas”-verb 实现这一点的帖子,我决定使用System.Diagnostics.Process

System.Diagnostics.ProcessStartInfo startInfo = 
    new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C net start " + SERVICE_NAME;
startInfo.RedirectStandardError = true; //// <<<<<<<<<<<<<<<<<
startInfo.UseShellExecute = true;
startInfo.Verb = "runas";

Process process = new Process();
process.StartInfo = startInfo;
process.ErrorDataReceived += cmd_DataReceived;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();

但似乎 runas-Verb 和 StandardErrorOutput 的重定向不起作用。如果我注释掉该行,它会起作用,但我需要它来确定命令是否成功执行。

有没有办法启动/停止服务(以临时管理员权限执行)并获得成功的结果?

【问题讨论】:

  • 不确定它是否是您要查找的内容,但您可以调查MSDN for FileIOPermission MSDN

标签: c# shell process command uac


【解决方案1】:

您最好的选择是在您自己的应用程序中自己启动服务。

问题当然是您的应用程序通常不是以管理员身份运行的(您也不希望这样)。这意味着您无法启动或停止服务。这就是为什么您应该暂时提升,执行所需的任务,然后退出。

换句话说,启动您的应用程序的第二个副本,传递一个命令行选项来指示您启动服务。第二个副本仅执行此操作,然后退出。

代码

首先我们有一个按钮,用户可以点击它来启动服务。我们首先检查用户是否已经是管理员,在这种情况下我们不需要做任何特殊的事情:

private void button1_Click(object sender, EventArgs e)
{
    //If we're an administrator, then do it
    if (IsUserAnAdmin())
    {
        StartService("bthserv"); //"Bluetooth Support Service" for my sample test code
        return;
    }

    //We're not running as an administrator.
    //Relaunch ourselves as admin, telling us that we want to start the service
    ExecuteAsAdmin(Application.ExecutablePath, "/serviceStart");
}

//helper function that tells us if we're already running with administrative rights
private Boolean IsUserAnAdmin()
{
    //A user can be a member of the Administrator group, but not an administrator.
    //Conversely, the user can be an administrator and not a member of the administrators group.

    //Check if the current user has administrative privelages
    var identity = WindowsIdentity.GetCurrent();
    return (null != identity && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator));
}

private void ExecuteAsAdmin(string Filename, string Arguments)
{
   //Launch process elevated
   ProcessStartInfo startInfo = new ProcessStartInfo(Filename, Arguments);
   startInfo.Verb = "runas"; //the runas verb is the secret to making UAC prompt come up
   System.Diagnostics.Process.Start(startInfo);
}

然后,在启动期间,我们只需要检查是否使用命令行选项调用我们。如果是,启动服务:

public Form1()
{
    InitializeComponent();

    //Ideally this would be in program.cs, before the call to Application.Run()
    //But that would require me to refactor code out of the Form file, which is overkill for a demo
    if (FindCmdLineSwitch("serviceStart", true))
    {
        StartService("bthserv"); //"Bluetooth Support Service"
        Environment.Exit(0);
    }
}

private bool FindCmdLineSwitch(string Switch, bool IgnoreCase)
{
    foreach (String s  in System.Environment.GetCommandLineArgs())
    {
        if (String.Compare(s, "/" + Switch, IgnoreCase) == 0)
            return true;
        if (String.Compare(s, "-" + Switch, IgnoreCase) == 0)
            return true;
    }
    return false;
}

最后启动服务:

private void StartService(String ServiceName)
{
    TimeSpan timeout = TimeSpan.FromMilliseconds(30000); //30 seconds

    using (ServiceController service = new ServiceController(ServiceName))
    {
        try
        {
            service.Start();
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message, "Error starting service");
            return;
        }
        service.WaitForStatus(ServiceControllerStatus.Running, timeout);
    }
}

特别花哨

您可以添加任意数量的精美代码。检测用户是否不是管理员,如果不是,则使用正确的 Windows API (BCM_SETSHIELD message) 为您的按钮添加 UAC 屏蔽:

这样用户就知道期望 UAC 会出现;因为您遵循了 Microsoft UI 指南:

指南

UAC 盾牌图标

显示带有 UAC 盾牌的控件,以指示在完全启用 UAC 时任务需要立即提升,即使 UAC 当前未完全启用也是如此。如果向导和页面流的所有路径都需要提升,请在任务的入口点显示 UAC 盾牌。 正确使用 UAC 防护罩有助于用户预测何时需要提升。

阅读奖励

注意:任何代码都会发布到公共领域。无需署名。

【讨论】:

  • 根据问题,这不是一个直接的答案,但它是我可以使用的一个很好的解决方案。 (实际上我是通过命令行启动服务 - System.Diagnostics.Process 并检查 service.WaitForStatus 是否成功
【解决方案2】:

您可以使用框架提供的实用程序 MT.exe(在 SDK 子目录中)来生成强制您的应用程序以管理员模式运行的清单。在这种情况下,处理受保护的服务应该没有问题。当然,权衡是......你在管理员模式下运行。

mt.exe -manifest "your-path\your-app.exe.manifest" -updateresource:"$(TargetDir)$(TargetName).exe;#1

希望这会有所帮助。

【讨论】:

  • 谢谢,但听起来不像是真正的解决方案。然后我必须在管理员模式下运行整个程序,而不仅仅是打开和关闭服务。
  • 好的,很公平。我不知道你的应用程序有多大。您是否尝试将 startInfo.UseShellExecute 设置为 true ?这可能会影响您的输出重定向。
  • 我已经对此进行了测试,truefalse。但是MSDN说必须将UseShellExecute设置为false,否则streamoutputredirection不起作用。
猜你喜欢
  • 1970-01-01
  • 2011-10-30
  • 2019-05-17
  • 2013-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-23
  • 1970-01-01
相关资源
最近更新 更多