我正在编写一个控制服务的C#winform应用程序。我尝试使用System.Diagnostics.Process
和System.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 的重定向不起作用。如果我注释掉该行,它就可以工作,但我需要这个来决定命令是否成功执行。
有没有办法如何启动/停止服务(使用临时管理员权限执行)并获得成功结果?
答案 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屏蔽:
用户知道期待 UAC出现;因为您遵循Microsoft UI准则:
准则
UAC盾牌图标
使用UAC屏蔽显示控件,以指示在UAC完全启用时任务需要立即提升,即使UAC当前未完全启用也是如此。如果向导和页面流的所有路径都需要提升,请在任务的入口点显示UAC屏蔽。 正确使用UAC防护罩可帮助用户预测何时需要升高。
注意:任何代码都会发布到公共域中。无需归属。