[英]Remote controlling of windows service installed on intranet machine
我在本地IIS 7上部署了Web应用程序,并将应用程序池配置为在内置NETWORK SERVICE帐户下工作。 从此Web应用程序中,我需要检查Windows服务的状态(如果已启动,已停止等)。 我用这样的语句来得到它:
public string GetServiceStatus(string machine, string service)
{
var service = new ServiceController(machine, service);
service.Refresh();
return service.Status;
}
该machine
是我的Intranet(运行Windows服务的主机)中的主机的IP地址(也是在内置的NETWORK SERVICE帐户下)。
不幸的是,代码给出了一个例外:
service.Status threw an exception of type 'System.InvalidOperationException'
Cannot open MyService service on computer '192.168.0.7'. Access is denied.
问题出在哪儿 ?
问题是网络服务没有足够的权限来控制Windows服务。 我需要切换到另一个用户上下文才能对其进行控制。 但是我不想在整个应用程序中都这样做。 相反,我在特定身份下搜索任意一段代码执行。
我检查了很多用于模仿的资源,包括Malcolm 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服务的权限-为此,可以将其添加到Administrators组。
现在,我可以通过以下方式启动/停止我的服务并获取其当前状态:
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下是正确的。
顺便说一句: Impersonate
是包装函数,用于设置模拟,然后将其传递给lambda,以完成实际工作。 这种写代码风格的计算机科学术语是高阶编程 。
如果可以使用模拟,请尝试
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.