3

我希望以编程方式启用和启动 C# 中的 Net.Tcp 端口共享服务。我可以使用 ServiceController 类轻松启动服务。但是,如何启用默认禁用的服务?

我在网上找到了一个建议,将以下注册表项设置为 2,如下所示,据称将服务启动类型设置为自动:

string path = "SYSTEM\\CurrentControlSet\\Services\\" + serviceName;
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path, true)) {
    key.SetValue("Start", 2);
}

我尝试了这个,虽然它似乎将启动类型更改为自动,但肯定还有更多,因为服务现在不会启动(以编程方式或手动)。我必须通过 services.msc 手动重置启动类型以重置事物,以便可以启用并再次启动该服务。

有没有人解决这个问题?

4

2 回答 2

11

有不止一种方法可以做到这一点,具体取决于您想要的解决方案的“纯”程度。这里有一些选项。 请注意,所有这些解决方案都需要管理权限,并且必须在提升的进程中运行。

通过 C# 使用命令提示符

sc.exe这将涉及通过命令行参数对服务的启动类型进行脱壳和更改。这类似于您上面提到的解决方案,除了不需要注册表黑客。

namespace Sample
{
    using System;
    using System.Diagnostics;
    using System.Globalization;

    internal class ServiceSample
    {
        private static bool ChangeStartupType(string serviceName, string startupType)
        {
            string arguments = string.Format(
                CultureInfo.InvariantCulture,
                "config {0} start= {1}",
                serviceName,
                startupType);
            using (Process sc = Process.Start("sc.exe", arguments))
            {
                sc.WaitForExit();
                return sc.ExitCode == 0;
            }
        }

        private static void Main()
        {
            ServiceSample.ChangeStartupType("NetTcpPortSharing", "auto");
        }
    }
}

使用 WMI

这需要System.Management.dll. 在这里,我们将使用 WMI 功能来ChangeStartMode提供服务。

namespace Sample
{
    using System;
    using System.Globalization;
    using System.Management;

    internal class ServiceSample
    {
        private static bool ChangeStartupType(string serviceName, string startupType)
        {
            const string MethodName = "ChangeStartMode";
            ManagementPath path = new ManagementPath();
            path.Server = ".";
            path.NamespacePath = @"root\CIMV2";
            path.RelativePath = string.Format(
                CultureInfo.InvariantCulture,
                "Win32_Service.Name='{0}'",
                serviceName);
            using (ManagementObject serviceObject = new ManagementObject(path))
            {
                ManagementBaseObject inputParameters = serviceObject.GetMethodParameters(MethodName);
                inputParameters["startmode"] = startupType;
                ManagementBaseObject outputParameters = serviceObject.InvokeMethod(MethodName, inputParameters, null);
                return (uint)outputParameters.Properties["ReturnValue"].Value == 0;
            }
        }

        private static void Main()
        {
            ServiceSample.ChangeStartupType("NetTcpPortSharing", "Automatic");
        }
    }
}

使用 P/Invoke 到 Win32 API

对某些人来说,这是最“纯粹”的方法,尽管要做到正确更棘手。基本上,您需要从 .NET调用ChangeServiceConfig 。但是,这要求您首先为指定的服务调用OpenService ,并且需要事先调用OpenSCManager(完成后不要忘记CloseServiceHandle!)。

注意:此代码仅用于演示目的。它不包含任何错误处理并且可能会泄漏资源。一个正确的实现应该使用SafeHandle类型来确保正确的清理,并应该添加适当的错误检查。

namespace Sample
{
    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;

    internal class ServiceSample
    {
        private const uint SC_MANAGER_CONNECT = 0x1;
        private const uint SERVICE_CHANGE_CONFIG = 0x2;
        private const uint STANDARD_RIGHTS_WRITE = 0x20000;

        private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;

        private const uint SERVICE_AUTO_START = 0x2;
        private const uint SERVICE_DEMAND_START = 0x3;
        private const uint SERVICE_DISABLED = 0x4;

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, uint dwDesiredAccess);

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool ChangeServiceConfig(IntPtr hService, uint dwServiceType, uint dwStartType, uint dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword, string lpDisplayName);

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool CloseServiceHandle(IntPtr hSCObject);

        private static bool ChangeStartupType(string serviceName, uint startType)
        {
            IntPtr scManager = ServiceSample.OpenSCManager(null, null, ServiceSample.SC_MANAGER_CONNECT);
            IntPtr service = ServiceSample.OpenService(scManager, serviceName, ServiceSample.SERVICE_CHANGE_CONFIG | ServiceSample.STANDARD_RIGHTS_WRITE);
            bool succeeded = ServiceSample.ChangeServiceConfig(service, ServiceSample.SERVICE_NO_CHANGE, startType, ServiceSample.SERVICE_NO_CHANGE, null, null, IntPtr.Zero, null, null, null, null);
            ServiceSample.CloseServiceHandle(service);
            ServiceSample.CloseServiceHandle(scManager);

            return succeeded;
        }

        private static void Main()
        {
            ServiceSample.ChangeStartupType("NetTcpPortSharing", ServiceSample.SERVICE_AUTO_START);
        }
    }
}
于 2010-02-01T06:15:28.170 回答
0

使用 ServiceController 有一个简单的答案

System.ServiceProcess.ServiceController netTcpPortSharingService = new System.ServiceProcess.ServiceController("NetTcpPortSharing");

if (netTcpPortSharingService != null)
            {
                if(netTcpPortSharingService.Status!=ServiceControllerStatus.Running && netTcpPortSharingService.Status!=ServiceControllerStatus.StartPending)
                {
                    netTcpPortSharingService.Start();
                }
            }
于 2010-08-20T05:35:43.963 回答