1

I have a Win Form Application, from where I want to create/generate a new windows service on button click. When ever a user clicks on that button, a windows service should be generated performing some task. If it is possible, how to do?

The Win Form App I have, actually process some data files. The user dose some setting for the data file like apply some constraints and mapping of data file columns with the SQL table and column. In the last this application should generate a service that should process such data files using that settings(already saved in DB). My actually question is, is it possible to generate a windows service?? how it will process the file, its another thing. Focus on question.

  • By `create` you mean actually generating the code the service runs? – Zohar Peled Apr 12 '18 at 07:07
  • 1
    This doesnt make any sense, please take the time to exactly explain what you mean – TheGeneral Apr 12 '18 at 07:08
  • @ZoharPeled I have updated the question, I hope you can understand it. – Muhammad Muneeb Hafeez Apr 12 '18 at 07:21
  • @TheGeneral please see the question again. – Muhammad Muneeb Hafeez Apr 12 '18 at 07:21
  • Sorry, still unclear. A window service is just a program with a specific API and without a user interface, that windows can run automatically. I still don't understand what you mean by `Generate` - do you mean that the winforms app should somehow create the code for the windows service, or just activate an existing service? – Zohar Peled Apr 12 '18 at 07:25
  • I mean just like we create a new project of windows service in Visual Studio and when we build this project, VS generate some exe files in bin > Debug folder. This task i want to perform through c# code when i click on a button. – Muhammad Muneeb Hafeez Apr 12 '18 at 07:42
  • Ok, so you want to generate the entire project, code included, when the user clicks a button. Seems to me like a massive overkill. Not to say that's impossible (and can be easier using something like [TopShelf](https://github.com/Topshelf/Topshelf/wiki) to handle all the service-related stuff for you), but I think it's probably going to the wrong direction. I would suggest re-thinking this approach. – Zohar Peled Apr 12 '18 at 09:45
  • @MuhammadMuneebHafeez: does my answer help you? Thanks! – Cosmin Ioniță Apr 17 '18 at 15:45

1 Answers1

0

I will assume that you are refering to installing a Windows Service.

As you may know, when you create a Windows Service (from Visual Studio), you will write some code for Start / Stop methods (the code that the service will run) but if you actually want to run the service, you have to install it, as you can see in this post.

Then, after you install it, you can start it by going to Services -> Select your service -> Start.

If this is your scenario, then yes, you can do this from a C# app, using this [1] API that I found on the web.

Now, using this API, you can install and even start your service. However, in order to perform something useful, you need to send some data to it. This can be done very easy using WCF (there are plenty examples on the web).

