Windows 服务配置
Windows Service configuration
问题:
我有一个 windows 服务,我想以交互模式启动它。
我们的一些客户没有使用服务的经验,我们的服务需要以可以与桌面交互的方式进行配置。
从命令行我会这样配置它:
C:\Windows\system32>sc config myservice obj= LocalSystem type=
interact type= own
由于程序是从配置 GUI 启动的,所以我想在 C# 中设置值:
ServiceController[] mySc = ServiceController.GetServices();
foreach (ServiceController sc in mySc)
{
if (sc.DisplayName == "myservice")
{
if (sc.Status == ServiceControllerStatus.Stopped)
{
//sc.ServiceType <-- readonly so i can't set it
sc.Start();
}
break;
}
}
我发现唯一可行的方法是使用进程对象
var process = new Process();
var processStartInfo = new ProcessStartInfo(startpath);
arg += "all my arguments...";
processStartInfo.Arguments = arg;
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo = processStartInfo;
process.Start();
process.WaitForExit();
我的问题:
有没有办法通过 ServiceController 配置服务?我看到你可以通过 sc.Start(args[]) 启动服务,但我还没有找到可以传递的参数。
在 Michael(见上面的评论)提供的一篇文章的帮助下,我创建了一个 class,使我能够配置服务。
我的代码主要是这个博客的副本 post 有一些小的变化:Changing Start Mode of a Windows Service
这是我的 Class:
public static class C_ServiceControllerExtension
{
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean ChangeServiceConfig(
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
[In] char[] lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(
IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr OpenSCManager(
string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
public static extern int CloseServiceHandle(IntPtr hSCObject);
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
/// <summary>
/// Changes the configuration of the service
/// </summary>
/// <param name="svc">Service controller</param>
/// <param name="mode">ServiceStartMode || 0</param>
/// <param name="type">ServiceType || 0</param>
public static bool ChangeServiceConfiguration(this ServiceController svc, ServiceStartMode mode, ServiceType type)
{
uint uMode = SERVICE_NO_CHANGE;
uint uType = SERVICE_NO_CHANGE;
if (mode > 0)
{
uMode = (uint)mode;
}
if (type > 0)
{
uType = (uint)type;
}
var scManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if (scManagerHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Manager Error");
}
var serviceHandle = OpenService(
scManagerHandle,
svc.ServiceName,
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if (serviceHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Error");
}
var result = ChangeServiceConfig(
serviceHandle,
uType,
uMode,
SERVICE_NO_CHANGE,
null,
null,
IntPtr.Zero,
null,
null,
null,
null);
if (result == false)
{
int nError = Marshal.GetLastWin32Error();
var win32Exception = new Win32Exception(nError);
return false;
//throw new ExternalException("Could not change service start type: " + win32Exception.Message);
}
CloseServiceHandle(serviceHandle);
CloseServiceHandle(scManagerHandle);
return true;
}
}
ChangeServiceConfiguration 方法是一种扩展方法,因此您可以直接在 ServiceController
上调用该方法。
我调用方法如下:
ServiceController[] mySc = ServiceController.GetServices();
bool startedServiceCorrect = false;
foreach (ServiceController sc in mySc)
{
if (sc.DisplayName == "myservice")
{
if (sc.Status == ServiceControllerStatus.Stopped)
{
if (sc.ServiceType != (ServiceType.InteractiveProcess | ServiceType.Win32OwnProcess))
{
startedServiceCorrect = sc.ChangeServiceConfiguration(0, (ServiceType.InteractiveProcess | ServiceType.Win32OwnProcess));
}
try
{
sc.Start();
}
catch
{
startedServiceCorrect = false;
}
}
break;
}
}
如果您使用的是 .Net 3.0 及更高版本,扩展方法应该开箱即用,但如果您使用的是 .Net2.0,那么您必须添加这个小命名空间,这样扩展方法才能工作:
Extension Method C# 2.0
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class
| AttributeTargets.Method)]
public sealed class ExtensionAttribute : Attribute { }
}
问题:
我有一个 windows 服务,我想以交互模式启动它。 我们的一些客户没有使用服务的经验,我们的服务需要以可以与桌面交互的方式进行配置。
从命令行我会这样配置它:
C:\Windows\system32>sc config myservice obj= LocalSystem type= interact type= own
由于程序是从配置 GUI 启动的,所以我想在 C# 中设置值:
ServiceController[] mySc = ServiceController.GetServices();
foreach (ServiceController sc in mySc)
{
if (sc.DisplayName == "myservice")
{
if (sc.Status == ServiceControllerStatus.Stopped)
{
//sc.ServiceType <-- readonly so i can't set it
sc.Start();
}
break;
}
}
我发现唯一可行的方法是使用进程对象
var process = new Process();
var processStartInfo = new ProcessStartInfo(startpath);
arg += "all my arguments...";
processStartInfo.Arguments = arg;
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo = processStartInfo;
process.Start();
process.WaitForExit();
我的问题:
有没有办法通过 ServiceController 配置服务?我看到你可以通过 sc.Start(args[]) 启动服务,但我还没有找到可以传递的参数。
在 Michael(见上面的评论)提供的一篇文章的帮助下,我创建了一个 class,使我能够配置服务。
我的代码主要是这个博客的副本 post 有一些小的变化:Changing Start Mode of a Windows Service
这是我的 Class:
public static class C_ServiceControllerExtension
{
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Boolean ChangeServiceConfig(
IntPtr hService,
UInt32 nServiceType,
UInt32 nStartType,
UInt32 nErrorControl,
String lpBinaryPathName,
String lpLoadOrderGroup,
IntPtr lpdwTagId,
[In] char[] lpDependencies,
String lpServiceStartName,
String lpPassword,
String lpDisplayName);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr OpenService(
IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr OpenSCManager(
string machineName, string databaseName, uint dwAccess);
[DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
public static extern int CloseServiceHandle(IntPtr hSCObject);
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;
/// <summary>
/// Changes the configuration of the service
/// </summary>
/// <param name="svc">Service controller</param>
/// <param name="mode">ServiceStartMode || 0</param>
/// <param name="type">ServiceType || 0</param>
public static bool ChangeServiceConfiguration(this ServiceController svc, ServiceStartMode mode, ServiceType type)
{
uint uMode = SERVICE_NO_CHANGE;
uint uType = SERVICE_NO_CHANGE;
if (mode > 0)
{
uMode = (uint)mode;
}
if (type > 0)
{
uType = (uint)type;
}
var scManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
if (scManagerHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Manager Error");
}
var serviceHandle = OpenService(
scManagerHandle,
svc.ServiceName,
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
if (serviceHandle == IntPtr.Zero)
{
throw new ExternalException("Open Service Error");
}
var result = ChangeServiceConfig(
serviceHandle,
uType,
uMode,
SERVICE_NO_CHANGE,
null,
null,
IntPtr.Zero,
null,
null,
null,
null);
if (result == false)
{
int nError = Marshal.GetLastWin32Error();
var win32Exception = new Win32Exception(nError);
return false;
//throw new ExternalException("Could not change service start type: " + win32Exception.Message);
}
CloseServiceHandle(serviceHandle);
CloseServiceHandle(scManagerHandle);
return true;
}
}
ChangeServiceConfiguration 方法是一种扩展方法,因此您可以直接在 ServiceController
上调用该方法。
我调用方法如下:
ServiceController[] mySc = ServiceController.GetServices();
bool startedServiceCorrect = false;
foreach (ServiceController sc in mySc)
{
if (sc.DisplayName == "myservice")
{
if (sc.Status == ServiceControllerStatus.Stopped)
{
if (sc.ServiceType != (ServiceType.InteractiveProcess | ServiceType.Win32OwnProcess))
{
startedServiceCorrect = sc.ChangeServiceConfiguration(0, (ServiceType.InteractiveProcess | ServiceType.Win32OwnProcess));
}
try
{
sc.Start();
}
catch
{
startedServiceCorrect = false;
}
}
break;
}
}
如果您使用的是 .Net 3.0 及更高版本,扩展方法应该开箱即用,但如果您使用的是 .Net2.0,那么您必须添加这个小命名空间,这样扩展方法才能工作: Extension Method C# 2.0
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class
| AttributeTargets.Method)]
public sealed class ExtensionAttribute : Attribute { }
}