Как программно включить службу общего доступа к портам Net.Tcp? - PullRequest
3 голосов
/ 01 февраля 2010

Я хотел бы программно включить и запустить службу общего доступа к портам Net.Tcp в C #. Я могу легко запустить службу, используя класс ServiceController. Однако как включить службу, которая по умолчанию отключена?

Я нашел одну рекомендацию в Интернете, чтобы установить для следующего раздела реестра значение 2 следующим образом, который предположительно устанавливает тип запуска службы на Автоматический:

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

Я попытался это сделать, и, хотя он, по-видимому, изменил тип запуска на Автоматический, в нем должно быть что-то большее, поскольку служба теперь не запускается (программно или вручную). Мне пришлось сбросить тип запуска вручную через services.msc, чтобы сбросить настройки, чтобы служба могла быть включена и снова запущена.

Кто-нибудь решил это?

Ответы [ 2 ]

11 голосов
/ 01 февраля 2010

Существует несколько способов сделать это в зависимости от того, насколько «чистым» является решение, которое вы хотите. Вот несколько вариантов. Обратите внимание, что все эти решения требуют административных прав и должны запускаться с повышенными правами.

Используйте командную строку через 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

Для некоторых людей это самый «чистый» метод, хотя более сложно понять, как правильно. В основном вам нужно позвонить ChangeServiceConfig из .NET. Однако для этого требуется, чтобы вы сначала вызвали 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);
        }
    }
}
0 голосов
/ 20 августа 2010

Существует простой ответ с использованием ServiceController

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

if (netTcpPortSharingService != null)
            {
                if(netTcpPortSharingService.Status!=ServiceControllerStatus.Running && netTcpPortSharingService.Status!=ServiceControllerStatus.StartPending)
                {
                    netTcpPortSharingService.Start();
                }
            }
...