36

I have a program that installs a service, and I'd like to be able to give the user the option later on to change the startup type to "Automatic".

The OS is XP - if it makes any difference (Windows APIs?).

How can I do this in .NET? C# if possible! :)

joshcomley
  • 28,099
  • 24
  • 107
  • 147

9 Answers9

64

I wrote a blog post on how to do this using P/Invoke. Using the ServiceHelper class from my post you can do the following to change the Start Mode.

var svc = new ServiceController("ServiceNameGoesHere");  
ServiceHelper.ChangeStartMode(svc, ServiceStartMode.Automatic); 
Peter Kelly
  • 14,253
  • 6
  • 54
  • 63
  • 3
    I tried this out in a few VMs, and everything seems to work well in Win 7. Thanks again. – Tom May 04 '11 at 17:37
  • 2
    @PeterKelly In blog you mentioned "There is a managed option in .NET - the System.ServiceProcess.ServiceController class which exposes some useful Properties and Methods for managing a Service. However, it provides no way to change the start mode of a Service." Why is it so? I mean why was this functionality left out? Also can this be invoked on remote machine like .NET's ServiceController takes MachineName? – Ankush Aug 21 '12 at 07:46
  • ServiceHelper appears to be deprecated now =( – Dave Feb 14 '14 at 16:27
  • 5
    @Dave ServiceHelper is a custom class I wrote in my blog post. Saying it is deprecated doesn't make sense! :) – Peter Kelly Feb 26 '14 at 11:32
  • 1
    One more thing... if you want to change the service on a different computer, then set the MachineName on the ServiceController class, then change ChangeStartMode method to the following: `public static void ChangeStartMode(ServiceController svc, ServiceStartMode mode) { string machineName = string.IsNullOrWhiteSpace(svc.MachineName) ? null : svc.MachineName; var scManagerHandle = OpenSCManager(machineName, null, SC_MANAGER_ALL_ACCESS);` ... – AngryHacker Aug 02 '16 at 04:47
  • This response and that of Christian K. point to using unmanaged code. If that's the only way -- no problem. But I'm wondering if now in 2018 there is a native .NET equivalent that I just haven't found yet? – Steve Robbins Sep 07 '18 at 19:11
13

In the service installer you have to say

[RunInstaller(true)]
public class ProjectInstaller : System.Configuration.Install.Installer 
{
    public ProjectInstaller()
    {
        ...
        this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
    }
}

You could also ask the user during installation and then set this value. Or just set this property in the visual studio designer.

Arthur
  • 7,939
  • 3
  • 29
  • 46
10

You can use the OpenService() and ChangeServiceConfig() native Win32 APIs for that purpose. I believe that there is some information on pinvoke.net and of course on MSDN. You might want to check out the P/Invoke Interopt Assistant.

Christian.K
  • 47,778
  • 10
  • 99
  • 143
7

You can use WMI to query all services and then match the service name to the inputted user value

Once the service has been found just change the StartMode Property

if(service.Properties["Name"].Value.ToString() == userInputValue)
{
    service.Properties["StartMode"].Value = "Automatic";
    //service.Properties["StartMode"].Value = "Manual";
}

//This will get all of the Services running on a Domain Computer and change the "Apple Mobile Device" Service to the StartMode of Automatic.  These two functions should obviously be separated, but it is simple to change a service start mode after installation using WMI

private void getServicesForDomainComputer(string computerName)
{
    ConnectionOptions co1 = new ConnectionOptions();
    co1.Impersonation = ImpersonationLevel.Impersonate;
    //this query could also be: ("select * from Win32_Service where name = '" + serviceName + "'");
    ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2");
    scope.Options = co1;

    SelectQuery query = new SelectQuery("select * from Win32_Service");

    using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
    {
        ManagementObjectCollection collection = searcher.Get();

        foreach (ManagementObject service in collection)
        {
            //the following are all of the available properties 
            //boolean AcceptPause
            //boolean AcceptStop
            //string Caption
            //uint32 CheckPoint
            //string CreationClassName
            //string Description
            //boolean DesktopInteract
            //string DisplayName
            //string ErrorControl
            //uint32 ExitCode;
            //datetime InstallDate;
            //string Name
            //string PathName
            //uint32 ProcessId
            //uint32 ServiceSpecificExitCode
            //string ServiceType
            //boolean Started
            //string StartMode
            //string StartName
            //string State
            //string Status
            //string SystemCreationClassName
            //string SystemName;
            //uint32 TagId;
            //uint32 WaitHint;
            if(service.Properties["Name"].Value.ToString() == "Apple Mobile Device")
            {
                service.Properties["StartMode"].Value = "Automatic";
            }
        }
    }         
}

I wanted to improve this response... One method to change startMode for Specified computer, service:

public void changeServiceStartMode(string hostname, string serviceName, string startMode)
{
    try
    {
        ManagementObject classInstance = 
            new ManagementObject(@"\\" + hostname + @"\root\cimv2",
                                 "Win32_Service.Name='" + serviceName + "'",
                                 null);

        // Obtain in-parameters for the method
        ManagementBaseObject inParams = classInstance.GetMethodParameters("ChangeStartMode");

        // Add the input parameters.
        inParams["StartMode"] = startMode;

        // Execute the method and obtain the return values.
        ManagementBaseObject outParams = classInstance.InvokeMethod("ChangeStartMode", inParams, null);

        // List outParams
        //Console.WriteLine("Out parameters:");
        //richTextBox1.AppendText(DateTime.Now.ToString() + ": ReturnValue: " + outParams["ReturnValue"]);
    }
    catch (ManagementException err)
    {
        //richTextBox1.AppendText(DateTime.Now.ToString() + ": An error occurred while trying to execute the WMI method: " + err.Message);
    }
}
StuiterSlurf
  • 2,464
  • 4
  • 34
  • 55
John Bartels
  • 2,583
  • 3
  • 19
  • 26
3

How about make use of c:\windows\system32\sc.exe to do that ?!

In VB.NET Codes, use System.Diagnostics.Process to call sc.exe to change the startup mode of a windows service. following is my sample code

    Public Function SetStartModeToDisabled(ByVal ServiceName As String) As Boolean
    Dim sbParameter As New StringBuilder
    With sbParameter
        .Append("config ")
        .AppendFormat("""{0}"" ", ServiceName)
        .Append("start=disabled")
    End With

    Dim processStartInfo As ProcessStartInfo = New ProcessStartInfo()
    Dim scExeFilePath As String = String.Format("{0}\sc.exe", Environment.GetFolderPath(Environment.SpecialFolder.System))
    processStartInfo.FileName = scExeFilePath
    processStartInfo.Arguments = sbParameter.ToString
    processStartInfo.UseShellExecute = True

    Dim process As Process = process.Start(processStartInfo)
    process.WaitForExit()

    Return process.ExitCode = 0 

End Function

2

In ProjectInstaller.cs, click/select the Service1 component on the design surface. In the properties windo there is a startType property for you to set this.

Matt Wrock
  • 6,590
  • 29
  • 23
0
ServiceInstaller myInstaller = new System.ServiceProcess.ServiceInstaller();
myInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
CSharpAtl
  • 7,374
  • 8
  • 39
  • 53
0

You can do it in the Installer class for the service by setting ServiceInstaller.StartType property to whatever value you get (you'll probably have to do this in a custom action since you want the user to specify) or you can modify the Service's "Start" REG_DWORD entry, the value 2 is automatic and 3 is manual. Its in HKEY_LOCAL_MACHINE\SYSTEM\Services\YourServiceName

HasaniH
  • 8,232
  • 6
  • 41
  • 59
  • Modifying the registry would be the best way to change the startup type post-install. You can use the Microsoft.Win32.Registry class to do it. – Chris Tybur Sep 25 '09 at 16:06
  • 3
    Don't do that. Hacks like these are exactly the reason why there is tons of compatibility stuff in newer windows versions. There are perfectly reasonable APIs to do it - just that they are not managed. However when you know you are on windows (you know in both cases Registry or API/unmanged), that shouldn't matter. – Christian.K Sep 28 '09 at 06:04
  • How is using the registry the way it is meant to be used a hack? The API modifies the registry, its just a layer of abstraction... – HasaniH Sep 28 '09 at 12:51
  • Well, if they ever need or want to change the (logical) layout of the registry for service registration, your code will break. So unless that part of the registry is explicitly document, and I doubt it is, it should be off-limits (aside manual troubleshooting, maybe). Besides, are you sure that the service control manager will actually recognize changes you make directly to the registry vs. those done via the official API? There might be a whole boatload of issues that only surface when you don't expect them. Just don't go down that road. – Christian.K Sep 28 '09 at 12:58
  • Well seeing that the API changes that registry value I'd say that the changes you are suggesting would break my code would also break the API. Did you even read the API docs you linked to? Because they mention the use of the registry... – HasaniH Sep 28 '09 at 14:09
  • Yeah, I did read them. However, I didn't bother too much with the registry paths (which are not full documented nevertheless). But anyway, I stand corrected on this point. If the MSDN page mentions the paths, than probably it is rather save to use them. – Christian.K Sep 28 '09 at 16:38
  • I see what Chris is getting at, I prefer to use the registry for reading values but only write to it as a last resort in case its use ever changes but the API keeps its old functionality for compatibility reasons. But if the docs say you can use it, go ahead and use it. – Patrick Apr 02 '12 at 14:01
-3

One way would be to uninstall previous service and install new one with updated parameters directly from your C# application.

You will need WindowsServiceInstaller in your app.

[RunInstaller(true)]
public class WindowsServiceInstaller : Installer
{
    public WindowsServiceInstaller()
    {
        ServiceInstaller si = new ServiceInstaller();
        si.StartType = ServiceStartMode.Automatic; // get this value from some global variable
        si.ServiceName = @"YOUR APP";
        si.DisplayName = @"YOUR APP";
        this.Installers.Add(si);

        ServiceProcessInstaller spi = new ServiceProcessInstaller();
        spi.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
        spi.Username = null;
        spi.Password = null;
        this.Installers.Add(spi);
    }
}

and to reinstall service just use these two lines.

ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
lubos hasko
  • 24,752
  • 10
  • 56
  • 61