2013-01-15 160 views
1

我在本地IIS 7上部署了Web應用程序,應用程序池配置爲在內置的NETWORK SERVICE帳戶下工作。從這個Web應用程序我需要檢查我的Windows服務的狀態(如果它是開始,停止等)。我用這樣的語句來得到它:遠程控制安裝在Intranet機器上的Windows服務

public string GetServiceStatus(string machine, string service) 
{ 
    var service = new ServiceController(machine, service); 
    service.Refresh(); 
    return service.Status; 
} 

machine是在我的內網主機的IP地址(讓它是192.168.0.7),Windows服務運行的位置 - 也正在內置網絡服務帳戶。

不幸的是,代碼給出了一個例外:

service.Status threw an exception of type 'System.InvalidOperationException' 
Cannot open MyService service on computer '192.168.0.7'. Access is denied. 

問題出在哪裏?

回答

0

的問題是網絡服務不具有控制窗口服務足夠的權限。我需要切換到另一個用戶上下文才能控制它。但我不想爲整個應用程序做這件事。相反,我在特定的身份下搜索任意一段代碼。

我檢查了很多模仿資源包括由馬爾科姆Frexner所示。因爲我正在使用Windows 7(64位)以及Windows Server 2008 R2(64位),所以我發現它們不適合我。我結束了這樣的通用的解決方案:

using System; 
using System.ComponentModel; 
using System.Runtime.InteropServices; 
using System.Security.Principal; 

namespace Thing.Namespace 
{ 
    public enum LogOnType 
    { 
     LogOn32LogOnInteractive = 2, 
     LogOn32LogOnNetwork = 3, 
     LogOn32LogOnBatch = 4, 
     LogOn32LogOnService = 5, 
     LogOn32LogOnUnlock = 7, 
     LogOn32LogOnNetworkCleartext = 8, 
     LogOn32LogOnNewCredentials = 9 
    } 

    public enum LogOnProvider 
    { 
     LogOn32ProviderDefault = 0, 
     LogOn32ProviderWinnt35 = 1, 
     LogOn32ProviderWinnt40 = 2, 
     LogOn32ProviderWinnt50 = 3 
    } 

    public enum ImpersonationLevel 
    { 
     SecurityAnonymous = 0, 
     SecurityIdentification = 1, 
     SecurityImpersonation = 2, 
     SecurityDelegation = 3 
    } 

    public static class IdentityBoss 
    { 
     private static WindowsImpersonationContext _impersonationContext; 
     private static readonly object _locker = new object(); 