[1]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Service_API
{
  public static class ServiceManager
  {
    private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
    private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010;

    [StructLayout(LayoutKind.Sequential)]
    private class SERVICE_STATUS
    {
        public int dwServiceType = 0;
        public ServiceState dwCurrentState = 0;
        public int dwControlsAccepted = 0;
        public int dwWin32ExitCode = 0;
        public int dwServiceSpecificExitCode = 0;
        public int dwCheckPoint = 0;
        public int dwWaitHint = 0;
    }

    #region OpenSCManager
    [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
    static extern IntPtr OpenSCManager(string machineName, string databaseName, ScmAccessRights dwDesiredAccess);
    #endregion

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

    #region CreateService
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceAccessRights dwDesiredAccess, int dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword);
    #endregion

    #region CloseServiceHandle
    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CloseServiceHandle(IntPtr hSCObject);
    #endregion

    #region QueryServiceStatus
    [DllImport("advapi32.dll")]
    private static extern int QueryServiceStatus(IntPtr hService, SERVICE_STATUS lpServiceStatus);
    #endregion

    #region DeleteService
    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool DeleteService(IntPtr hService);
    #endregion

    #region ControlService
    [DllImport("advapi32.dll")]
    private static extern int ControlService(IntPtr hService, ServiceControl dwControl, SERVICE_STATUS lpServiceStatus);
    #endregion

    #region StartService
    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern int StartService(IntPtr hService, int dwNumServiceArgs, int lpServiceArgVectors);
    #endregion

    public static void Uninstall(string serviceName)
    {
        IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess);

        try
        {
            IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess);
            if (service == IntPtr.Zero)
                throw new ApplicationException("Service not installed.");

            try
            {
                StopService(service);
                if (!DeleteService(service))
                    throw new ApplicationException("Could not delete service " + Marshal.GetLastWin32Error());
            }
            finally
            {
                CloseServiceHandle(service);
            }
        }
        finally
        {
            CloseServiceHandle(scm);
        }
    }

    public static bool ServiceIsInstalled(string serviceName)
    {
        IntPtr scm = OpenSCManager(ScmAccessRights.Connect);

        try
        {
            IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus);

            if (service == IntPtr.Zero)
                return false;

            CloseServiceHandle(service);
            return true;
        }
        finally
        {
            CloseServiceHandle(scm);
        }
    }

    public static void InstallAndStart(string serviceName, string displayName, string fileName)
    {
        IntPtr scm = OpenSCManager(ScmAccessRights.AllAccess);

        try
        {
            IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.AllAccess);

            if (service == IntPtr.Zero)
                service = CreateService(scm, serviceName, displayName, ServiceAccessRights.AllAccess, SERVICE_WIN32_OWN_PROCESS, ServiceBootFlag.AutoStart, ServiceError.Normal, fileName, null, IntPtr.Zero, null, null, null);

            if (service == IntPtr.Zero)
                throw new ApplicationException("Failed to install service.");

            try
            {
                StartService(service);
            }
            finally
            {
                CloseServiceHandle(service);
            }
        }
        finally
        {
            CloseServiceHandle(scm);
        }
    }

    public static void StartService(string serviceName)
    {
        IntPtr scm = OpenSCManager(ScmAccessRights.Connect);

        try
        {
            IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Start);
            if (service == IntPtr.Zero)
                throw new ApplicationException("Could not open service.");

            try
            {
                StartService(service);
            }
            finally
            {
                CloseServiceHandle(service);
            }
        }
        finally
        {
            CloseServiceHandle(scm);
        }
    }

    public static void StopService(string serviceName)
    {
        IntPtr scm = OpenSCManager(ScmAccessRights.Connect);

        try
        {
            IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus | ServiceAccessRights.Stop);
            if (service == IntPtr.Zero)
                throw new ApplicationException("Could not open service.");

            try
            {
                StopService(service);
            }
            finally
            {
                CloseServiceHandle(service);
            }
        }
        finally
        {
            CloseServiceHandle(scm);
        }
    }

    private static void StartService(IntPtr service)
    {
        SERVICE_STATUS status = new SERVICE_STATUS();
        StartService(service, 0, 0);
        var changedStatus = WaitForServiceStatus(service, ServiceState.StartPending, ServiceState.Running);
        if (!changedStatus)
            throw new ApplicationException("Unable to start service");
    }

    private static void StopService(IntPtr service)
    {
        SERVICE_STATUS status = new SERVICE_STATUS();
        ControlService(service, ServiceControl.Stop, status);
        var changedStatus = WaitForServiceStatus(service, ServiceState.StopPending, ServiceState.Stopped);
        if (!changedStatus)
            throw new ApplicationException("Unable to stop service");
    }

    public static ServiceState GetServiceStatus(string serviceName)
    {
        IntPtr scm = OpenSCManager(ScmAccessRights.Connect);

        try
        {
            IntPtr service = OpenService(scm, serviceName, ServiceAccessRights.QueryStatus);
            if (service == IntPtr.Zero)
                return ServiceState.NotFound;

            try
            {
                return GetServiceStatus(service);
            }
            finally
            {
                CloseServiceHandle(service);
            }
        }
        finally
        {
            CloseServiceHandle(scm);
        }
    }

    private static ServiceState GetServiceStatus(IntPtr service)
    {
        SERVICE_STATUS status = new SERVICE_STATUS();

        if (QueryServiceStatus(service, status) == 0)
            throw new ApplicationException("Failed to query service status.");

        return status.dwCurrentState;
    }

    private static bool WaitForServiceStatus(IntPtr service, ServiceState waitStatus, ServiceState desiredStatus)
    {
        SERVICE_STATUS status = new SERVICE_STATUS();

        QueryServiceStatus(service, status);
        if (status.dwCurrentState == desiredStatus) return true;

        int dwStartTickCount = Environment.TickCount;
        int dwOldCheckPoint = status.dwCheckPoint;

        while (status.dwCurrentState == waitStatus)
        {
            // Do not wait longer than the wait hint. A good interval is
            // one tenth the wait hint, but no less than 1 second and no
            // more than 10 seconds.

            int dwWaitTime = status.dwWaitHint / 10;

            if (dwWaitTime < 1000) dwWaitTime = 1000;
            else if (dwWaitTime > 10000) dwWaitTime = 10000;

            Thread.Sleep(dwWaitTime);

            // Check the status again.

            if (QueryServiceStatus(service, status) == 0) break;

            if (status.dwCheckPoint > dwOldCheckPoint)
            {
                // The service is making progress.
                dwStartTickCount = Environment.TickCount;
                dwOldCheckPoint = status.dwCheckPoint;
            }
            else
            {
                if (Environment.TickCount - dwStartTickCount > status.dwWaitHint)
                {
                    // No progress made within the wait hint
                    break;
                }
            }
        }
        return (status.dwCurrentState == desiredStatus);
    }

    private static IntPtr OpenSCManager(ScmAccessRights rights)
    {
        IntPtr scm = OpenSCManager(null, null, rights);
        if (scm == IntPtr.Zero)
            throw new ApplicationException("Could not connect to service control manager.");

        return scm;
    }
}


