0

Problem:

I have a windows Service which I want to start in interactive mode. Some of our customers are not experienced with services and our service needs to be configured in the way that it can interact with the desktop.

From the commandline I would configure it like this:

C:\Windows\system32>sc config myservice obj= LocalSystem type= interact type= own

Since the program is started from a configuration GUI I want to set the values in C#:

ServiceController[] mySc = ServiceController.GetServices();

foreach (ServiceController sc in mySc)
{
    if (sc.DisplayName == "myservice")
    {

        if (sc.Status == ServiceControllerStatus.Stopped)
        {
            //sc.ServiceType <-- readonly so i can't set it
            sc.Start();

        }
        break;
    }
}

The only way I have found which could work is to use a process object

var process = new Process();
var processStartInfo = new ProcessStartInfo(startpath);
arg += "all my arguments...";
processStartInfo.Arguments = arg;
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo = processStartInfo;
process.Start();
process.WaitForExit();

My question:

Is there a way to configure the service via the ServiceController ? I saw that you can start the service via sc.Start(args[]) but I haven't found the arguments that can be passed.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Bongo
  • 2,933
  • 5
  • 36
  • 67
  • Do you want a Windows service which can show popup windows etc.? – Orkun Bekar Apr 30 '15 at 09:42
  • No the service should be able to get access to the filesystem – Bongo Apr 30 '15 at 09:43
  • And you achieve that by using process but you don't want to use process, you want to use SC right? – Orkun Bekar Apr 30 '15 at 09:45
  • If your operating system is Windows 7+ then there is no way I think. I had similar problems and I had to use process also. – Orkun Bekar Apr 30 '15 at 09:48
  • 1
    There actually seems a way to do this. Check [this SO post](http://stackoverflow.com/questions/1477618/how-do-i-change-a-windows-services-startup-type-in-net-post-install), the answer that has most votes will navigate you to [blog post](http://peterkellyonline.blogspot.co.uk/2011/04/configuring-windows-service.html) which contains helper method to configure win service. One of the parameters you can pass to configure it is - `nServiceType` which I believe is what you're looking for. – Michael Apr 30 '15 at 09:56
  • @Michael looks primissing, I will look into it – Bongo Apr 30 '15 at 10:52
  • @Michael Okay worked great. – Bongo Apr 30 '15 at 14:06
  • 1
    @Bongo glad it helped! Please update your question, and include some details how you solved your problem, link to SO question and blog post you referred and possibly some code you used for future reference for others. – Michael Apr 30 '15 at 14:31
  • No Problem, will Update asap – Bongo Apr 30 '15 at 15:07
  • @Michael Thanks for the great blog post again. I provided an answer with all the details. – Bongo May 05 '15 at 08:31

1 Answers1

1

With the help of an article which Michael(See the comments above) provided I created a class which enables me to configurate the service.

My code is mostly a copy of this blog post with some minor changes: Changing Start Mode of a Windows Service

Here is my Class:

 public static class C_ServiceControllerExtension
    {
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern Boolean ChangeServiceConfig(
            IntPtr hService,
            UInt32 nServiceType,
            UInt32 nStartType,
            UInt32 nErrorControl,
            String lpBinaryPathName,
            String lpLoadOrderGroup,
            IntPtr lpdwTagId,
            [In] char[] lpDependencies,
            String lpServiceStartName,
            String lpPassword,
            String lpDisplayName);

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

        [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr OpenSCManager(
            string machineName, string databaseName, uint dwAccess);

        [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
        public static extern int CloseServiceHandle(IntPtr hSCObject);

        private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
        private const uint SERVICE_QUERY_CONFIG = 0x00000001;
        private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
        private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;

        /// <summary>
        /// Changes the configuration of the service
        /// </summary>
        /// <param name="svc">Service controller</param>
        /// <param name="mode">ServiceStartMode || 0</param>
        /// <param name="type">ServiceType || 0</param>
        public static bool ChangeServiceConfiguration(this ServiceController svc, ServiceStartMode mode, ServiceType type)
        {
            uint uMode = SERVICE_NO_CHANGE;
            uint uType = SERVICE_NO_CHANGE;
            if (mode > 0) 
            {
                uMode = (uint)mode;
            }

            if (type > 0) 
            {
                uType = (uint)type;
            }

            var scManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
            if (scManagerHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Manager Error");
            }

            var serviceHandle = OpenService(
                scManagerHandle,
                svc.ServiceName,
                SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);

            if (serviceHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Error");
            }

            var result = ChangeServiceConfig(
                serviceHandle,
                uType,
                uMode,
                SERVICE_NO_CHANGE,
                null,
                null,
                IntPtr.Zero,
                null,
                null,
                null,
                null);

            if (result == false)
            {
                int nError = Marshal.GetLastWin32Error();
                var win32Exception = new Win32Exception(nError);
                return false;
                //throw new ExternalException("Could not change service start type: " + win32Exception.Message);
            }

            CloseServiceHandle(serviceHandle);
            CloseServiceHandle(scManagerHandle);
            return true;
        }
    }

The ChangeServiceConfiguration method is an extension method so you can call the method directly on your ServiceController.

I call the method as follows:

ServiceController[] mySc = ServiceController.GetServices();
bool startedServiceCorrect = false;
foreach (ServiceController sc in mySc)
{
    if (sc.DisplayName == "myservice")
    {
        if (sc.Status == ServiceControllerStatus.Stopped)
        {
            if (sc.ServiceType != (ServiceType.InteractiveProcess | ServiceType.Win32OwnProcess)) 
            {
                startedServiceCorrect = sc.ChangeServiceConfiguration(0, (ServiceType.InteractiveProcess | ServiceType.Win32OwnProcess));
            }
            try
            {
                sc.Start();
            }
            catch
            { 

                startedServiceCorrect = false;
            }
        }
        break;
    }
}

If you are using .Net 3.0 and above the extension methods should work out of the box but if you are using .Net2.0 then you have to add this little namespace so the extension method will work: Extension Method C# 2.0

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class
         | AttributeTargets.Method)]
    public sealed class ExtensionAttribute : Attribute { }
}
Community
  • 1
  • 1
Bongo
  • 2,933
  • 5
  • 36
  • 67
  • I think I provided all my sources. If I forget to mention your code please add a comment – Bongo May 05 '15 at 08:30