2

I installed a windows service by the following code.

 #region Private Variables

    #endregion Private Variables
    #region DLLImport
    [DllImport("advapi32.dll")]
    public static extern IntPtr OpenSCManager(string lpMachineName, string lpSCDB, int scParameter);
    [DllImport("Advapi32.dll")]
    public static extern IntPtr CreateService(IntPtr SC_HANDLE, string lpSvcName, string lpDisplayName,
    int dwDesiredAccess, int dwServiceType, int dwStartType, int dwErrorControl, string lpPathName,
    string lpLoadOrderGroup, int lpdwTagId, string lpDependencies, string lpServiceStartName, string lpPassword);
    [DllImport("advapi32.dll")]
    public static extern void CloseServiceHandle(IntPtr SCHANDLE);
    [DllImport("advapi32.dll")]
    public static extern int StartService(IntPtr SVHANDLE, int dwNumServiceArgs, string lpServiceArgVectors);
    [DllImport("advapi32.dll", SetLastError = true)]
    public static extern IntPtr OpenService(IntPtr SCHANDLE, string lpSvcName, int dwNumServiceArgs);
    [DllImport("advapi32.dll")]
    public static extern int DeleteService(IntPtr SVHANDLE);
    [DllImport("kernel32.dll")]
    public static extern int GetLastError();
    #endregion DLLImport
    public static bool  InstallService(string svcPath, string svcName, string svcDispName)
    {
        #region Constants declaration.
        int SC_MANAGER_CREATE_SERVICE = 0x0002;
        int SERVICE_WIN32_OWN_PROCESS = 0x00000010;
        //int SERVICE_DEMAND_START = 0x00000003;
        int SERVICE_ERROR_NORMAL = 0x00000001;
        int STANDARD_RIGHTS_REQUIRED = 0xF0000;
        int SERVICE_QUERY_CONFIG = 0x0001;
        int SERVICE_CHANGE_CONFIG = 0x0002;
        int SERVICE_QUERY_STATUS = 0x0004;
        int SERVICE_ENUMERATE_DEPENDENTS = 0x0008;
        int SERVICE_START = 0x0010;
        int SERVICE_STOP = 0x0020;
        int SERVICE_PAUSE_CONTINUE = 0x0040;
        int SERVICE_INTERROGATE = 0x0080;
        int SERVICE_USER_DEFINED_CONTROL = 0x0100;
        int SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |
        SERVICE_QUERY_CONFIG |
        SERVICE_CHANGE_CONFIG |
        SERVICE_QUERY_STATUS |
        SERVICE_ENUMERATE_DEPENDENTS |
        SERVICE_START |
        SERVICE_STOP |
        SERVICE_PAUSE_CONTINUE |
        SERVICE_INTERROGATE |
        SERVICE_USER_DEFINED_CONTROL);
        int SERVICE_AUTO_START = 0x00000002;
        #endregion Constants declaration.
        try
        {
            IntPtr sc_handle = OpenSCManager(null, null, SC_MANAGER_CREATE_SERVICE);
            if (sc_handle.ToInt32() != 0)
            {
                IntPtr sv_handle = CreateService(sc_handle, svcName, svcDispName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, svcPath, null, 0, null, null, null);
                if (sv_handle.ToInt32() == 0)
                {
                    CloseServiceHandle(sc_handle);
                    return false;
                }
                else
                {
                    //now trying to start the service
                    int i = StartService(sv_handle, 0, null);
                    // If the value i is zero, then there was an error starting the service.
                    // note: error may arise if the service is already running or some other problem.
                    if (i == 0)
                    {
                        Console.WriteLine("Couldnt start service");
                        return false;
                    }
                    Console.WriteLine("Service started successfully");
                    CloseServiceHandle(sc_handle);
                    return true;
                }
            }
            else
            {
                Console.WriteLine("SCM not opened successfully");
                return false;
            }
        }
        catch (Exception e)
        {
            throw e;
        }
    }

But the description is blank in SCM, please don't use ServiceInsatller since I want to modify the existing code in production. Thanks for code contribution.

UPDATE:

If forget the above code and use ManagedInstallerClass or other methods, how?

2 Answers2

11

The answered code does not work. This is a version that does work for me:

const int SERVICE_CONFIG_DESCRIPTION = 0x01;

[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, [MarshalAs(UnmanagedType.Struct)] ref SERVICE_DESCRIPTION lpInfo);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SERVICE_DESCRIPTION
{
    public string lpDescription;
}

var pinfo = new SERVICE_DESCRIPTION
{
    lpDescription = "My Service Description"
};

ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, ref pinfo);
Shane Powell
  • 13,698
  • 2
  • 49
  • 61
  • worked for me - VS 2015 targeting FW 4.0 today. Note: you have to open the service with ChangeConfig right: IntPtr service = OpenService(scm, ServiceName, ServiceRights.QueryStatus | ServiceRights.ChangeConfig); – Spike0xff May 17 '17 at 18:06
  • after trying out many things, this actually works ! – Allie May 26 '23 at 08:07
1

The original answer is located here, and repeated here for convenience.

Call ChangeServiceConfig2 passing SERVICE_CONFIG_DESCRIPTION as the dwInfoLevel parameter. You will also need a handle to the service, but CreateService gives you one of those.

As far the DllImport goes, you can find the specification for ChangeServiceConfig2 here.

EDIT:

If you want to abandon the existing approach altogether, using ManagedInstallerClass is not the right way to go because MSDN explicitly says not to use it in your code. The correct way, in my estimation, is to use the ServiceInstaller component, but your original question said you didn't want to use that approach.

Let's say you relented on not using ServiceInstaller. Then I would suggest following the instructions that I provided in my answer here, paying particular attention to steps 6-9. In step #8, you can add whatever description you like to the ServiceInstaller component in the property grid.

If you still don't want to do that approach, then my original answer stands. It would look something like this (mind you, I have not tested this):

[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ChangeServiceConfig2(
    IntPtr hService, 
    int dwInfoLevel,
    IntPtr lpInfo);

int SERVICE_CONFIG_DESCRIPTION = 0x01;
ChangeServiceConfig2(sv_handle, SERVICE_CONFIG_DESCRIPTION, "a test description");
Community
  • 1
  • 1
Matt Davis
  • 45,297
  • 16
  • 93
  • 124
  • I am thinking if using ManagedInstallerClass rather than the old fashion, how can I add service name, display name and description? –  Mar 26 '14 at 14:43
  • Count me confused. MSDN says `ManagedInstallerClass` should **not** be used directly from production code. And in your original question, you said you didn't want to use the `ServiceInstaller`, and the example shows the old-fashioned way of doing it, namely via p/invoke. It's unclear what you're wanting, at least to me. – Matt Davis Mar 26 '14 at 16:27
  • /@Mattsorry about it. Although my question is for old-fashion, but I still feel it is complex. So I don't abandon any idea to do with ManagedInstallerClass. I just want to add description to the code. –  Mar 26 '14 at 16:47
  • Right. If you're just wanting to work within the code as described, you'll need to import the `ChangeServiceConfig2` call just like you did with `OpenSCManager`, `CreateService`, etc. Then call it with the parameters as described in the answer. That should do it. – Matt Davis Mar 26 '14 at 17:03