C#以管理员身份获取外部shell命令结果

时间:2013-12-10 21:01:50

标签: c# shell process command uac

我正在编写一个控制服务的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 的重定向不起作用。如果我注释掉该行,它就可以工作,但我需要这个来决定命令是否成功执行。

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

2 个答案:

答案 0 :(得分:0)

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

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

希望这有帮助。

答案 1 :(得分:0)

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

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

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

代码

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

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屏蔽:

enter image description here

用户知道期待 UAC出现;因为您遵循Microsoft UI准则:

  

准则

     

UAC盾牌图标

     

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

奖金阅读

  

注意:任何代码都会发布到公共域中。无需归属。