     private static class NativeMethods 
     { 
      [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
      public static extern int LogonUser(String lpszUserName, 
               String lpszDomain, 
               String lpszPassword, 
               int dwLogonType, 
               int dwLogonProvider, 
               ref IntPtr phToken); 

      [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
      public static extern int DuplicateToken(IntPtr hToken, 
                int impersonationLevel, 
                ref IntPtr hNewToken); 

      [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
      [return: MarshalAs(UnmanagedType.Bool)] 
      public static extern bool RevertToSelf(); 

      [DllImport("kernel32.dll", SetLastError = true)] 
      [return: MarshalAs(UnmanagedType.Bool)] 
      public static extern bool CloseHandle(IntPtr handle); 
     } 

     public static void Impersonate(Action action, string user, string domain, string password, 
             LogOnType logOnType, LogOnProvider logOnProvider, 
             ImpersonationLevel impersonationLevel) 
     { 
      try 
      { 
       ImpersonateValidUser(user, domain, password, logOnType, logOnProvider, impersonationLevel); 
       action(); 
      } 
      finally 
      { 
       UndoImpersonation(); 
      } 
     } 

     public static void ImpersonateHappily(Action action, string user, string domain, string password) 
     { 
      Impersonate(action, user, domain, password, LogOnType.LogOn32LogOnNetworkCleartext, 
         LogOnProvider.LogOn32ProviderDefault, ImpersonationLevel.SecurityImpersonation); 
     } 

     public static TResult Impersonate<TResult>(Func<TResult> action, string user, string domain, string password, 
                LogOnType logOnType, LogOnProvider logOnProvider, 
                ImpersonationLevel impersonationLevel) 
     { 
      try 
      { 
       ImpersonateValidUser(user, domain, password, logOnType, logOnProvider, impersonationLevel); 
       return action(); 
      } 
      finally 
      { 
       UndoImpersonation(); 
      } 
     } 

     public static TResult ImpersonateHappily<TResult>(Func<TResult> action, string user, string domain, string password) 
     { 
      return Impersonate(action, user, domain, password, LogOnType.LogOn32LogOnNetworkCleartext, 
           LogOnProvider.LogOn32ProviderDefault, ImpersonationLevel.SecurityImpersonation); 
     } 

     private static void ImpersonateValidUser(String userName, String domain, String password, LogOnType logonType, LogOnProvider logonProvider, ImpersonationLevel impersonationLevel) 
     { 
      lock (_locker) 
      { 
       var token = IntPtr.Zero; 
       var tokenDuplicate = IntPtr.Zero; 
       WindowsIdentity tempWindowsIdentity = null; 

       try 
       { 
        if (!NativeMethods.RevertToSelf()) 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        if (NativeMethods.LogonUser(userName, domain, password, (int) logonType, (int) logonProvider,ref token) == 0) 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 
        if (NativeMethods.DuplicateToken(token, (int) impersonationLevel, ref tokenDuplicate) == 0) 
         throw new Win32Exception(Marshal.GetLastWin32Error()); 

        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); 
        _impersonationContext = tempWindowsIdentity.Impersonate(); 
       } 
       finally 
       { 
        if (token != IntPtr.Zero) 
         NativeMethods.CloseHandle(token); 
        if (tokenDuplicate != IntPtr.Zero) 
         NativeMethods.CloseHandle(tokenDuplicate); 
        if (tempWindowsIdentity != null) 
         tempWindowsIdentity.Dispose(); 
       } 
      } 
     } 

     private static void UndoImpersonation() 
     { 
      lock (_locker) 
      { 
       if (_impersonationContext != null) 
       { 
        _impersonationContext.Undo(); 
       } 
      }    
     } 
    } 
} 

此外,我需要我的機器安裝了服務上創建新的用戶。用戶必須具有控制Windows服務的權限 - 爲此,可以將其添加到管理員組。現在

我可以啓動/停止我的服務,並得到他們的當前狀態以這樣的方式:

private const string user = "MyUser"; 
private const string domain = "."; 
private const string password = "MyPa$$w0rd"; 

public string StartService(string machine, string service) 
{ 
    IdentityBoss.ImpersonateHappily(
     () => 
      { 
       Controller.Instance.StartService(machine, service); 
      }, user, domain, password 
     ); 
} 

public string GetServiceStatus(string machine, string service) 
{ 
    return IdentityBoss.ImpersonateHappily(
     () => 
      { 
       return Controller.Instance.GetServiceStatus(machine, service); 
      }, user, domain, password 
     ); 
} 

ImpersonateHappily僅僅是一個函數,它接受它與我的操作系統工作參數。來自網絡的其他類似解決方案使用dwLogonType參數傳遞給win 32 api函數LogonUserA,值爲2或9,而在我的系統中,值8爲正確。

BTW:Impersonate是一個包裝函數,它設置模擬,然後傳遞一個lambda來完成實際的工作。這種編寫代碼的花式計算機科學術語是higher-order programming

+0

這適用於我的本地計算機,不適用於Intranet。它說用戶/合格組合不被識別。這怎麼可能? – AgentFire

+0

我不知道問題的根源是什麼。嘗試提供完整的堆棧跟蹤以揭示更多信息。 – jwaliszko

相關問題