Как программно включить службу общего доступа к портам Net.Tcp?

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

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

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

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

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


person Elan    schedule 01.02.2010    source источник


Ответы (2)


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

Используйте командную строку через 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 из . СЕТЬ. Однако для этого необходимо сначала вызвать 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);
        }
    }
}
person bobbymcr    schedule 01.02.2010
comment
Это выглядит многообещающе! Я вижу, что на моем компьютере для разработки доступны и sc.exe, и System.Management.dll. Доступны ли один или оба этих варианта при стандартной установке Windows (XP, Vista, Windows 7, Server 2003, 2008 и т. д.) без Visal Studio? Требуют ли эти варианты установки Resource Kit или SDK на компьютере? - person Elan; 01.02.2010
comment
sc.exe является стандартным для каждой системы Windows, по крайней мере, начиная с Windows 2000. System.Management.dll является частью .NET и будет присутствовать до тех пор, пока существует .NET (по крайней мере, начиная с .NET 1.1). - person bobbymcr; 01.02.2010
comment
Сейчас я выбираю вариант WMI - он сработал как шарм! Большое спасибо! - person Elan; 01.02.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();
                }
            }
person Wayne Lo    schedule 20.08.2010
comment
Ну, я просто понимаю, что если startType отключен, приведенный выше код не будет работать. - person Wayne Lo; 20.08.2010