public enum ServiceState
{
    Unknown = -1, // The state cannot be (has not been) retrieved.
    NotFound = 0, // The service is not known on the host server.
    Stopped = 1,
    StartPending = 2,
    StopPending = 3,
    Running = 4,
    ContinuePending = 5,
    PausePending = 6,
    Paused = 7
}

[Flags]
public enum ScmAccessRights
{
    Connect = 0x0001,
    CreateService = 0x0002,
    EnumerateService = 0x0004,
    Lock = 0x0008,
    QueryLockStatus = 0x0010,
    ModifyBootConfig = 0x0020,
    StandardRightsRequired = 0xF0000,
    AllAccess = (StandardRightsRequired | Connect | CreateService |
                 EnumerateService | Lock | QueryLockStatus | ModifyBootConfig)
}

[Flags]
public enum ServiceAccessRights
{
    QueryConfig = 0x1,
    ChangeConfig = 0x2,
    QueryStatus = 0x4,
    EnumerateDependants = 0x8,
    Start = 0x10,
    Stop = 0x20,
    PauseContinue = 0x40,
    Interrogate = 0x80,
    UserDefinedControl = 0x100,
    Delete = 0x00010000,
    StandardRightsRequired = 0xF0000,
    AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig |
                 QueryStatus | EnumerateDependants | Start | Stop | PauseContinue |
                 Interrogate | UserDefinedControl)
}

public enum ServiceBootFlag
{
    Start = 0x00000000,
    SystemStart = 0x00000001,
    AutoStart = 0x00000002,
    DemandStart = 0x00000003,
    Disabled = 0x00000004
}

public enum ServiceControl
{
    Stop = 0x00000001,
    Pause = 0x00000002,
    Continue = 0x00000003,
    Interrogate = 0x00000004,
    Shutdown = 0x00000005,
    ParamChange = 0x00000006,
    NetBindAdd = 0x00000007,
    NetBindRemove = 0x00000008,
    NetBindEnable = 0x00000009,
    NetBindDisable = 0x0000000A
}

public enum ServiceError
{
    Ignore = 0x00000000,
    Normal = 0x00000001,
    Severe = 0x00000002,
    Critical = 0x00000003
}
}
Cosmin Ioniță
  • 3,598
  • 4
  • 23
  • 48