2013-12-10 43 views
1

我正在編寫一個控制服務的C#winform應用程序。我試圖用System.Diagnostics.ProcessSystem.ServiceProcess.ServiceController來控制這項服務。C#以管理員身份獲取外部shell命令結果

由於所需的AdminRights在服務和一些文章中進行了更改,因此我想了解使用「runas」動詞的情況,我決定使用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(); 

但似乎運行方式 -Verb和StandardErrorOutput重定向不起作用。如果我註釋掉該行,它會起作用,但我需要這個來決定命令是否成功執行。

有沒有辦法如何啓動/停止服務(使用臨時管理員權限執行)並獲得成功的結果?

+0

不知道它是你在找什麼,但你可以調查'FileIOPermission'的[MSDN] [MSDN](http://msdn.microsoft.com/zh-cn/library/system.security。 permissions.fileiopermission(v = vs.110).aspx) – KSdev

回答

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出現;因爲你遵循了微軟UI準則:

準則

UAC盾牌圖標

與UAC屏蔽顯示控件,以表明任務需要時,UAC完全能夠立即擡高,即使UAC目前尚未完全啓用。如果嚮導和頁面流程的所有路徑都需要提升,請在任務的入口點顯示UAC屏蔽。 正確使用UAC防護罩可幫助用戶預測何時需要仰角。

加成讀

注意:任何代碼被釋放到公共領域。無需歸屬。

+0

根據這個問題,它不是一個直接的答案,但它是我可以使用的一個很好的解決方案。 (實際上我通過命令行啓動服務 - System.Diagnostics.Process並檢查service.WaitForStatus是否成功 – Martin

0

您可以使用由框架提供的實用程序MT.exe(在SDK子目錄中)來生成強制您的應用程序以管理員模式運行的清單。在這種情況下,您應該沒有問題處理您的受保護服務。當然,交易是......你在管理模式下運行。

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

希望這會有所幫助。

+0

謝謝,但聽起來不像一個真正的解決方案。然後,我將不得不以管理員模式運行整個程序,而不僅僅是打開和關閉服務。 – Martin

+0

好吧,夠公平的。我不知道你的應用有多大。你有沒有嘗試將'startInfo.UseShellExecute'設置爲true?這可能會改變您的輸出重定向。 – DarkUrse

+0

我已經測試了這個,'true'和'false'。但是MSDN表示你必須將'UseShellExecute'設置爲'false',否則streamoutputredirection將不起作用。 – Martin

相關問題