【问题标题】:How can I enable Net.Tcp Port Sharing Service programmatically?如何以编程方式启用 Net.Tcp 端口共享服务?
【发布时间】:2010-02-01 05:45:19
【问题描述】:

我希望在 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 手动重置启动类型来重置东西,以便可以启用并再次启动服务。

有人解决了吗?

【问题讨论】:

    标签: c# windows-services net.tcp


    【解决方案1】:

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

    通过 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");
            }
        }
    }
    

    对 Win32 API 使用 P/Invoke

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

    注意:此代码仅用于演示目的。它不包含任何错误处理并且可能会泄漏资源。正确的实现应使用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);
            }
        }
    }
    

    【讨论】:

    • 这看起来很有希望!我看到我的开发机器上同时提供了 sc.exe 和 System.Management.dll。在没有 Visal Studio 的标准 Windows 安装(XP、Vista、Windows 7、Server 2003、2008 等)上,这些选项中的一个或两个是否可用?这些选项是否需要在机器上安装资源工具包或 SDK?
    • sc.exe 至少从 Windows 2000 开始在每个 Windows 系统上都是标准的。System.Management.dll 是 .NET 的一部分,只要 .NET 存在(至少从 .NET 1.1 开始)就会存在。
    • 我现在将使用 WMI 选项 - 它就像一个魅力!非常感谢!
    【解决方案2】:

    使用 ServiceController 有一个简单的答案

    System.ServiceProcess.ServiceController netTcpPortSharingService = new System.ServiceProcess.ServiceController("NetTcpPortSharing");
    
    if (netTcpPortSharingService != null)
                {
                    if(netTcpPortSharingService.Status!=ServiceControllerStatus.Running && netTcpPortSharingService.Status!=ServiceControllerStatus.StartPending)
                    {
                        netTcpPortSharingService.Start();
                    }
                }
    

    【讨论】:

    • 好吧,我才意识到如果startType是禁用的,上面的代码就不起作用了。
    猜你喜欢
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 1970-01-01
    • 2012-01-21
    • 1970-01-01
    相关资源
    最近更新 更多